diff --git a/build.gradle b/build.gradle index d576ffd4..2e0c3ccb 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,6 @@ buildscript { jcenter() google() mavenCentral() - maven { url "https://plugins.gradle.org/m2/" } maven { url "https://maven.google.com" } } @@ -92,15 +91,16 @@ ext { libOkHttp3Log = "com.squareup.okhttp3:logging-interceptor:${okhttpVersion}" minSdkVersion = 14 - targetSdkVersion = 28 - compileSdkVersion = 28 - buildToolsVersion = '28.0.3' + targetSdkVersion = 29 + compileSdkVersion = 29 + buildToolsVersion = '29.0.2' // firebase related https://firebase.google.com/docs/android/setup - firebaseCore = "com.google.firebase:firebase-core:16.0.6" - firebaseMsg = "com.google.firebase:firebase-messaging:17.3.4" + firebaseCore = "com.google.firebase:firebase-core:16.0.7" + firebaseMsg = "com.google.firebase:firebase-messaging:18.0.0" + firebaseIid = "com.google.firebase:firebase-iid:17.0.4" firebaseAuth = "com.google.firebase:firebase-auth:16.1.0" - firebaseDatabase = "com.google.firebase:firebase-database:16.0.5" + firebaseDatabase = "com.google.firebase:firebase-database:16.0.6" // https://developers.google.com/android/guides/setup googlePlayServiceAuth = "com.google.android.gms:play-services-auth:16.0.1" diff --git a/local.properties b/local.properties index 4929a5a2..b8cc166d 100644 --- a/local.properties +++ b/local.properties @@ -4,6 +4,8 @@ # Location of the SDK. This is only used by Gradle. # For customization when using a Version Control System, please read the # header note. -#Thu Aug 02 10:23:49 CST 2018 -ndk.dir=/Users/yanhecun/Library/Android/sdk/ndk-bundle -sdk.dir=/Users/yanhecun/Library/Android/sdk +#Mon Apr 13 14:53:44 CST 2020 +#ndk.dir=/Users/yanhecun/Library/Android/sdk/ndk-bundle +ndk.dir=D\:\\android-sdk-windows\\ndk\\20.1.5948944 +sdk.dir=D\:\\android-sdk-windows + diff --git a/qbaselib b/qbaselib index d379973c..7cf3672c 160000 --- a/qbaselib +++ b/qbaselib @@ -1 +1 @@ -Subproject commit d379973cb724f65f342a1fbfc7b426b185b7ce32 +Subproject commit 7cf3672cd25bcd0f935ef0124144cb080dd6308d diff --git a/qpysdk/build.gradle b/qpysdk/build.gradle index deab3e87..62eac93e 100644 --- a/qpysdk/build.gradle +++ b/qpysdk/build.gradle @@ -20,11 +20,11 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - externalNativeBuild { - ndkBuild { - path 'src/main/jni/Android.mk' - } - } +// externalNativeBuild { +// ndkBuild { +// path 'src/main/jni/Android.mk' +// } +// } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 diff --git a/qpysdk/src/main/java/org/qpython/qpysdk/QPyConstants.java b/qpysdk/src/main/java/org/qpython/qpysdk/QPyConstants.java index bf9ebd1d..29203212 100644 --- a/qpysdk/src/main/java/org/qpython/qpysdk/QPyConstants.java +++ b/qpysdk/src/main/java/org/qpython/qpysdk/QPyConstants.java @@ -3,6 +3,7 @@ import android.os.Environment; import com.quseit.config.BASE_CONF; +import com.quseit.util.FileUtils; public interface QPyConstants extends BASE_CONF { @@ -33,9 +34,10 @@ public interface QPyConstants extends BASE_CONF { final String KEY_NOTEBOOK2_RES = "setting.notebook2resource.path"; - String ABSOLUTE_PATH = Environment.getExternalStorageDirectory().getPath() + "/" + BASE_PATH; - String PY_CACHE_PATH = ABSOLUTE_PATH+"/"+PY_CACHE; - String ABSOLUTE_LOG = ABSOLUTE_PATH + "/log/last.log"; +// String ABSOLUTE_PATH = Environment.getExternalStorageDirectory().getPath() + "/" + BASE_PATH; +// String ABSOLUTE_PATH = FileUtils.getPath().getPath() + "/" + BASE_PATH; +// String PY_CACHE_PATH = ABSOLUTE_PATH+"/"+PY_CACHE; +// String ABSOLUTE_LOG = ABSOLUTE_PATH + "/log/last.log"; String PYTHON_2 = "2.x"; diff --git a/qpysdk/src/main/java/org/qpython/qpysdk/QPySDK.java b/qpysdk/src/main/java/org/qpython/qpysdk/QPySDK.java index 516f523e..9d1973a6 100644 --- a/qpysdk/src/main/java/org/qpython/qpysdk/QPySDK.java +++ b/qpysdk/src/main/java/org/qpython/qpysdk/QPySDK.java @@ -94,10 +94,10 @@ public void playQScript(final String script) { mArguments.add(script); String[] argumentsArray = mArguments.toArray(new String[mArguments.size()]); - final File mLog = new File(String.format("%s", QPyConstants.ABSOLUTE_LOG)); + final File mLog = new File(String.format("%s", com.quseit.util.FileUtils.getAbsoluteLogPath(context.getApplicationContext()))); File logDir = mLog.getParentFile(); - mFd = Exec.createSubprocess(binaryPath, argumentsArray, getEnvironmentArray(f.getParentFile() + ""), Environment.getExternalStorageDirectory() + "/", pid); + mFd = Exec.createSubprocess(binaryPath, argumentsArray, getEnvironmentArray(f.getParentFile() + ""), com.quseit.util.FileUtils.getPath(context.getApplicationContext()) + "/", pid); final AtomicInteger mPid = new AtomicInteger(PID_INIT_VALUE); mPid.set(pid[0]); @@ -106,34 +106,32 @@ public void playQScript(final String script) { long mStartTime = System.currentTimeMillis(); - new Thread(new Runnable() { - public void run() { - int returnValue = Exec.waitFor(mPid.get()); - //long mEndTime = System.currentTimeMillis(); - int pid = mPid.getAndSet(PID_INIT_VALUE); - Log.d("", "out:" + mFd.out.toString()); + new Thread(() -> { + int returnValue = Exec.waitFor(mPid.get()); + //long mEndTime = System.currentTimeMillis(); + int pid1 = mPid.getAndSet(PID_INIT_VALUE); + Log.d("", "out:" + mFd.out.toString()); - Message msg = new Message(); - msg.what = returnValue; - msg.obj = mArguments.get(0); + Message msg = new Message(); + msg.what = returnValue; + msg.obj = mArguments.get(0); - Log.d(TAG, "Process " + pid + " exited with result code " + returnValue + "."); + Log.d(TAG, "Process " + pid1 + " exited with result code " + returnValue + "."); - try { - mIn.close(); - } catch (IOException e) { - Log.e(TAG, e.getMessage()); - } + try { + mIn.close(); + } catch (IOException e) { + Log.e(TAG, e.getMessage()); + } - try { - mOut.close(); - } catch (IOException e) { - Log.e(TAG, e.getMessage()); - } + try { + mOut.close(); + } catch (IOException e) { + Log.e(TAG, e.getMessage()); + } - //context.updateNotify(msg); + //context.updateNotify(msg); - } }).start(); } @@ -149,7 +147,7 @@ private String[] getEnvironmentArray(String pyPath) { environmentVariables.add("PYTHONHOME=" + filesDir); environmentVariables.add("ANDROID_PRIVATE=" + filesDir); - File externalStorage = new File(Environment.getExternalStorageDirectory(), "org.qpython.qpy"); + File externalStorage = new File(com.quseit.util.FileUtils.getPath(context.getApplicationContext()), "org.qpython.qpy"); environmentVariables.add("PYTHONPATH=" + externalStorage + "/lib/python2.7/site-packages/:" + filesDir + "/lib/python2.7/site-packages/:" diff --git a/qpysdk/src/main/java/org/qpython/qpysdk/utils/FileHelper.java b/qpysdk/src/main/java/org/qpython/qpysdk/utils/FileHelper.java index 90ffe237..90f8b721 100644 --- a/qpysdk/src/main/java/org/qpython/qpysdk/utils/FileHelper.java +++ b/qpysdk/src/main/java/org/qpython/qpysdk/utils/FileHelper.java @@ -6,6 +6,8 @@ import android.os.Environment; import android.webkit.MimeTypeMap; +import com.quseit.util.FileUtils; + import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; @@ -23,29 +25,29 @@ /** * 对SD卡文件的管理 - * + * * @author ch.linghu - * */ public class FileHelper { - @SuppressWarnings("unused") - private static final String TAG = "FileHelper"; - - public static final void createDirIfNExists(String dirname) { - File yy = new File(dirname); - if (!yy.exists()) { - yy.mkdirs(); - } - } - - public static final void createFileFromAssetsIfNExists(Context con, String filename, String dst) { - File yy = new File(dst); - if (!yy.exists()) { - String content = FileHelper.LoadDataFromAssets(con, filename); - FileHelper.writeToFile(dst, content, false); - } - } - public static void openFile(Context context, String filePath, String fileExtension) { + @SuppressWarnings("unused") + private static final String TAG = "FileHelper"; + + public static final void createDirIfNExists(String dirname) { + File yy = new File(dirname); + if (!yy.exists()) { + yy.mkdirs(); + } + } + + public static final void createFileFromAssetsIfNExists(Context con, String filename, String dst) { + File yy = new File(dst); + if (!yy.exists()) { + String content = FileHelper.LoadDataFromAssets(con, filename); + FileHelper.writeToFile(dst, content, false); + } + } + + public static void openFile(Context context, String filePath, String fileExtension) { Intent intent = new Intent(); intent.setAction(android.content.Intent.ACTION_VIEW); File file = new File(filePath); @@ -57,328 +59,331 @@ public static void openFile(Context context, String filePath, String fileExtensi context.startActivity(intent); } catch (android.content.ActivityNotFoundException e) { } -} + } + + public static String getFileNameFromUrl(String urlFile) { + try { + URL url = new URL(urlFile); + File f = new File(url.getPath()); + return f.getName(); + + } catch (MalformedURLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + + return "unname.dat"; + } + } + + public static String getTypeByMimeType(String mType) { + if (mType.equals("application/vnd.android.package-archive")) { + return "apk"; + } else { + String[] xx = mType.split("/"); + if (xx.length > 1) { + return xx[0]; + } + } + return "other"; + } + + public static String LoadDataFromAssets(Context context, String inFile) { + String tContents = ""; + + try { + InputStream stream = context.getAssets().open(inFile); + int size = stream.available(); + byte[] buffer = new byte[size]; + stream.read(buffer); + stream.close(); + tContents = new String(buffer); + } catch (IOException e) { + } + return tContents; + } - public static String getFileNameFromUrl(String urlFile) { - try { - URL url = new URL(urlFile); - File f = new File(url.getPath()); - return f.getName(); - - } catch (MalformedURLException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - - return "unname.dat"; - } - } - public static String getTypeByMimeType(String mType) { - if (mType.equals("application/vnd.android.package-archive")) { - return "apk"; - } else { - String[] xx = mType.split("/"); - if (xx.length>1) { - return xx[0]; - } - } - return "other"; - } - - public static String LoadDataFromAssets(Context context, String inFile) { - String tContents = ""; - - try { - InputStream stream = context.getAssets().open(inFile); - int size = stream.available(); - byte[] buffer = new byte[size]; - stream.read(buffer); - stream.close(); - tContents = new String(buffer); - } catch (IOException e) { - } - return tContents; - } - - public static void putFileContents(Context context, String filename, String content) { - try { - File fileCache = new File(filename); - byte[] data = content.getBytes(); - FileOutputStream outStream; - outStream = new FileOutputStream(fileCache); - outStream.write(data); - outStream.close(); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - - } - } - - public static void writeToFile(String filePath, String data, boolean append) { - try { - FileOutputStream fOut = new FileOutputStream(filePath, append); + public static void putFileContents(Context context, String filename, String content) { + try { + File fileCache = new File(filename); + byte[] data = content.getBytes(); + FileOutputStream outStream; + outStream = new FileOutputStream(fileCache); + outStream.write(data); + outStream.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + + } + } + + public static void writeToFile(String filePath, String data, boolean append) { + try { + FileOutputStream fOut = new FileOutputStream(filePath, append); fOut.write(data.getBytes()); fOut.flush(); fOut.close(); - } catch (IOException iox) { - iox.printStackTrace(); - } - - } - - - public static String getFileContentsFromAssets(Context context, String filename) { - String content=""; //结果字符串 - try{ - java.io.InputStream is= context.getResources().getAssets().open(filename); //打开文件 - int ch=0; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); //实现了一个输出流 - while((ch=is.read())!=-1){ - baos.write(ch); //将指定的字节写入此 byte 数组输出流 - } - byte[] buff=baos.toByteArray();//以byte 数组的形式返回此输出流的当前内容 - baos.close(); //关闭流 - is.close(); //关闭流 - content=new String(buff,"UTF-8"); //设置字符串编码 - - } catch(Exception e) { - e.printStackTrace(); - //LogUtil.d(TAG, "getFileContentsFromAssets:"+e.getMessage()); - } - return content; - } - public static String getFileContents(String filename) { - - File scriptFile = new File( filename ); + } catch (IOException iox) { + iox.printStackTrace(); + } + + } + + + public static String getFileContentsFromAssets(Context context, String filename) { + String content = ""; //结果字符串 + try { + java.io.InputStream is = context.getResources().getAssets().open(filename); //打开文件 + int ch = 0; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); //实现了一个输出流 + while ((ch = is.read()) != -1) { + baos.write(ch); //将指定的字节写入此 byte 数组输出流 + } + byte[] buff = baos.toByteArray();//以byte 数组的形式返回此输出流的当前内容 + baos.close(); //关闭流 + is.close(); //关闭流 + content = new String(buff, "UTF-8"); //设置字符串编码 + + } catch (Exception e) { + e.printStackTrace(); + //LogUtil.d(TAG, "getFileContentsFromAssets:"+e.getMessage()); + } + return content; + } + + public static String getFileContents(String filename) { + + File scriptFile = new File(filename); + String tContent = ""; + if (scriptFile.exists()) { + BufferedReader in; + try { + in = new BufferedReader(new FileReader(scriptFile)); + String line; + + while ((line = in.readLine()) != null) { + tContent += line + "\n"; + } + in.close(); + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + return tContent; + } + + public static String getFileContents(String filename, int pos) { + + File scriptFile = new File(filename); String tContent = ""; if (scriptFile.exists()) { - BufferedReader in; - try { - in = new BufferedReader(new FileReader(scriptFile)); - String line; - - while ((line = in.readLine())!=null) { - tContent += line+"\n"; - } - in.close(); - } catch (FileNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + BufferedReader in; + try { + in = new BufferedReader(new FileReader(scriptFile)); + String line; + + while ((line = in.readLine()) != null) { + tContent += line + "\n"; + if (tContent.length() >= pos) { + in.close(); + return tContent; + } + } + in.close(); + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + return tContent; + } + + public static void clearDir(String dir, int level, boolean deleteS) { + //LogUtil.d(TAG, "clearDir:"+dir); + File basePath = new File(dir); + if (basePath.exists() && basePath.isDirectory()) { + for (File item : basePath.listFiles()) { + if (item.isFile()) { + //LogUtil.d(TAG, "deleteItem:"+item.getAbsolutePath()); + item.delete(); + + } else if (item.isDirectory()) { + clearDir(item.getAbsolutePath(), level + 1, deleteS); + } + } + if (level > 0 || deleteS) { + basePath.delete(); + } + } + } + + public static File getBasePath(Context context, String parDir, String subdir) throws IOException { + try { + File basePath = new File(FileUtils.getPath(context), + parDir); + + if (!basePath.exists()) { + if (!basePath.mkdirs()) { + throw new IOException(String.format("%s cannot be created!", + basePath.toString())); + } + } + File subPath = null; + if (!subdir.equals("")) { + subPath = new File(FileUtils.getPath(context.getApplicationContext()), + parDir + "/" + subdir); + if (!subPath.exists()) { + if (!subPath.mkdirs()) { + throw new IOException(String.format("%s cannot be created!", + subPath.toString())); + } + } + } + + if (!basePath.isDirectory()) { + throw new IOException(String.format("%s is not a directory!", + basePath.toString())); + } + if (subdir.equals("")) { + return basePath; + } else { + return subPath; + } + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /*public static File getBasePath(String subdir) throws IOException { + File basePath = new File(Environment.getExternalStorageDirectory(), + BASE_CONF.BASE_PATH); + + if (!basePath.exists()) { + if (!basePath.mkdirs()) { + throw new IOException(String.format("%s cannot be created!", + basePath.toString())); + } + } + File subPath = null; + if (!subdir.equals("")) { + subPath = new File(Environment.getExternalStorageDirectory(), + BASE_CONF.BASE_PATH+"/"+subdir); + if (!subPath.exists()) { + if (!subPath.mkdirs()) { + throw new IOException(String.format("%s cannot be created!", + subPath.toString())); + } + } + } + + if (!basePath.isDirectory()) { + throw new IOException(String.format("%s is not a directory!", + basePath.toString())); + } + if (subdir.equals("")) + return basePath; + else + return subPath; + } + */ + public static File getABSPath(String subdir) throws IOException { + File basePath = new File(subdir); + + if (!basePath.exists()) { + if (!basePath.mkdirs()) { + throw new IOException(String.format("%s cannot be created!", + basePath.toString())); + } + } + File subPath = null; + if (!subdir.equals("")) { + subPath = new File(subdir); + if (!subPath.exists()) { + if (!subPath.mkdirs()) { + throw new IOException(String.format("%s cannot be created!", + subPath.toString())); + } + } + } + if (!basePath.isDirectory()) { + throw new IOException(String.format("%s is not a directory!", + basePath.toString())); } - return tContent; - } - - public static String getFileContents(String filename, int pos) { - - File scriptFile = new File( filename ); - String tContent = ""; - if (scriptFile.exists()) { - BufferedReader in; - try { - in = new BufferedReader(new FileReader(scriptFile)); - String line; - - while ((line = in.readLine())!=null) { - tContent += line+"\n"; - if (tContent.length()>=pos) { - in.close(); - return tContent; - } - } - in.close(); - } catch (FileNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - } - return tContent; - } - - public static void clearDir(String dir, int level, boolean deleteS) { - //LogUtil.d(TAG, "clearDir:"+dir); - File basePath = new File(dir); - if (basePath.exists() && basePath.isDirectory()) { - for (File item : basePath.listFiles()) { - if (item.isFile()) { - //LogUtil.d(TAG, "deleteItem:"+item.getAbsolutePath()); - item.delete(); - - } else if (item.isDirectory()){ - clearDir(item.getAbsolutePath(), level+1, deleteS); - } - } - if (level>0 || deleteS) { - basePath.delete(); - } - } - } - - public static File getBasePath(String parDir, String subdir) throws IOException { - try { - File basePath = new File(Environment.getExternalStorageDirectory(), - parDir); - - if (!basePath.exists()) { - if (!basePath.mkdirs()) { - throw new IOException(String.format("%s cannot be created!", - basePath.toString())); - } - } - File subPath = null; - if (!subdir.equals("")) { - subPath = new File(Environment.getExternalStorageDirectory(), - parDir+"/"+subdir); - if (!subPath.exists()) { - if (!subPath.mkdirs()) { - throw new IOException(String.format("%s cannot be created!", - subPath.toString())); - } - } - } - - if (!basePath.isDirectory()) { - throw new IOException(String.format("%s is not a directory!", - basePath.toString())); - } - if (subdir.equals("")) - return basePath; - else - return subPath; - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - /*public static File getBasePath(String subdir) throws IOException { - File basePath = new File(Environment.getExternalStorageDirectory(), - BASE_CONF.BASE_PATH); - - if (!basePath.exists()) { - if (!basePath.mkdirs()) { - throw new IOException(String.format("%s cannot be created!", - basePath.toString())); - } - } - File subPath = null; - if (!subdir.equals("")) { - subPath = new File(Environment.getExternalStorageDirectory(), - BASE_CONF.BASE_PATH+"/"+subdir); - if (!subPath.exists()) { - if (!subPath.mkdirs()) { - throw new IOException(String.format("%s cannot be created!", - subPath.toString())); - } - } - } - - if (!basePath.isDirectory()) { - throw new IOException(String.format("%s is not a directory!", - basePath.toString())); - } - if (subdir.equals("")) - return basePath; - else - return subPath; - } - */ - public static File getABSPath(String subdir) throws IOException { - File basePath = new File(subdir); - - if (!basePath.exists()) { - if (!basePath.mkdirs()) { - throw new IOException(String.format("%s cannot be created!", - basePath.toString())); - } - } - File subPath = null; - if (!subdir.equals("")) { - subPath = new File(subdir); - if (!subPath.exists()) { - if (!subPath.mkdirs()) { - throw new IOException(String.format("%s cannot be created!", - subPath.toString())); - } - } - } - - if (!basePath.isDirectory()) { - throw new IOException(String.format("%s is not a directory!", - basePath.toString())); - } - if (subdir.equals("")) - return basePath; - else - return subPath; - } - - public static String getFileName(String filename) { - File f = new File(filename); - return f.getName(); - } - + if (subdir.equals("")) + {return basePath;} + else + {return subPath;} + } + + public static String getFileName(String filename) { + File f = new File(filename); + return f.getName(); + } + public static String getExt(String filename, String def) { - String[] yy = filename.split("\\?"); + String[] yy = filename.split("\\?"); String[] xx = yy[0].split("\\."); //LogUtil.d(TAG, "filename:"+filename+"-size:"+xx.length); - - if (xx.length<2) { - return def; + + if (xx.length < 2) { + return def; } else { - String ext = xx[xx.length-1]; + String ext = xx[xx.length - 1]; //LogUtil.d(TAG, "ext:"+ext); return ext; - } - } - + } + } + public static boolean getUrlAsFile(String link, String fileName) { - try { - // get URL content - URL url = new URL(link); - URLConnection conn = url.openConnection(); - - // open the stream and put it into BufferedReader - BufferedReader br = new BufferedReader( - new InputStreamReader(conn.getInputStream())); - - String inputLine; - - //save to this filename - File file = new File(fileName); - - if (!file.exists()) { - file.createNewFile(); - } - - //use FileWriter to write file - FileWriter fw = new FileWriter(file.getAbsoluteFile()); - BufferedWriter bw = new BufferedWriter(fw); - - while ((inputLine = br.readLine()) != null) { - bw.write(inputLine+"\n"); - } - - bw.close(); - br.close(); - - //System.out.println("Done"); - return true; - } catch (MalformedURLException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - return false; - - } + try { + // get URL content + URL url = new URL(link); + URLConnection conn = url.openConnection(); + + // open the stream and put it into BufferedReader + BufferedReader br = new BufferedReader( + new InputStreamReader(conn.getInputStream())); + + String inputLine; + + //save to this filename + File file = new File(fileName); + + if (!file.exists()) { + file.createNewFile(); + } + + //use FileWriter to write file + FileWriter fw = new FileWriter(file.getAbsoluteFile()); + BufferedWriter bw = new BufferedWriter(fw); + + while ((inputLine = br.readLine()) != null) { + bw.write(inputLine + "\n"); + } + + bw.close(); + br.close(); + + //System.out.println("Done"); + return true; + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return false; + + } } diff --git a/qpysdk/src/main/java/org/renpy/android/PythonSDLActivity.java b/qpysdk/src/main/java/org/renpy/android/PythonSDLActivity.java index e727399e..33817663 100644 --- a/qpysdk/src/main/java/org/renpy/android/PythonSDLActivity.java +++ b/qpysdk/src/main/java/org/renpy/android/PythonSDLActivity.java @@ -28,6 +28,7 @@ import android.widget.Toast; +import com.quseit.util.FileUtils; import com.quseit.util.NAction; import com.quseit.util.NUtil; @@ -69,6 +70,7 @@ public class PythonSDLActivity extends SDLActivity { ResourceManager resourceManager; + @Override protected String[] getLibraries() { return new String[] { "png16", @@ -243,11 +245,7 @@ public void toastError(final String msg) { final Activity thisActivity = this; - runOnUiThread(new Runnable () { - public void run() { - Toast.makeText(thisActivity, msg, Toast.LENGTH_LONG).show(); - } - }); + runOnUiThread(() -> Toast.makeText(thisActivity, msg, Toast.LENGTH_LONG).show()); // Wait to show the error. synchronized (this) { @@ -276,7 +274,7 @@ public void initEnviron() { nativeSetEnv("ANDROID_ARGUMENT", path.getAbsolutePath()); } else { - nativeSetEnv("ANDROID_ARGUMENT", QPyConstants.ABSOLUTE_PATH); + nativeSetEnv("ANDROID_ARGUMENT", FileUtils.getAbsolutePath(getApplicationContext())); } @@ -299,7 +297,7 @@ public void preparePython() { resourceManager = new ResourceManager(this); - File oldExternalStorage = new File(Environment.getExternalStorageDirectory(), getPackageName()); + File oldExternalStorage = new File(FileUtils.getPath(getApplicationContext()), getPackageName()); File externalStorage = getExternalFilesDir(null); File path; @@ -323,7 +321,7 @@ public void preparePython() { path = getFilesDir(); } - nativeSetEnv("ANDROID_LOG", QPyConstants.ABSOLUTE_LOG); + nativeSetEnv("ANDROID_LOG",FileUtils.getAbsoluteLogPath(getApplicationContext())); File filesDir = getFilesDir(); //unpackData("private", getFilesDir()); @@ -331,7 +329,7 @@ public void preparePython() { nativeSetEnv("ANDROID_ARGUMENT", path.getAbsolutePath()); nativeSetEnv("ANDROID_PRIVATE", getFilesDir().getAbsolutePath()); - nativeSetEnv("ANDROID_PUBLIC", QPyConstants.ABSOLUTE_PATH); + nativeSetEnv("ANDROID_PUBLIC", FileUtils.getAbsolutePath(getApplicationContext())); nativeSetEnv("ANDROID_OLD_PUBLIC", oldExternalStorage.getAbsolutePath()); nativeSetEnv("LD_LIBRARY_PATH", ".:"+filesDir+"/lib/"+":"+filesDir+"/:"+filesDir.getParentFile()+"/lib/"); @@ -403,6 +401,7 @@ protected void onDestroy() { super.onDestroy(); } + @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); } @@ -433,18 +432,18 @@ public int getDPI() { public PowerManager.WakeLock wakeLock = null; - public void setWakeLock(boolean active) { - if (wakeLock == null) { - PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "Screen On"); - wakeLock.setReferenceCounted(false); - } - - if (active) { - wakeLock.acquire(); - } else { - wakeLock.release(); - } - } +// public void setWakeLock(boolean active) { +// if (wakeLock == null) { +// PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); +// wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "Screen On"); +// wakeLock.setReferenceCounted(false); +// } +// +// if (active) { +// wakeLock.acquire(); +// } else { +// wakeLock.release(); +// } +// } } diff --git a/qpysdk/src/main/jni b/qpysdk/src/main/jni index 3ee8b209..13b2bb1b 160000 --- a/qpysdk/src/main/jni +++ b/qpysdk/src/main/jni @@ -1 +1 @@ -Subproject commit 3ee8b209df6481a585826a44bfa94eeecd621e4e +Subproject commit 13b2bb1b99c208f41c8f0c83eb284bc29493587e diff --git a/qpysdk/src/main/libs/arm64-v8a/libSDL2.so b/qpysdk/src/main/libs/arm64-v8a/libSDL2.so new file mode 100755 index 00000000..479428c8 Binary files /dev/null and b/qpysdk/src/main/libs/arm64-v8a/libSDL2.so differ diff --git a/qpysdk/src/main/libs/arm64-v8a/libSDL2_gfx.so b/qpysdk/src/main/libs/arm64-v8a/libSDL2_gfx.so new file mode 100755 index 00000000..04a7d33e Binary files /dev/null and b/qpysdk/src/main/libs/arm64-v8a/libSDL2_gfx.so differ diff --git a/qpysdk/src/main/libs/arm64-v8a/libSDL2_image.so b/qpysdk/src/main/libs/arm64-v8a/libSDL2_image.so new file mode 100755 index 00000000..d7c20bbb Binary files /dev/null and b/qpysdk/src/main/libs/arm64-v8a/libSDL2_image.so differ diff --git a/qpysdk/src/main/libs/arm64-v8a/libSDL2_mixer.so b/qpysdk/src/main/libs/arm64-v8a/libSDL2_mixer.so new file mode 100755 index 00000000..72742e96 Binary files /dev/null and b/qpysdk/src/main/libs/arm64-v8a/libSDL2_mixer.so differ diff --git a/qpysdk/src/main/libs/arm64-v8a/libSDL2_ttf.so b/qpysdk/src/main/libs/arm64-v8a/libSDL2_ttf.so new file mode 100755 index 00000000..1b1ac8e0 Binary files /dev/null and b/qpysdk/src/main/libs/arm64-v8a/libSDL2_ttf.so differ diff --git a/qpysdk/src/main/libs/arm64-v8a/libmain.so b/qpysdk/src/main/libs/arm64-v8a/libmain.so new file mode 100755 index 00000000..c635de54 Binary files /dev/null and b/qpysdk/src/main/libs/arm64-v8a/libmain.so differ diff --git a/qpysdk/src/main/libs/arm64-v8a/libpng16.so b/qpysdk/src/main/libs/arm64-v8a/libpng16.so new file mode 100755 index 00000000..76a0ef33 Binary files /dev/null and b/qpysdk/src/main/libs/arm64-v8a/libpng16.so differ diff --git a/qpysdk/src/main/libs/arm64-v8a/libqpysdk.so b/qpysdk/src/main/libs/arm64-v8a/libqpysdk.so new file mode 100755 index 00000000..259563f3 Binary files /dev/null and b/qpysdk/src/main/libs/arm64-v8a/libqpysdk.so differ diff --git a/qpysl4a b/qpysl4a index fc9d4745..2d03143d 160000 --- a/qpysl4a +++ b/qpysl4a @@ -1 +1 @@ -Subproject commit fc9d47452c2cdd99b1314c2d5f913dfea33698ad +Subproject commit 2d03143d1160884c22e5769f92b33492aad3d983 diff --git a/qpython/build.gradle b/qpython/build.gradle index 738e1a9e..b9e36ca6 100644 --- a/qpython/build.gradle +++ b/qpython/build.gradle @@ -68,7 +68,12 @@ android { } debug { - signingConfig signingConfigs.release +// signingConfig signingConfigs.release + debuggable true + minifyEnabled false + jniDebuggable true + signingConfig signingConfigs.debug + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } @@ -162,6 +167,9 @@ dependencies { api 'org.litepal.android:core:1.3.1' api 'me.dm7.barcodescanner:zxing:1.9' api 'com.android.support:multidex:1.0.1' + implementation ('com.gyf.cactus:cactus-support:1.1.3-beta09'){ + exclude group: 'com.google.guava' + } api rootProject.ext.libOkHttp3 api rootProject.ext.libOkHttp3Log @@ -176,23 +184,38 @@ dependencies { api rootProject.ext.libSupportCardView api rootProject.ext.libSupportPreference - osApi rootProject.ext.firebaseCore - osApi rootProject.ext.firebaseMsg - osApi rootProject.ext.firebaseAuth - osApi rootProject.ext.firebaseDatabase - osApi rootProject.ext.googlePlayServiceAuth - - olApi rootProject.ext.firebaseCore - olApi rootProject.ext.firebaseMsg - olApi rootProject.ext.firebaseAuth - olApi rootProject.ext.firebaseDatabase - olApi rootProject.ext.googlePlayServiceAuth + odApi rootProject.ext.firebaseCore + implementation ("com.google.firebase:firebase-messaging:17.3.4"){ + exclude group: 'com.google.firebase', module: 'firebase-iid' + } + odApi rootProject.ext.firebaseIid + odApi rootProject.ext.firebaseAuth + odApi rootProject.ext.firebaseDatabase + odApi rootProject.ext.googlePlayServiceAuth + +// osApi rootProject.ext.firebaseCore +// osApi rootProject.ext.firebaseMsg +// osApi rootProject.ext.firebaseAuth +// osApi rootProject.ext.firebaseDatabase +// implementation platform('com.google.firebase:firebase-bom:29.0.0') +// osApi rootProject.ext.firebaseAnalytics +// osApi rootProject.ext.firebaseMsg +// osApi rootProject.ext.googlePlayServiceAuth + +// olApi rootProject.ext.firebaseCore +// olApi rootProject.ext.firebaseMsg +// olApi rootProject.ext.firebaseAuth +// olApi rootProject.ext.firebaseDatabase +// implementation platform('com.google.firebase:firebase-bom:29.0.0') +// olApi rootProject.ext.firebaseAnalytics +// olApi rootProject.ext.firebaseMsg +// olApi rootProject.ext.googlePlayServiceAuth api rootProject.ext.retrofit api rootProject.ext.retrofitCoverterGson api rootProject.ext.retrofitAdapterRxjava - api 'com.android.support.constraint:constraint-layout:1.0.2' + api 'com.android.support.constraint:constraint-layout:1.1.3' // 微信 opApi('com.tencent.mm.opensdk:wechat-sdk-android-with-mta:1.4.0') { diff --git a/qpython/src/main/AndroidManifest.xml b/qpython/src/main/AndroidManifest.xml index db2647f9..1418caa5 100644 --- a/qpython/src/main/AndroidManifest.xml +++ b/qpython/src/main/AndroidManifest.xml @@ -19,8 +19,6 @@ - - @@ -35,6 +33,22 @@ + + + + + + + + + + + + + + + + @@ -57,6 +71,7 @@ android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:largeHeap="true" + android:requestLegacyExternalStorage="true" android:supportsRtl="true" android:theme="@style/AppTheme"> @@ -68,6 +83,8 @@ + + diff --git a/qpython/src/main/java/org/qpython/qpy/console/ScriptExec.java b/qpython/src/main/java/org/qpython/qpy/console/ScriptExec.java index 9a23d02d..4aa17d1e 100644 --- a/qpython/src/main/java/org/qpython/qpy/console/ScriptExec.java +++ b/qpython/src/main/java/org/qpython/qpy/console/ScriptExec.java @@ -20,6 +20,7 @@ import android.text.TextUtils; import android.util.Log; +import org.qpython.qpy.main.app.App; import org.qpython.qpysdk.Exec; import com.quseit.util.FileUtils; import com.quseit.util.NAction; @@ -129,7 +130,7 @@ public void playProject(Context context, String project, String args, boolean no public String getLastLog() { - File logFile = new File(QPyConstants.ABSOLUTE_LOG); + File logFile = new File(FileUtils.getAbsoluteLogPath(App.getContext())); if (!logFile.getAbsoluteFile().getParentFile().exists()) { logFile.getAbsoluteFile().getParentFile().mkdirs(); @@ -146,7 +147,7 @@ public String[] getPyEnv(Context context, String path, String term, String pyPat boolean isQPy3 = NAction.isQPy3(context); File filesDir = context.getFilesDir(); - File externalStorage = new File(QPyConstants.ABSOLUTE_PATH); + File externalStorage = new File(FileUtils.getAbsolutePath(context)); String[] env = new String[24]; @@ -332,7 +333,7 @@ public void playKScript(Context context, final String script, String argv, boole if (Utils.isOpenGL2supported(context)) { File scriptParent = new File(script).getParentFile(); String proj, log; - log = QPyConstants.ABSOLUTE_LOG; + log = FileUtils.getAbsoluteLogPath(App.getContext()); if (scriptParent.getName().startsWith("scripts")) { proj = new File(script).getName(); @@ -413,9 +414,9 @@ public int playQScript(Context context, final String script, String argv, boolea String[] argumentsArray = mArguments.toArray(new String[mArguments.size()]); - final File mLog = new File(QPyConstants.ABSOLUTE_LOG); + final File mLog = new File(FileUtils.getAbsoluteLogPath(App.getContext())); - mFd = Exec.createSubprocess(binaryPath, argumentsArray, getPyEnv(context, System.getenv("PATH"), System.getenv("TERM"),f.getParentFile() + ""), Environment.getExternalStorageDirectory() + "/", pid); + mFd = Exec.createSubprocess(binaryPath, argumentsArray, getPyEnv(context, System.getenv("PATH"), System.getenv("TERM"),f.getParentFile() + ""), FileUtils.getPath(App.getContext()) + "/", pid); final AtomicInteger mPid = new AtomicInteger(PID_INIT_VALUE); mPid.set(pid[0]); diff --git a/qpython/src/main/java/org/qpython/qpy/console/TermActivity.java b/qpython/src/main/java/org/qpython/qpy/console/TermActivity.java index bf2f1f01..c91dbe21 100644 --- a/qpython/src/main/java/org/qpython/qpy/console/TermActivity.java +++ b/qpython/src/main/java/org/qpython/qpy/console/TermActivity.java @@ -158,6 +158,7 @@ public class TermActivity extends AppCompatActivity implements UpdateCallback, S * Intercepts keys before the view/terminal gets it. */ private View.OnKeyListener mKeyListener = new View.OnKeyListener() { + @Override public boolean onKey(View v, int keyCode, KeyEvent event) { return backkeyInterceptor(keyCode, event) || keyboardShortcuts(keyCode, event); } @@ -232,6 +233,7 @@ public void onReceive(Context context, Intent intent) { } }; private ServiceConnection mTSConnection = new ServiceConnection() { + @Override public void onServiceConnected(ComponentName className, IBinder service) { Log.i(TermDebug.LOG_TAG, "Bound to TermService"); TermService.TSBinder binder = (TermService.TSBinder) service; @@ -246,6 +248,7 @@ public void onServiceConnected(ComponentName className, IBinder service) { } } + @Override public void onServiceDisconnected(ComponentName arg0) { mTermService = null; } @@ -293,7 +296,7 @@ public void onCreate(Bundle icicle) { initView(); mPrivateAlias = new ComponentName(this, RemoteInterface.PRIVACT_ACTIVITY_ALIAS); if (icicle == null) - onNewIntent(getIntent()); + {onNewIntent(getIntent());} Intent broadcast = new Intent(ACTION_PATH_BROADCAST); @@ -944,6 +947,7 @@ private void closeActivity() { } // Called when the list of sessions changes + @Override public void onUpdate() { SessionList sessions = mTermSessions; if (sessions == null) { @@ -1154,7 +1158,7 @@ private void execURL(String link) { PackageManager pm = getPackageManager(); List handlers = pm.queryIntentActivities(openLink, 0); if (handlers.size() > 0) - startActivity(openLink); + {startActivity(openLink);} } private TermSession createPyTermSession(String[] mArgs) throws IOException { @@ -1181,7 +1185,7 @@ private TermSession createPyTermSession(String[] mArgs) throws IOException { } else { StringBuilder cm = new StringBuilder(); for (int i = 0; i < mArgs.length; i++) { - if (i == 1 && mArgs[0].contains(mArgs[i])) continue; + if (i == 1 && mArgs[0].contains(mArgs[i])) {continue;} cm.append(" ").append(mArgs[i]).append(" "); } session = createTermSession(this, settings, scmd + " " + cm + " && exit", mArgs[1]); @@ -1238,14 +1242,14 @@ private class EmulatorViewGestureListener extends SimpleOnGestureListener { @Override public boolean onSingleTapUp(MotionEvent e) { // Let the EmulatorView handle taps if mouse tracking is active - if (view.isMouseTrackingActive()) return false; + if (view.isMouseTrackingActive()) {return false;} //Check for link at tap location String link = view.getURLat(e.getX(), e.getY()); if (link != null) - execURL(link); + {execURL(link);} else - doUIToggle((int) e.getX(), (int) e.getY(), view.getVisibleWidth(), view.getVisibleHeight()); + {doUIToggle((int) e.getX(), (int) e.getY(), view.getVisibleWidth(), view.getVisibleHeight());} return true; } diff --git a/qpython/src/main/java/org/qpython/qpy/console/shortcuts/AddShortcut.java b/qpython/src/main/java/org/qpython/qpy/console/shortcuts/AddShortcut.java index 566eaaa5..3e4cad77 100644 --- a/qpython/src/main/java/org/qpython/qpy/console/shortcuts/AddShortcut.java +++ b/qpython/src/main/java/org/qpython/qpy/console/shortcuts/AddShortcut.java @@ -18,6 +18,8 @@ import android.widget.ScrollView; import android.widget.TextView; +import com.quseit.util.FileUtils; + import org.qpython.qpy.R; import org.qpython.qpy.console.RemoteInterface; @@ -26,6 +28,7 @@ import org.qpython.qpy.console.compont.AlertDialogCompat; import org.qpython.qpy.console.compont.PRNGFixes; import org.qpython.qpy.console.util.ShortcutEncryption; +import org.qpython.qpy.main.app.App; import java.io.File; import java.security.GeneralSecurityException; @@ -82,7 +85,7 @@ void makeShortcut() { btn_path.setOnClickListener(p1 -> { String lastPath = SP.getString("lastPath", null); File get = (lastPath == null) - ? Environment.getExternalStorageDirectory() + ? FileUtils.getPath(App.getContext()) : new File(lastPath).getParentFile(); Intent pickerIntent = new Intent(); if (SP.getBoolean("useInternalScriptFinder", false)) { diff --git a/qpython/src/main/java/org/qpython/qpy/console/shortcuts/FSNavigator.java b/qpython/src/main/java/org/qpython/qpy/console/shortcuts/FSNavigator.java index 3a95584c..a20893ea 100644 --- a/qpython/src/main/java/org/qpython/qpy/console/shortcuts/FSNavigator.java +++ b/qpython/src/main/java/org/qpython/qpy/console/shortcuts/FSNavigator.java @@ -19,7 +19,10 @@ import android.widget.TextView; import android.widget.Toast; +import com.quseit.util.FileUtils; + import org.qpython.qpy.R; +import org.qpython.qpy.main.app.App; import java.io.File; @@ -57,7 +60,7 @@ public void onCreate(android.os.Bundle savedInstanceState) { getWindow().setSoftInputMode(android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); Intent intent = getIntent(); - extSdCardFile = Environment.getExternalStorageDirectory(); + extSdCardFile = FileUtils.getPath(App.getContext()); extSdCard = getCanonicalPath(extSdCardFile); Uri uri = intent.getData(); String path = uri == null ? null : uri.getPath(); diff --git a/qpython/src/main/java/org/qpython/qpy/console/shortcuts/ShortcutReceiver.java b/qpython/src/main/java/org/qpython/qpy/console/shortcuts/ShortcutReceiver.java new file mode 100644 index 00000000..848230e7 --- /dev/null +++ b/qpython/src/main/java/org/qpython/qpy/console/shortcuts/ShortcutReceiver.java @@ -0,0 +1,16 @@ +package org.qpython.qpy.console.shortcuts; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import org.qpython.qsl4a.qsl4a.LogUtil; + +public class ShortcutReceiver extends BroadcastReceiver { + + + @Override + public void onReceive(Context context, Intent intent) { + LogUtil.e("111111111" + intent); + } +} diff --git a/qpython/src/main/java/org/qpython/qpy/main/activity/AppListActivity.java b/qpython/src/main/java/org/qpython/qpy/main/activity/AppListActivity.java index e964f622..a1252ea5 100644 --- a/qpython/src/main/java/org/qpython/qpy/main/activity/AppListActivity.java +++ b/qpython/src/main/java/org/qpython/qpy/main/activity/AppListActivity.java @@ -1,20 +1,35 @@ package org.qpython.qpy.main.activity; -import android.app.Activity; +import android.Manifest; +import android.app.ActivityManager; import android.app.AlertDialog; import android.app.LoaderManager; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.Loader; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; +import android.graphics.drawable.Icon; +import android.os.Build; import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; +import android.support.annotation.RequiresApi; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.widget.TextView; +import android.widget.Toast; import com.quseit.util.FileHelper; +import com.quseit.util.FileUtils; import com.quseit.util.FolderUtils; import com.quseit.util.NAction; @@ -23,12 +38,15 @@ import org.greenrobot.eventbus.ThreadMode; import org.qpython.qpy.R; import org.qpython.qpy.console.ScriptExec; +import org.qpython.qpy.console.shortcuts.ShortcutReceiver; import org.qpython.qpy.main.adapter.AppListAdapter; import org.qpython.qpy.main.event.AppsLoader; import org.qpython.qpy.main.model.AppModel; import org.qpython.qpy.main.model.QPyScriptModel; +import org.qpython.qpy.utils.ShortcutUtil; import org.qpython.qpysdk.QPyConstants; import org.qpython.qpysdk.utils.Utils; +import org.qpython.qsl4a.qsl4a.LogUtil; import java.io.File; import java.io.IOException; @@ -36,6 +54,8 @@ import java.util.Arrays; import java.util.List; +import static org.qpython.qpy.R2.string.show; + /** * Local App list * Created by Hmei on 2017-05-22. @@ -43,10 +63,13 @@ public class AppListActivity extends BaseActivity implements LoaderManager.LoaderCallbacks> { public static final String TYPE_SCRIPT = "script"; + private static final int REQUEST_INSTALL_SHORTCUT = 0; private List dataList; private AppListAdapter adapter; + ShortcutReceiver receiver; + public static void start(Context context, String type) { Intent starter = new Intent(context, AppListActivity.class); starter.putExtra("type", type); @@ -56,12 +79,25 @@ public static void start(Context context, String type) { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + initShortcutReceiver(); runShortcut(); setContentView(R.layout.activity_local_app); initView(); EventBus.getDefault().register(this); } + private void initShortcutReceiver() { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_CREATE_SHORTCUT); + filter.addAction("com.android.launcher.action.INSTALL_SHORTCUT"); + filter.addAction("android.content.pm.action.CONFIRM_PIN_SHORTCUT"); + filter.addAction(Intent.ACTION_VIEW); + + receiver = new ShortcutReceiver(); + registerReceiver(receiver,filter); + + } + @Override protected void onResume() { super.onResume(); @@ -74,6 +110,9 @@ protected void onResume() { @Override protected void onDestroy() { super.onDestroy(); + if (receiver != null){ + unregisterReceiver(receiver); + } EventBus.getDefault().unregister(this); } @@ -90,6 +129,7 @@ private void runShortcut() { } } + QPyScriptModel mBean; private void initView() { dataList = new ArrayList<>(); adapter = new AppListAdapter(dataList, getIntent().getStringExtra("type"), this); @@ -104,6 +144,16 @@ public void runProject(QPyScriptModel item) { ScriptExec.getInstance().playProject(AppListActivity.this, item.getPath(), false); } + @Override + public void createShortcut(QPyScriptModel item) { + mBean = item; +// if (!checkPermission()){ +// return; +// } + createShortcutOnThis(); +// test(); + } + @Override public void exit() { AppListActivity.this.finish(); @@ -116,14 +166,117 @@ public void exit() { appsView.setAdapter(adapter); ((TextView) findViewById(R.id.tv_folder_name)).setText(R.string.qpy_app); + findViewById(R.id.iv_back).setOnClickListener(view -> AppListActivity.this.finish()); + getScriptList(); } +// private boolean checkPermission() { +// if (Build.VERSION.SDK_INT >= 23) { +// int checkPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.INSTALL_SHORTCUT); +// LogUtil.e("checkPermission" + checkPermission); +// LogUtil.e("checkPermission" + PackageManager.PERMISSION_GRANTED); +// if (checkPermission != PackageManager.PERMISSION_GRANTED) { +// ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.INSTALL_SHORTCUT}, REQUEST_INSTALL_SHORTCUT); +// return false; +// } else { +// return true; +// } +// } else { +// return true; +// } +// } + +// @Override +// public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { +// if (requestCode == REQUEST_INSTALL_SHORTCUT) { +// if (grantResults[0] != PackageManager.PERMISSION_GRANTED) { +// Toast.makeText(this, R.string.toast_read_permission_deny, Toast.LENGTH_SHORT).show(); +// } else { +// createShortcutOnThis(); +// } +// } +// } + + @RequiresApi(api = Build.VERSION_CODES.N_MR1) + private void test(){ + judgeShortcutNameV2("org.qpython.qpy"); + } + + private void createShortcutOnThis(){ + if (mBean == null){ + return; + } + + Intent intent = new Intent(); + intent.setClass(this, AppListActivity.class); + intent.setAction(Intent.ACTION_VIEW); + intent.putExtra("type", "script"); + intent.putExtra("path", mBean.getPath()); + intent.putExtra("isProj", mBean.isProj()); + + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + ShortcutManager mShortcutManager = getSystemService(ShortcutManager.class); + if (mShortcutManager.isRequestPinShortcutSupported()) { + ShortcutInfo pinShortcutInfo = + new ShortcutInfo.Builder(this, mBean.getLabel()) + .setShortLabel(mBean.getLabel()) + .setLongLabel(mBean.getLabel()) + .setIcon(Icon.createWithResource(this, mBean.getIconRes())) + .setIntent(intent) + .build(); + Intent pinnedShortcutCallbackIntent = + mShortcutManager.createShortcutResultIntent(pinShortcutInfo); + PendingIntent successCallback = PendingIntent.getBroadcast(this, 0, + pinnedShortcutCallbackIntent, 0); + LogUtil.e("createShortcut: " + "111111111111"); + mShortcutManager.requestPinShortcut(pinShortcutInfo, + successCallback.getIntentSender()); + LogUtil.e("createShortcut: " + mBean.getLabel()); + new Handler().postDelayed(() -> { + judgeShortcutNameV2("org.qpython.qpy"); +// judgeShortcutName(mBean.getLabel()); + },200); + } + } else { + //Adding shortcut for MainActivity + //on Home screen + Intent addIntent = new Intent(); + addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent); + addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, mBean.getLabel()); + addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, + Intent.ShortcutIconResource.fromContext(getApplicationContext(), + mBean.getIconRes())); + addIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); + getApplicationContext().sendBroadcast(addIntent); + Toast.makeText(this, getString(R.string.shortcut_create_suc, mBean.getLabel()), Toast.LENGTH_SHORT).show(); + } + } + +// private void judgeShortcutName(String name) { +// int shortcutNum = 0; +// for (String packageName : ShortcutUtil.getAllTheLauncher(getApplicationContext())) { +// LogUtil.e("packageName111111: " + packageName); +// if (name.equals(packageName)) { +// shortcutNum ++; +// } +// } +// LogUtil.e("packageName222222: " + shortcutNum); +// } + + @RequiresApi(api = Build.VERSION_CODES.N_MR1) + private void judgeShortcutNameV2(String name) { + if (!ShortcutUtil.getShortcutInfo(getApplicationContext()).isEmpty()){ + return; + } + Toast.makeText(this, getString(R.string.shortcut_create_fail), Toast.LENGTH_SHORT).show(); + } + private void getScriptList() { try { String projectPath = NAction.isQPy3(this.getApplicationContext())? QPyConstants.DFROM_PRJ3:QPyConstants.DFROM_PRJ2; - File[] projectFiles = FileHelper.getABSPath(QPyConstants.ABSOLUTE_PATH + "/" + projectPath).listFiles(); + File[] projectFiles = FileHelper.getABSPath(FileUtils.getAbsolutePath(getApplicationContext()) + "/" + projectPath).listFiles(); if (projectFiles != null) { Arrays.sort(projectFiles, FolderUtils.sortByName); dataList.clear(); @@ -135,7 +288,7 @@ private void getScriptList() { } String scriptPath = NAction.isQPy3(this.getApplicationContext())?QPyConstants.DFROM_QPY3:QPyConstants.DFROM_QPY2; - File[] files = FileHelper.getFilesByType(FileHelper.getABSPath(QPyConstants.ABSOLUTE_PATH + "/" + scriptPath)); + File[] files = FileHelper.getFilesByType(FileHelper.getABSPath(FileUtils.getAbsolutePath(getApplicationContext()) + "/" + scriptPath)); if (files!=null && files.length > 0) { Arrays.sort(files, FolderUtils.sortByName); for (File file : files) { diff --git a/qpython/src/main/java/org/qpython/qpy/main/activity/HomeMainActivity.java b/qpython/src/main/java/org/qpython/qpy/main/activity/HomeMainActivity.java index c058e0cd..21fc01f5 100644 --- a/qpython/src/main/java/org/qpython/qpy/main/activity/HomeMainActivity.java +++ b/qpython/src/main/java/org/qpython/qpy/main/activity/HomeMainActivity.java @@ -19,6 +19,8 @@ import android.view.View; import android.widget.Toast; +import com.gyf.cactus.Cactus; +import com.quseit.util.FileUtils; import com.quseit.util.NAction; import com.quseit.util.Utils; @@ -58,6 +60,7 @@ public class HomeMainActivity extends BaseActivity { private static final int LOGIN_REQUEST_CODE = 136; private ActivityMainBinding binding; + private SharedPreferences preferences; public static void start(Context context) { Intent starter = new Intent(context, HomeMainActivity.class); @@ -73,6 +76,7 @@ public static void start(Context context, String userName) { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + preferences = PreferenceManager.getDefaultSharedPreferences(this); binding = DataBindingUtil.setContentView(this, R.layout.activity_main); //App.setActivity(this); startMain(); @@ -88,6 +92,8 @@ private void initIcon() { case "2.x": binding.icon.setImageResource(R.drawable.img_home_logo); break; + default: + break; } } @@ -101,7 +107,7 @@ private void initUser() { private void startMain() { initListener(); - startPyService(); +// startPyService(); Bus.getDefault().register(this); init(); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { @@ -146,6 +152,8 @@ private void initListener() { .setTitle(R.string.choose_action) .setItems(chars, (dialog, which) -> { switch (which) { + default: + break; case 0: // Create Shortcut TermActivity.startActivity(HomeMainActivity.this); break; @@ -210,6 +218,11 @@ public void onClick(DialogInterface dialogInterface, int i) { protected void onDestroy() { super.onDestroy(); Bus.getDefault().unregister(this); + boolean isKeepAlive = preferences.getBoolean(getString(R.string.key_alive), false); + if (!isKeepAlive){ + return; + } + Cactus.getInstance().unregister(this); } private void handlePython3(Intent intent) { @@ -227,7 +240,7 @@ private void handlePython3(Intent intent) { } private void handleNotification(Bundle bundle) { - if (bundle == null) return; + if (bundle == null) {return;} if (!bundle.getBoolean("force") && !PreferenceManager.getDefaultSharedPreferences(this).getBoolean(getString(R.string.key_hide_push), true)) { return; } @@ -244,6 +257,7 @@ private void handleNotification(Bundle bundle) { Intent starter = new Intent(Intent.ACTION_VIEW, Uri.parse(link)); startActivity(starter); break; + default:break; } } } @@ -270,6 +284,7 @@ private void handleNotification() { Intent starter = new Intent(Intent.ACTION_VIEW, Uri.parse(link)); startActivity(starter); break; + default:break; } sharedPreferences.edit().clear().apply(); } catch (JSONException e) { @@ -310,11 +325,11 @@ protected void onPause() { super.onPause(); } - private void startPyService() { - Log.d(TAG, "startPyService"); - Intent intent = new Intent(this, QPyScriptService.class); - startService(intent); - } +// private void startPyService() { +// Log.d(TAG, "startPyService"); +// Intent intent = new Intent(this, QPyScriptService.class); +// startService(intent); +// } private void openQpySDK() { Log.d("HomeMainActivity", "openQpySDK"); @@ -368,7 +383,7 @@ private void initQPy(boolean py3) { if (py3) { qpysdk.extractRes("notebook3", HomeMainActivity.this.getFilesDir()); } - File externalStorage = new File(Environment.getExternalStorageDirectory(), "qpython"); + File externalStorage = new File(FileUtils.getPath(App.getContext()), "qpython"); FileHelper.createDirIfNExists(externalStorage + "/cache"); FileHelper.createDirIfNExists(externalStorage + "/log"); FileHelper.createDirIfNExists(externalStorage + "/notebooks"); @@ -386,7 +401,7 @@ private void initQPy(boolean py3) { * 初始化内置python项目 */ public void extractRes() { - File externalStorage = new File(QPyConstants.ABSOLUTE_PATH); + File externalStorage = new File(FileUtils.getAbsolutePath(getApplicationContext())); if (checkExpired("public", new File(externalStorage + "/lib").getAbsolutePath(), "programs"+NAction.getPyVer(this))) { String name, sFileName; InputStream content; @@ -403,23 +418,23 @@ public void extractRes() { content.reset(); if (sFileName.equals("projects2.zip")) { - Utils.createDirectoryOnExternalStorage("qpython/projects/"); - Utils.unzip(content, Environment.getExternalStorageDirectory().getAbsolutePath() + "/qpython/projects/", false); + Utils.createDirectoryOnExternalStorage(App.getContext(),"qpython/projects/"); + Utils.unzip(content, FileUtils.getQyPath(App.getContext()) + "/qpython/projects/", false); } else if (sFileName.equals("scripts2.zip")) { - Utils.unzip(content, Environment.getExternalStorageDirectory().getAbsolutePath() + "/qpython/scripts/", false); + Utils.unzip(content, FileUtils.getQyPath(App.getContext()) + "/qpython/scripts/", false); } else if (sFileName.equals("projects3.zip")) { - Utils.createDirectoryOnExternalStorage("qpython/projects3/"); - Utils.unzip(content, Environment.getExternalStorageDirectory().getAbsolutePath() + "/qpython/projects3/", false); + Utils.createDirectoryOnExternalStorage(App.getContext(),"qpython/projects3/"); + Utils.unzip(content, FileUtils.getQyPath(App.getContext()) + "/qpython/projects3/", false); } else if (sFileName.equals("scripts3.zip")) { - Utils.createDirectoryOnExternalStorage("qpython/scripts3/"); - Utils.unzip(content, Environment.getExternalStorageDirectory().getAbsolutePath() + "/qpython/scripts3/", false); + Utils.createDirectoryOnExternalStorage(App.getContext(),"qpython/scripts3/"); + Utils.unzip(content, FileUtils.getQyPath(App.getContext()) + "/qpython/scripts3/", false); } if (sFileName.equals("ipynb.zip")) { - Utils.createDirectoryOnExternalStorage("qpython/notebooks/"); - Utils.unzip(content, Environment.getExternalStorageDirectory().getAbsolutePath() + "/qpython/notebooks/", false); + Utils.createDirectoryOnExternalStorage(App.getContext(),"qpython/notebooks/"); + Utils.unzip(content, FileUtils.getQyPath(App.getContext()) + "/qpython/notebooks/", false); } } catch (Exception e) { @@ -470,4 +485,5 @@ public static class StartQrCodeActivityEvent { private void sendEvent(String evenName) { } + } diff --git a/qpython/src/main/java/org/qpython/qpy/main/activity/NotebookActivity.java b/qpython/src/main/java/org/qpython/qpy/main/activity/NotebookActivity.java index 38dbfe29..88f6b2b0 100644 --- a/qpython/src/main/java/org/qpython/qpy/main/activity/NotebookActivity.java +++ b/qpython/src/main/java/org/qpython/qpy/main/activity/NotebookActivity.java @@ -436,6 +436,7 @@ public boolean OnClickListener(String name) { } } + @Override public void toast(String msg) { Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); } diff --git a/qpython/src/main/java/org/qpython/qpy/main/activity/NotebookListActivity.java b/qpython/src/main/java/org/qpython/qpy/main/activity/NotebookListActivity.java index 7e7bb75f..28f2ec2b 100644 --- a/qpython/src/main/java/org/qpython/qpy/main/activity/NotebookListActivity.java +++ b/qpython/src/main/java/org/qpython/qpy/main/activity/NotebookListActivity.java @@ -9,9 +9,12 @@ import android.support.annotation.Nullable; import android.support.v7.widget.LinearLayoutManager; +import com.quseit.util.FileUtils; + import org.qpython.qpy.R; import org.qpython.qpy.databinding.ActivityNotebooklistBinding; import org.qpython.qpy.main.adapter.NotebookAdapter; +import org.qpython.qpy.main.app.App; import org.qpython.qpysdk.QPyConstants; import java.io.File; @@ -29,7 +32,7 @@ public class NotebookListActivity extends BaseActivity { private ActivityNotebooklistBinding mBinding; private NotebookAdapter mNotebookAdapter; private File mRootFile; - private static final String sdcard = Environment.getExternalStorageDirectory().getAbsolutePath(); + private static final String sdcard = FileUtils.getQyPath(App.getContext()); public static void start(Context context){ Intent intent = new Intent(context,NotebookListActivity.class); @@ -47,7 +50,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { } private void initRootDir() { - mRootFile = new File(QPyConstants.ABSOLUTE_PATH+"/notebooks"); + mRootFile = new File(FileUtils.getAbsolutePath(getApplicationContext()) +"/notebooks"); if (!mRootFile.exists()) { mRootFile.mkdirs(); } diff --git a/qpython/src/main/java/org/qpython/qpy/main/activity/QWebViewActivity.java b/qpython/src/main/java/org/qpython/qpy/main/activity/QWebViewActivity.java index e901c85f..eb3aa03f 100644 --- a/qpython/src/main/java/org/qpython/qpy/main/activity/QWebViewActivity.java +++ b/qpython/src/main/java/org/qpython/qpy/main/activity/QWebViewActivity.java @@ -32,6 +32,7 @@ import com.loopj.android.http.AsyncHttpResponseHandler; import com.quseit.base.QBaseApp; +import com.quseit.util.FileUtils; import com.quseit.util.NAction; import com.quseit.util.NUtil; @@ -224,16 +225,17 @@ public boolean onOptionsItemSelected(MenuItem item) { if (launchScript.contains("/scripts")) { String proj = new File(launchScript).getName(); - resultIntent.putExtra(LogActivity.LOG_PATH, QPyConstants.ABSOLUTE_LOG); + resultIntent.putExtra(LogActivity.LOG_PATH, FileUtils.getAbsoluteLogPath(App.getContext())); resultIntent.putExtra(LogActivity.LOG_TITLE, proj); } else { String proj = new File(launchScript).getParentFile().getName(); - resultIntent.putExtra(LogActivity.LOG_PATH, QPyConstants.ABSOLUTE_LOG); + resultIntent.putExtra(LogActivity.LOG_PATH, FileUtils.getAbsoluteLogPath(App.getContext())); resultIntent.putExtra(LogActivity.LOG_TITLE, proj); } startActivity(resultIntent); break; + default:break; } return true; } @@ -295,7 +297,7 @@ public void accessUrl() { } private void writeWebLog(String data) { - FileHelper.writeToFile(QPyConstants.ABSOLUTE_LOG,data+"\n", true); + FileHelper.writeToFile(FileUtils.getAbsoluteLogPath(App.getContext()),data+"\n", true); } // diff --git a/qpython/src/main/java/org/qpython/qpy/main/activity/SplashActivity.java b/qpython/src/main/java/org/qpython/qpy/main/activity/SplashActivity.java index 4d9fdfd6..ab213da0 100644 --- a/qpython/src/main/java/org/qpython/qpy/main/activity/SplashActivity.java +++ b/qpython/src/main/java/org/qpython/qpy/main/activity/SplashActivity.java @@ -1,30 +1,132 @@ package org.qpython.qpy.main.activity; import android.content.Intent; +import android.databinding.DataBindingUtil; +import android.graphics.Color; import android.os.Bundle; import android.support.annotation.Nullable; +import android.support.constraint.ConstraintLayout; import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.AppCompatTextView; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.method.LinkMovementMethod; +import android.util.Log; +import android.view.View; import org.qpython.qpy.R; +import org.qpython.qpy.databinding.ActivitySplashBinding; +import org.qpython.qpy.main.app.App; +import org.qpython.qpy.main.widget.MyCheckTextView; import java.util.Timer; import java.util.TimerTask; -public class SplashActivity extends AppCompatActivity { +public class SplashActivity extends AppCompatActivity implements View.OnClickListener, MyCheckTextView.ClickListener { + + ActivitySplashBinding binding; + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_splash); + binding = DataBindingUtil.setContentView(this, R.layout.activity_splash); +// setContentView(R.layout.activity_splash); + initClick(); + initData(); + } + + private void initClick() { + binding.tvPositive.setOnClickListener(this); + binding.tvNegative.setOnClickListener(this); + } + + private void initData() { + setContent(); + setAgreeContent(); + judgeAgreementStatus(); + } + + private void setAgreeContent() { + String one = "您可以阅读完整版"; + String two = "《服务协议》"; + String three = "和"; + String four = "《隐私政策》"; + + String content = one + two + three + four; + SpannableString str = new SpannableString(content); + str.setSpan(new MyCheckTextView(getApplicationContext(), 0, R.color.color_498fdd, this), one.length(), one.length() + two.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + str.setSpan(new MyCheckTextView(getApplicationContext(), 1, R.color.color_498fdd, this), one.length() + two.length() + three.length(), one.length() + two.length() + three.length() + four.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + binding.tvAgreeContent.setText(str); + + //不设置 没有点击事件 + binding.tvAgreeContent.setMovementMethod(LinkMovementMethod.getInstance()); + //设置点击后的颜色为透明 + binding.tvAgreeContent.setHighlightColor(Color.TRANSPARENT); + } + + private void setContent() { + StringBuffer buffer = new StringBuffer(); + buffer.append("1.为了给您提供发布服务,我们可能会向您申请摄像头权限、麦克风权限、收集存储权限;\n"); + buffer.append("2.为了向您推荐您附近的村庄,我们会向您申请位置权限;\n"); + buffer.append("3.为了账号安全,我们会向您申请系统设备权限收集设备信息、日志信息;\n"); + buffer.append("4.我们会努力采取各种安全技术保护您的个人信息。未经您同意,我们不会从第三方获取、共享或对外提供您的信息;\n"); + buffer.append("5.您还可以访问、更正、删除您的个人信息,我们为您提供了注销、投诉等多种不同方式。"); + binding.tvContent.setText(buffer.toString()); + } + + private void judgeAgreementStatus() { + if (App.getAgreementStatus()){ + delayJumpToMain(); + }else{ + binding.clAgreeLayout.setVisibility(View.VISIBLE); + } + } + + private void delayJumpToMain() { new Timer().schedule(new TimerTask() { @Override public void run() { - Intent intent = new Intent(SplashActivity.this,HomeMainActivity.class); - intent.setAction(getIntent().getAction()); - startActivity(intent); - finish(); + jumpToMain(); } - },1000); + }, 1000); + } + + private void jumpToMain(){ + Intent intent = new Intent(SplashActivity.this, HomeMainActivity.class); + intent.setAction(getIntent().getAction()); + startActivity(intent); + finish(); + } + + @Override + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.tv_positive){ + App.setAgreementStatus(true); + jumpToMain(); + return; + } + if (id == R.id.tv_negative){ + finish(); + } + } + + @Override + public void click(int mark) { + if (mark == 0) { + goServiceAgreement(); + }else{ + goPrivacyAgreement(); + } } + private void goPrivacyAgreement() { + QWebViewActivity.start(this, getString(R.string.privacy_agreement), "https://www.qpython.org/privacy-cn.html"); + } + + private void goServiceAgreement() { + QWebViewActivity.start(this, getString(R.string.service_agreement), "https://www.qpython.org/agreement-cn.html"); + } } \ No newline at end of file diff --git a/qpython/src/main/java/org/qpython/qpy/main/adapter/AppListAdapter.java b/qpython/src/main/java/org/qpython/qpy/main/adapter/AppListAdapter.java index 920a5d19..a5cb6aa3 100644 --- a/qpython/src/main/java/org/qpython/qpy/main/adapter/AppListAdapter.java +++ b/qpython/src/main/java/org/qpython/qpy/main/adapter/AppListAdapter.java @@ -28,6 +28,8 @@ import org.qpython.qpy.main.model.QPyScriptModel; import org.qpython.qpy.texteditor.EditorActivity; import org.qpython.qpy.texteditor.ui.view.EnterDialog; +import org.qpython.qsl4a.qsl4a.LogUtil; + import android.support.v7.app.AlertDialog; @@ -114,6 +116,8 @@ public void onBindViewHolder(MyViewHolder holder, int positi case 2: openToEdit(position); dialog.dismiss(); + default: + break; } }).setNegativeButton("CLOSE", new DialogInterface.OnClickListener() { @@ -146,6 +150,8 @@ public interface Callback { void runProject(QPyScriptModel item); + void createShortcut(QPyScriptModel item); + void exit(); } @@ -156,43 +162,46 @@ private boolean createShortCut(int position) { } // Create shortcut QPyScriptModel qPyScriptModel = (QPyScriptModel) dataList.get(position); - Intent intent = new Intent(); - intent.setClass(context, AppListActivity.class); - intent.setAction(Intent.ACTION_VIEW); - intent.putExtra("type", "script"); - intent.putExtra("path", qPyScriptModel.getPath()); - intent.putExtra("isProj", qPyScriptModel.isProj()); - - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - ShortcutManager mShortcutManager = context.getSystemService(ShortcutManager.class); - if (mShortcutManager.isRequestPinShortcutSupported()) { - ShortcutInfo pinShortcutInfo = - new ShortcutInfo.Builder(context, dataList.get(position).getLabel()) - .setShortLabel(dataList.get(position).getLabel()) - .setLongLabel(dataList.get(position).getLabel()) - .setIcon(Icon.createWithResource(context, dataList.get(position).getIconRes())) - .setIntent(intent) - .build(); - Intent pinnedShortcutCallbackIntent = - mShortcutManager.createShortcutResultIntent(pinShortcutInfo); - PendingIntent successCallback = PendingIntent.getBroadcast(context, 0, - pinnedShortcutCallbackIntent, 0); - mShortcutManager.requestPinShortcut(pinShortcutInfo, - successCallback.getIntentSender()); - } - } else { - //Adding shortcut for MainActivity - //on Home screen - Intent addIntent = new Intent(); - addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent); - addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, dataList.get(position).getLabel()); - addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, - Intent.ShortcutIconResource.fromContext(context.getApplicationContext(), - dataList.get(position).getIconRes())); - addIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); - context.getApplicationContext().sendBroadcast(addIntent); - Toast.makeText(context, context.getString(R.string.shortcut_create_suc, dataList.get(position).getLabel()), Toast.LENGTH_SHORT).show(); - } +// Intent intent = new Intent(); +// intent.setClass(context, AppListActivity.class); +// intent.setAction(Intent.ACTION_VIEW); +// intent.putExtra("type", "script"); +// intent.putExtra("path", qPyScriptModel.getPath()); +// intent.putExtra("isProj", qPyScriptModel.isProj()); +// +// if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { +// ShortcutManager mShortcutManager = context.getSystemService(ShortcutManager.class); +// if (mShortcutManager.isRequestPinShortcutSupported()) { +// ShortcutInfo pinShortcutInfo = +// new ShortcutInfo.Builder(context, dataList.get(position).getLabel()) +// .setShortLabel(dataList.get(position).getLabel()) +// .setLongLabel(dataList.get(position).getLabel()) +// .setIcon(Icon.createWithResource(context, dataList.get(position).getIconRes())) +// .setIntent(intent) +// .build(); +// Intent pinnedShortcutCallbackIntent = +// mShortcutManager.createShortcutResultIntent(pinShortcutInfo); +// PendingIntent successCallback = PendingIntent.getBroadcast(context, 0, +// pinnedShortcutCallbackIntent, 0); +// +// boolean aaa = mShortcutManager.requestPinShortcut(pinShortcutInfo, +// successCallback.getIntentSender()); +// LogUtil.e("11111111111" + aaa); +// } +// } else { +// //Adding shortcut for MainActivity +// //on Home screen +// Intent addIntent = new Intent(); +// addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent); +// addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, dataList.get(position).getLabel()); +// addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, +// Intent.ShortcutIconResource.fromContext(context.getApplicationContext(), +// dataList.get(position).getIconRes())); +// addIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); +// context.getApplicationContext().sendBroadcast(addIntent); +// Toast.makeText(context, context.getString(R.string.shortcut_create_suc, dataList.get(position).getLabel()), Toast.LENGTH_SHORT).show(); +// } + callback.createShortcut(qPyScriptModel); return true; } diff --git a/qpython/src/main/java/org/qpython/qpy/main/adapter/CloudScriptAdapter.java b/qpython/src/main/java/org/qpython/qpy/main/adapter/CloudScriptAdapter.java index 7df33aca..4efc8958 100644 --- a/qpython/src/main/java/org/qpython/qpy/main/adapter/CloudScriptAdapter.java +++ b/qpython/src/main/java/org/qpython/qpy/main/adapter/CloudScriptAdapter.java @@ -6,6 +6,8 @@ import android.view.ViewGroup; +import com.quseit.util.FileUtils; + import org.qpython.qpy.R; import org.qpython.qpy.codeshare.pojo.CloudFile; import org.qpython.qpy.databinding.ItemFolderBinding; @@ -50,7 +52,7 @@ public void onBindViewHolder(MyViewHolder holder, int positio } else { binding.uploading.setVisibility(View.GONE); } - if (new File(QPyConstants.ABSOLUTE_PATH + cloudFile.getPath()).exists()) { + if (new File(FileUtils.getAbsolutePath(binding.uploaded.getContext().getApplicationContext()) + cloudFile.getPath()).exists()) { binding.uploaded.setImageResource(R.drawable.ic_check_circle); binding.uploaded.setVisibility(View.VISIBLE); } else { diff --git a/qpython/src/main/java/org/qpython/qpy/main/app/App.java b/qpython/src/main/java/org/qpython/qpy/main/app/App.java index 95ea64e2..445d0411 100644 --- a/qpython/src/main/java/org/qpython/qpy/main/app/App.java +++ b/qpython/src/main/java/org/qpython/qpy/main/app/App.java @@ -1,18 +1,26 @@ package org.qpython.qpy.main.app; +import android.app.ActivityManager; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; +import android.preference.PreferenceManager; import android.support.multidex.MultiDex; import android.support.v7.app.AppCompatActivity; +import android.util.Log; import com.google.gson.Gson; +import com.gyf.cactus.Cactus; +import com.gyf.cactus.callback.CactusCallback; import com.quseit.common.CrashHandler; import com.quseit.common.updater.downloader.DefaultDownloader; +import com.quseit.util.FileUtils; import com.squareup.leakcanary.LeakCanary; +import org.qpython.qpy.R; import org.qpython.qpy.main.server.Service; import org.qpython.qpy.main.server.gist.Api; import org.qpython.qpy.main.server.gist.TokenManager; @@ -20,7 +28,9 @@ import org.qpython.qpy.main.server.http.Retrofitor; import org.qpython.qpy.utils.NotebookUtil; import org.qpython.qpysdk.QPyConstants; +import org.qpython.qsl4a.QPyScriptService; import org.qpython.qsl4a.QSL4APP; +import org.qpython.qsl4a.qsl4a.LogUtil; import java.util.ArrayList; import java.util.HashMap; @@ -35,21 +45,22 @@ import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; -public class App extends QSL4APP { +public class App extends QSL4APP implements CactusCallback{ + private static final String TAG = "APP"; private static Context sContext; - private static String sScriptPath; - private static String sProjectPath; + private static String sScriptPath; + private static String sProjectPath; //private static AppCompatActivity sActivity; - private static OkHttpClient okHttpClient; + private static OkHttpClient okHttpClient; private static HttpLoggingInterceptor interceptor; - private static Gson gson; + private static Gson gson; private static DefaultDownloader downloader; //保存user信息 - private static SharedPreferences mPreferences; + private static SharedPreferences mPreferences; private static Retrofit.Builder retrofitBuilder; @@ -87,7 +98,7 @@ public static Context getContext() { return sContext; } -// public static AppCompatActivity getActivity() { + // public static AppCompatActivity getActivity() { // return sActivity; // } // @@ -107,6 +118,18 @@ public static User getUser() { return user; } + public static boolean getAgreementStatus(){ + return mPreferences.getBoolean("user_agree_status",false); + } + + public static void setAgreementStatus(boolean status){ + SharedPreferences.Editor editor = mPreferences.edit(); + editor.putBoolean("user_agree_status",status); + if (!editor.commit()) { + editor.apply(); + } + } + public static DefaultDownloader getDownloader() { return downloader; } @@ -178,7 +201,7 @@ public void onCreate() { .addCallAdapterFactory(RxJavaCallAdapterFactory.create()); mService = new Service(); - String basePath = QPyConstants.ABSOLUTE_PATH; + String basePath = FileUtils.getAbsolutePath(getApplicationContext()); sProjectPath = String.format("%s/%s", basePath, "projects"); sScriptPath = String.format("%s/%s", basePath, "scripts"); @@ -197,7 +220,7 @@ public void onCreate() { .getInstance() .setTimeOut(Retrofitor.DEFAULT_TIMEOUT) // .openDebug(BuildConfig.DEBUG) - // .supportSSL(!BuildConfig.DEBUG) + // .supportSSL(!BuildConfig.DEBUG) .addHeaders(header) .init(Api.BASE_URL); @@ -213,8 +236,30 @@ public void onCreate() { NotebookUtil.killNBSrv(this); NotebookUtil.startNotebookService2(this); } + + initCactus(); } + private void initCactus() { + boolean isKeepAlive = PreferenceManager.getDefaultSharedPreferences(this).getBoolean(getString(R.string.key_alive), false); + LogUtil.e("isKeepAlive:" + isKeepAlive); + if (!isKeepAlive){ + LogUtil.e("doWork0000000"); + if (!isRunService( "org.qpython.qsl4a.QPyScriptService")) { + startPyService(); + } + return; + } + Cactus.getInstance() + .isDebug(true) + .setTitle("QPython") + .setContent("QPython service is alive") + .setLargeIcon(R.drawable.ic_launcher) + .setSmallIcon(R.drawable.ic_launcher) + .hideNotificationAfterO(false) + .addCallback(this) + .register(this); + } private void initLayoutDir() { if (Build.VERSION.SDK_INT >= 17) { @@ -225,4 +270,45 @@ private void initLayoutDir() { resources.updateConfiguration(config, resources.getDisplayMetrics()); } } + + /** + * 保活的回调接口 + * @param i + */ + @Override + public void doWork(int i) { + LogUtil.e("doWork1111111"); + if (!isRunService( "org.qpython.qsl4a.QPyScriptService")) { + startPyService(); + } + } + + /** + * 保活的回调接口 + */ + @Override + public void onStop() {} + + private void startPyService() { + LogUtil.e("doWork22222"); + Log.d(TAG, "startPyService"); + Intent intent = new Intent(this, QPyScriptService.class); + startService(intent); + } + + /** + * 判断服务是否在运行 + * + * @param serviceName + * @return 服务名称为全路径 例如com.ghost.WidgetUpdateService + */ + public boolean isRunService(String serviceName) { + ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); + for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { + if (serviceName.equals(service.service.getClassName())) { + return true; + } + } + return false; + } } diff --git a/qpython/src/main/java/org/qpython/qpy/main/app/CONF.java b/qpython/src/main/java/org/qpython/qpy/main/app/CONF.java index 6a37732c..898dc60a 100644 --- a/qpython/src/main/java/org/qpython/qpy/main/app/CONF.java +++ b/qpython/src/main/java/org/qpython/qpy/main/app/CONF.java @@ -6,18 +6,18 @@ public class CONF implements QPyConstants { - public static final String LIB_DOWNLOAD_TEMP = QPyConstants.ABSOLUTE_PATH + "/cache"; - public static final String QPYPI_URL = "https://pypi.org/simple/"; +// public static final String LIB_DOWNLOAD_TEMP = QPyConstants.ABSOLUTE_PATH + "/cache"; +// public static final String QPYPI_URL = "https://pypi.org/simple/"; public static final String NOTIFICATION_SP_NAME = "NOTIFICATION_EXTRA"; - public static final String NOTIFICATION_SP_OBJ = "NOTIFICATION_OBJ"; + public static final String NOTIFICATION_SP_OBJ = "NOTIFICATION_OBJ"; public static final String IAP_NUM_REQUEST_URL = "http://apu.quseit.com/conf/iaplognum/org.qpython.qpy/"; - public static final String GOOGLE_ID_TOKEN = "955258715976-i6t5usa0tjg8favq17lsfaj885l4lilv.apps.googleusercontent.com"; + public static final String GOOGLE_ID_TOKEN = "955258715976-i6t5usa0tjg8favq17lsfaj885l4lilv.apps.googleusercontent.com"; - public static final String CLOUD_MAP_CACHE_PATH = QPyConstants.ABSOLUTE_PATH + "/lib/.cloud_cache"; +// public static final String CLOUD_MAP_CACHE_PATH = QPyConstants.ABSOLUTE_PATH + "/lib/.cloud_cache"; public static String qpypiPath() { - return App.getContext().getFilesDir().getAbsolutePath() + "/lib/python"+(NAction.isQPy3(App.getContext())?"3.6":"2.7")+"/site-packages/"; + return App.getContext().getFilesDir().getAbsolutePath() + "/lib/python" + (NAction.isQPy3(App.getContext()) ? "3.6" : "2.7") + "/site-packages/"; } } diff --git a/qpython/src/main/java/org/qpython/qpy/main/event/Bean.java b/qpython/src/main/java/org/qpython/qpy/main/event/Bean.java index 12076c31..9b7628d4 100644 --- a/qpython/src/main/java/org/qpython/qpy/main/event/Bean.java +++ b/qpython/src/main/java/org/qpython/qpy/main/event/Bean.java @@ -16,11 +16,13 @@ import org.qpython.qpy.main.activity.PurchaseActivity; import org.qpython.qpy.main.activity.QWebViewActivity; import org.qpython.qpy.main.activity.SettingActivity; +import org.qpython.qpy.main.app.App; import org.qpython.qpy.main.app.CONF; import org.qpython.qpy.main.utils.Utils; import org.qpython.qpy.texteditor.EditorActivity; import com.quseit.util.FileHelper; +import com.quseit.util.FileUtils; import org.qpython.qpy.utils.NotebookUtil; import org.qpython.qpysdk.QPyConstants; @@ -160,7 +162,7 @@ public String isNetworkOk(Context context) { public String returnTmpScript(String xcode, String flag, String param) { try { // - File root = new File(QPyConstants.ABSOLUTE_PATH + "/cache"); + File root = new File(FileUtils.getLibDownloadTempPath(App.getContext())); if (root != null) { FileHelper.clearDir(root.toString(), 0, false); } @@ -182,7 +184,7 @@ public String returnTmpScript(String xcode, String flag, String param) { } } else { - py = QPyConstants.ABSOLUTE_PATH + "/cache/main.py"; + py = FileUtils.getAbsolutePath(App.getContext()) + "/cache/main.py"; if (xcode.contains("#{HEADER}")) { code = xcode.replace("#{HEADER}", "PARAM = '" + param + "'"); @@ -239,13 +241,13 @@ public File getLibFile(String smodule, String cat) { File libFile; if (cat.equals("script")) { - libFile = new File(Environment.getExternalStorageDirectory(), "qpython/" + ubase + "/" + smodule); + libFile = new File(FileUtils.getPath(App.getContext()), "qpython/" + ubase + "/" + smodule); } else if (cat.equals("user")) { - libFile = new File(Environment.getExternalStorageDirectory(), "qpython/" + pbase + "/" + smodule); + libFile = new File(FileUtils.getPath(App.getContext()), "qpython/" + pbase + "/" + smodule); } else if (cat.equals("component")) { - libFile = new File(Environment.getExternalStorageDirectory(), "qpython/lib/" + smodule); + libFile = new File(FileUtils.getPath(App.getContext()), "qpython/lib/" + smodule); } else { libFile = new File(context.getFilesDir(), "/lib/" + sbase + "/site-packages/" + smodule); diff --git a/qpython/src/main/java/org/qpython/qpy/main/fragment/FileFragment.java b/qpython/src/main/java/org/qpython/qpy/main/fragment/FileFragment.java index 2e14b019..a967b92e 100644 --- a/qpython/src/main/java/org/qpython/qpy/main/fragment/FileFragment.java +++ b/qpython/src/main/java/org/qpython/qpy/main/fragment/FileFragment.java @@ -13,6 +13,7 @@ import android.widget.Toast; import com.quseit.util.FileHelper; +import com.quseit.util.FileUtils; import com.quseit.util.ImageUtil; import com.quseit.util.NAction; import com.yanzhenjie.recyclerview.swipe.SwipeMenuCreator; @@ -20,6 +21,7 @@ import org.qpython.qpy.R; import org.qpython.qpy.databinding.FragmentRefreshRvBinding; +import org.qpython.qpy.main.app.App; import org.qpython.qpy.texteditor.TedLocalActivity; import org.qpython.qpy.texteditor.ui.adapter.FolderAdapter; import org.qpython.qpy.texteditor.ui.adapter.bean.FolderBean; @@ -43,11 +45,11 @@ public class FileFragment extends Fragment { public static final String PROJECT3 = "projects3"; public static final String SCRIPT3 = "scripts3"; - private static final String PROJECT_PATH = QPyConstants.ABSOLUTE_PATH + "/" + PROJECT; - private static final String SCRIPT_PATH = QPyConstants.ABSOLUTE_PATH + "/" + SCRIPT; + private static final String PROJECT_PATH = FileUtils.getAbsolutePath(App.getContext()) + "/" + PROJECT; + private static final String SCRIPT_PATH = FileUtils.getAbsolutePath(App.getContext()) + "/" + SCRIPT; - private static final String PROJECT_PATH3 = QPyConstants.ABSOLUTE_PATH + "/" + PROJECT3; - private static final String SCRIPT_PATH3 = QPyConstants.ABSOLUTE_PATH + "/" + SCRIPT3; + private static final String PROJECT_PATH3 = FileUtils.getAbsolutePath(App.getContext()) + "/" + PROJECT3; + private static final String SCRIPT_PATH3 = FileUtils.getAbsolutePath(App.getContext()) + "/" + SCRIPT3; private FolderAdapter adapter; private List dataList; diff --git a/qpython/src/main/java/org/qpython/qpy/main/fragment/LibProjectFragment.java b/qpython/src/main/java/org/qpython/qpy/main/fragment/LibProjectFragment.java index d83dc827..9aecb41b 100644 --- a/qpython/src/main/java/org/qpython/qpy/main/fragment/LibProjectFragment.java +++ b/qpython/src/main/java/org/qpython/qpy/main/fragment/LibProjectFragment.java @@ -17,6 +17,7 @@ import com.quseit.common.updater.downloader.Downloader; import com.quseit.util.ACache; +import com.quseit.util.FileUtils; import com.quseit.util.ImageUtil; import com.quseit.util.NAction; import com.quseit.util.NetStateUtil; @@ -49,14 +50,14 @@ */ public class LibProjectFragment extends RefreshFragment { - private static final int SCRIPT_CONSOLE_CODE = 1237; - private static String SCRIPT_DIR; // = QPyConstants.ABSOLUTE_PATH + "/" + QPyConstants.DFROM_QPY2 + "/"; - private static String PROJECT_DIR; // = QPyConstants.ABSOLUTE_PATH + "/" + QPyConstants.DFROM_PRJ2 + "/"; + private static final int SCRIPT_CONSOLE_CODE = 1237; + private static String SCRIPT_DIR; // = QPyConstants.ABSOLUTE_PATH + "/" + QPyConstants.DFROM_QPY2 + "/"; + private static String PROJECT_DIR; // = QPyConstants.ABSOLUTE_PATH + "/" + QPyConstants.DFROM_PRJ2 + "/"; - private List dataList; + private List dataList; private LibListAdapter adapter; private FragmentRefreshRvBinding binding; - private TextView header; + private TextView header; private int WIDTH = (int) ImageUtil.dp2px(60); @@ -77,8 +78,8 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); binding = DataBindingUtil.bind(view); - SCRIPT_DIR = QPyConstants.ABSOLUTE_PATH + "/" + (NAction.isQPy3(getActivity())?QPyConstants.DFROM_QPY3:QPyConstants.DFROM_QPY2) +"/"; - PROJECT_DIR = QPyConstants.ABSOLUTE_PATH + "/" + (NAction.isQPy3(getActivity())?QPyConstants.DFROM_PRJ3:QPyConstants.DFROM_PRJ2) +"/"; + SCRIPT_DIR = FileUtils.getAbsolutePath(App.getContext()) + "/" + (NAction.isQPy3(getActivity()) ? QPyConstants.DFROM_QPY3 : QPyConstants.DFROM_QPY2) + "/"; + PROJECT_DIR = FileUtils.getAbsolutePath(App.getContext()) + "/" + (NAction.isQPy3(getActivity()) ? QPyConstants.DFROM_PRJ3 : QPyConstants.DFROM_PRJ2) + "/"; initDataList(); initView(); @@ -161,6 +162,7 @@ private void openPip() { startActivity(intent); } } + private SwipeMenuCreator getMenu() { SwipeMenuItem detail = new SwipeMenuItem(getContext()) .setBackgroundColor(Color.parseColor("#FF4A4A4A")) @@ -201,33 +203,32 @@ private SwipeMenuCreator getMenu() { } - private void installTool(LibModel item) { String downloadDir = null; if (item.getCat().equals("script")) { - downloadDir = "qpython/"+(NAction.isQPy3(getActivity())?QPyConstants.DFROM_QPY3:QPyConstants.DFROM_QPY2); + downloadDir = "qpython/" + (NAction.isQPy3(getActivity()) ? QPyConstants.DFROM_QPY3 : QPyConstants.DFROM_QPY2); } else if (item.getCat().equals("user")) { - downloadDir = "qpython/"+(NAction.isQPy3(getActivity())?QPyConstants.DFROM_QPY3:QPyConstants.DFROM_QPY2); + downloadDir = "qpython/" + (NAction.isQPy3(getActivity()) ? QPyConstants.DFROM_QPY3 : QPyConstants.DFROM_QPY2); } // Download - App.getDownloader().download(item.getTitle(), item.getLink(), Environment.getExternalStorageDirectory()+"/"+downloadDir+"/"+item.getSmodule(), - new Downloader.Callback() { - @Override - public void pending(String name) { + App.getDownloader().download(item.getTitle(), item.getLink(), FileUtils.getPath(App.getContext()) + "/" + downloadDir + "/" + item.getSmodule(), + new Downloader.Callback() { + @Override + public void pending(String name) { - } + } - @Override - public void complete(String name, File installer) { - refresh(true); - } + @Override + public void complete(String name, File installer) { + refresh(true); + } - @Override - public void error(String err) { + @Override + public void error(String err) { - } - }); + } + }); } private void initListener() { diff --git a/qpython/src/main/java/org/qpython/qpy/main/fragment/SettingFragment.java b/qpython/src/main/java/org/qpython/qpy/main/fragment/SettingFragment.java index f5624c51..a7da8f2f 100644 --- a/qpython/src/main/java/org/qpython/qpy/main/fragment/SettingFragment.java +++ b/qpython/src/main/java/org/qpython/qpy/main/fragment/SettingFragment.java @@ -12,6 +12,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Environment; +import android.os.Handler; import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.PreferenceFragment; @@ -30,6 +31,7 @@ import com.loopj.android.http.JsonHttpResponseHandler; import com.quseit.base.QBaseApp; import com.quseit.common.updater.downloader.Downloader; +import com.quseit.util.FileUtils; import com.quseit.util.NAction; import com.quseit.util.NStorage; @@ -69,12 +71,12 @@ public class SettingFragment extends PreferenceFragment { private LoadingDialog mLoadingDialog; private SharedPreferences settings; - private Resources resources; - private Preference mPassWordPref, username_pref, portnum_pref, chroot_pref, lastlog; - private CheckBoxPreference sl4a, running_state, root, display_pwd, notebook_run; + private Resources resources; + private Preference mPassWordPref, username_pref, portnum_pref, chroot_pref, lastlog; + private CheckBoxPreference sl4a, running_state, root, display_pwd, notebook_run, keepAliveBox; - private PreferenceScreen py_inter,notebook_page; - private Preference py3,py2; //notebook_res, py2compatible + private PreferenceScreen py_inter, notebook_page; + private Preference py3, py2; //notebook_res, py2compatible //private Preference update_qpy3,update_qpy2compatible; private SwitchPreference log, app; @@ -99,7 +101,7 @@ public void onReceive(Context context, Intent intent) { static private String transformPassword(String password) { StringBuilder sb = new StringBuilder(password.length()); for (int i = 0; i < password.length(); ++i) - sb.append('*'); + {sb.append('*');} return sb.toString(); } @@ -140,7 +142,7 @@ private void initSettings() { ip = null; } - if (ip!=null) { + if (ip != null) { ipaddress.setSummary(ip.getHostAddress()); } else { ipaddress.setSummary(R.string.ip_address_need_wifi); @@ -153,7 +155,7 @@ private void initSettings() { notebook_page.setSummary(NotebookUtil.isNotebookLibInstall(getActivity()) ? R.string.notebook_installed : R.string.notebook_not_started); } else { - notebook_page.setSummary( R.string.notebook_py3_support); + notebook_page.setSummary(R.string.notebook_py3_support); } @@ -169,6 +171,7 @@ private void initSettings() { root = (CheckBoxPreference) findPreference(resources.getString(R.string.key_root)); + keepAliveBox = (CheckBoxPreference) findPreference(resources.getString(R.string.key_alive)); sl4a = (CheckBoxPreference) findPreference(resources.getString(R.string.key_sl4a)); app = (SwitchPreference) findPreference(getString(R.string.key_hide_push)); log = (SwitchPreference) findPreference(resources.getString(R.string.key_hide_noti)); @@ -185,6 +188,11 @@ private void initSettings() { root.setChecked(isRoot); root.setSummary(isRoot ? R.string.enable_root : R.string.disable_root); + boolean isKeepAlive; + isKeepAlive = settings.getBoolean(getString(R.string.key_alive), false); + keepAliveBox.setChecked(isKeepAlive); + keepAliveBox.setSummary(isKeepAlive ? R.string.enable_keep_alive : R.string.disable_keep_alive); + isRunning = isMyServiceRunning(QPyScriptService.class); sl4a.setChecked(isRunning); sl4a.setSummary(isRunning ? R.string.sl4a_running : R.string.sl4a_un_running); @@ -204,13 +212,14 @@ private void initSettings() { portnum_pref.setSummary(settings.getString(resources.getString(R.string.key_port_num), resources.getString(org.swiftp.R.string.portnumber_default))); chroot_pref.setSummary(settings.getString(resources.getString(R.string.key_root_dir), - Environment.getExternalStorageDirectory().getPath())); + FileUtils.getPath(App.getContext()).getPath())); py_inter.setSummary(NAction.isQPy3(getActivity()) ? R.string.py3_now : R.string.py2_now); //setNotebookCheckbox(); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean(getString(R.string.key_root), root.isChecked()); + editor.putBoolean(getString(R.string.key_alive), keepAliveBox.isChecked()); //editor.putString(getString(R.string.key_qpypi), qpypi.getSummary().toString()); editor.putString(getString(R.string.key_username), username_pref.getSummary().toString()); editor.putString(getString(R.string.key_ftp_pwd), settings.getString(mPassWordPref.getKey(), "ftp")); @@ -249,7 +258,7 @@ private boolean isMyServiceRunning(Class serviceClass) { private void initListener() { lastlog.setOnPreferenceClickListener(preference -> { - Utils.checkRunTimeLog(getActivity(), getString(R.string.last_log), QPyConstants.ABSOLUTE_LOG); + Utils.checkRunTimeLog(getActivity(), getString(R.string.last_log), FileUtils.getAbsoluteLogPath(App.getContext())); return false; }); @@ -262,13 +271,13 @@ private void initListener() { notebook_run.setChecked(NotebookUtil.isNBSrvSet(getActivity())); notebook_run.setOnPreferenceChangeListener((preference, newValue) -> { - if ((boolean)newValue) { + if ((boolean) newValue) { NotebookUtil.startNotebookService2(getActivity()); } else { NotebookUtil.killNBSrv(getActivity()); } - notebook_page.setSummary(NotebookUtil.isNotebookEnable(getActivity())?R.string.notebook_installed : R.string.notebook_not_started); + notebook_page.setSummary(NotebookUtil.isNotebookEnable(getActivity()) ? R.string.notebook_installed : R.string.notebook_not_started); return true; }); @@ -305,6 +314,20 @@ private void initListener() { } }); + keepAliveBox.setOnPreferenceChangeListener((preference, newValue) -> + { + boolean isCheck = (boolean) newValue; + settings.edit().putBoolean(getString(R.string.key_alive), isCheck).apply(); + Toast.makeText(getActivity(), R.string.keep_alive_tips, Toast.LENGTH_SHORT).show(); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + restartAppV2(); + } + },2000); + return true; + }); + sl4a.setOnPreferenceChangeListener((preference, newValue) -> { @@ -333,32 +356,32 @@ private void initListener() { }); findPreference(resources.getString(R.string.key_reset)). - setOnPreferenceClickListener(preference -> - { - NAction.startInstalledAppDetailsActivity(getActivity()); - return false; - }); + setOnPreferenceClickListener(preference -> + { + NAction.startInstalledAppDetailsActivity(getActivity()); + return false; + }); findPreference(resources.getString(R.string.key_about)). - setOnPreferenceClickListener(preference -> - { - AboutActivity.start(getActivity()); - return true; - }); + setOnPreferenceClickListener(preference -> + { + AboutActivity.start(getActivity()); + return true; + }); findPreference("course"). - setOnPreferenceClickListener(preference -> - { - CourseActivity.start(getActivity()); - return true; - }); + setOnPreferenceClickListener(preference -> + { + CourseActivity.start(getActivity()); + return true; + }); findPreference("community"). - setOnPreferenceClickListener(preference -> - { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(URL_COMMUNITY+"?from="+NAction.getCode(this.getActivity())))); - return true; - }); + setOnPreferenceClickListener(preference -> + { + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(URL_COMMUNITY + "?from=" + NAction.getCode(this.getActivity())))); + return true; + }); /* ====================FTP==================== */ running_state.setOnPreferenceChangeListener((preference, newValue) -> { @@ -475,6 +498,14 @@ private void initListener() { }); } + private void restartAppV2() { + Intent intent = getActivity().getPackageManager().getLaunchIntentForPackage(getActivity().getPackageName()); + PendingIntent restartIntent = PendingIntent.getActivity(getActivity().getApplicationContext(), 0, intent, PendingIntent.FLAG_ONE_SHOT); + AlarmManager mgr = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE); + mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, restartIntent); // 1秒钟后重启应用 + System.exit(0); + } + private void releaseNotebook(Preference preference) { Observable.create((Observable.OnSubscribe) subscriber -> { try { @@ -493,32 +524,32 @@ private void releaseNotebook(Preference preference) { } }) - .subscribeOn(Schedulers.io()) - .doOnSubscribe(() -> mLoadingDialog.show()) - .subscribeOn(AndroidSchedulers.mainThread()) - .observeOn(AndroidSchedulers.mainThread()) - .doOnTerminate(() -> mLoadingDialog.dismiss()) - .subscribe(new Observer() { - @Override - public void onCompleted() { + .subscribeOn(Schedulers.io()) + .doOnSubscribe(() -> mLoadingDialog.show()) + .subscribeOn(AndroidSchedulers.mainThread()) + .observeOn(AndroidSchedulers.mainThread()) + .doOnTerminate(() -> mLoadingDialog.dismiss()) + .subscribe(new Observer() { + @Override + public void onCompleted() { - } + } - @Override - public void onError(Throwable e) { + @Override + public void onError(Throwable e) { - } + } - @Override - public void onNext(Boolean aBoolean) { - Log.d(TAG, "onNext"); + @Override + public void onNext(Boolean aBoolean) { + Log.d(TAG, "onNext"); - NotebookUtil.startNotebookService2(getActivity()); - notebook_page.setSummary(NotebookUtil.isNotebookLibInstall(getActivity())?R.string.notebook_installed : R.string.notebook_not_started); + NotebookUtil.startNotebookService2(getActivity()); + notebook_page.setSummary(NotebookUtil.isNotebookLibInstall(getActivity()) ? R.string.notebook_installed : R.string.notebook_not_started); - } - }); + } + }); } private void installNotebook() { @@ -539,7 +570,7 @@ private void installNotebook() { private void extractNotebookRes(String path) { final String extarget = NotebookUtil.RELEASE_PATH; - if (path!=null && !path.equals("")) { + if (path != null && !path.equals("")) { File resf = new File(path); if (resf.exists()) { QPySDK qpySDK = new QPySDK(App.getContext(), getActivity()); @@ -550,9 +581,9 @@ private void extractNotebookRes(String path) { private void releaseQPycRes(String path) { - final String extarget = QPyConstants.PY_CACHE_PATH; + final String extarget = FileUtils.getPyCachePath(App.getContext()); - if (path!=null && !path.equals("")) { + if (path != null && !path.equals("")) { File res = new File(path); if (res.exists()) { @@ -569,7 +600,7 @@ private void releasePython2Standard(Preference preference) { QPySDK qpysdk = new QPySDK(getActivity(), getActivity()); qpysdk.extractRes("private1", getActivity().getFilesDir(), true); qpysdk.extractRes("private2", getActivity().getFilesDir(), true); - qpysdk.extractRes("private3", getActivity().getFilesDir(),true); + qpysdk.extractRes("private3", getActivity().getFilesDir(), true); subscriber.onNext(true); subscriber.onCompleted(); @@ -578,30 +609,30 @@ private void releasePython2Standard(Preference preference) { subscriber.onError(new Throwable("Failed to release Py2 resources")); } }) - .subscribeOn(Schedulers.io()) - .doOnSubscribe(() -> mLoadingDialog.show()) - .subscribeOn(AndroidSchedulers.mainThread()) - .observeOn(AndroidSchedulers.mainThread()) - .doOnTerminate(() -> mLoadingDialog.dismiss()) - .subscribe(new Observer() { - @Override - public void onCompleted() { + .subscribeOn(Schedulers.io()) + .doOnSubscribe(() -> mLoadingDialog.show()) + .subscribeOn(AndroidSchedulers.mainThread()) + .observeOn(AndroidSchedulers.mainThread()) + .doOnTerminate(() -> mLoadingDialog.dismiss()) + .subscribe(new Observer() { + @Override + public void onCompleted() { - } + } - @Override - public void onError(Throwable e) { - Toast.makeText(getActivity(), "Faild to extract Py2 resource", Toast.LENGTH_SHORT).show(); - } + @Override + public void onError(Throwable e) { + Toast.makeText(getActivity(), "Faild to extract Py2 resource", Toast.LENGTH_SHORT).show(); + } - @Override - public void onNext(Boolean aBoolean) { - NAction.setQPyInterpreter(getActivity(), "2.x"); - py_inter.setSummary(R.string.py2_now); + @Override + public void onNext(Boolean aBoolean) { + NAction.setQPyInterpreter(getActivity(), "2.x"); + py_inter.setSummary(R.string.py2_now); - getActivity().recreate(); - } - }); + getActivity().recreate(); + } + }); } private void releasePython2Compatable(Preference preference) { @@ -619,30 +650,30 @@ private void releasePython2Compatable(Preference preference) { subscriber.onError(new Throwable("Failed to release Py2 resources")); } }) - .subscribeOn(Schedulers.io()) - .doOnSubscribe(() -> mLoadingDialog.show()) - .subscribeOn(AndroidSchedulers.mainThread()) - .observeOn(AndroidSchedulers.mainThread()) - .doOnTerminate(() -> mLoadingDialog.dismiss()) - .subscribe(new Observer() { - @Override - public void onCompleted() { + .subscribeOn(Schedulers.io()) + .doOnSubscribe(() -> mLoadingDialog.show()) + .subscribeOn(AndroidSchedulers.mainThread()) + .observeOn(AndroidSchedulers.mainThread()) + .doOnTerminate(() -> mLoadingDialog.dismiss()) + .subscribe(new Observer() { + @Override + public void onCompleted() { - } + } - @Override - public void onError(Throwable e) { - Toast.makeText(getActivity(), "Faild to extract Py2 resource", Toast.LENGTH_SHORT).show(); - } + @Override + public void onError(Throwable e) { + Toast.makeText(getActivity(), "Faild to extract Py2 resource", Toast.LENGTH_SHORT).show(); + } - @Override - public void onNext(Boolean aBoolean) { - NAction.setQPyInterpreter(getActivity(), "2.x"); - py_inter.setSummary(R.string.py2_now); + @Override + public void onNext(Boolean aBoolean) { + NAction.setQPyInterpreter(getActivity(), "2.x"); + py_inter.setSummary(R.string.py2_now); - getActivity().recreate(); - } - }); + getActivity().recreate(); + } + }); } @@ -650,15 +681,15 @@ private void releasePython3(Preference preference) { QPySDK qpysdk = new QPySDK(this.getActivity(), this.getActivity()); Observable.create((Observable.OnSubscribe) subscriber -> { try { - releaseQPycRes(NStorage.getSP(App.getContext(),QPyConstants.KEY_PY3_RES)); + releaseQPycRes(NStorage.getSP(App.getContext(), QPyConstants.KEY_PY3_RES)); //extractQPyCore(false); qpysdk.extractRes("private31", getActivity().getFilesDir(), true); qpysdk.extractRes("private32", getActivity().getFilesDir(), true); - qpysdk.extractRes("private33", getActivity().getFilesDir(),true); + qpysdk.extractRes("private33", getActivity().getFilesDir(), true); qpysdk.extractRes("notebook3", getActivity().getFilesDir(), true); - File externalStorage = new File(Environment.getExternalStorageDirectory(), "qpython"); + File externalStorage = new File(FileUtils.getPath(App.getContext()), "qpython"); qpysdk.extractRes("publi3c", new File(externalStorage + "/lib")); @@ -669,30 +700,30 @@ private void releasePython3(Preference preference) { subscriber.onError(new Throwable("Failed to release Py3 resources")); } }) - .subscribeOn(Schedulers.io()) - .doOnSubscribe(() -> mLoadingDialog.show()) - .subscribeOn(AndroidSchedulers.mainThread()) - .observeOn(AndroidSchedulers.mainThread()) - .doOnTerminate(() -> mLoadingDialog.dismiss()) - .subscribe(new Observer() { - @Override - public void onCompleted() { + .subscribeOn(Schedulers.io()) + .doOnSubscribe(() -> mLoadingDialog.show()) + .subscribeOn(AndroidSchedulers.mainThread()) + .observeOn(AndroidSchedulers.mainThread()) + .doOnTerminate(() -> mLoadingDialog.dismiss()) + .subscribe(new Observer() { + @Override + public void onCompleted() { - } + } - @Override - public void onError(Throwable e) { - Toast.makeText(getActivity(), "Faild to extract Py3 resource", Toast.LENGTH_SHORT).show(); - } + @Override + public void onError(Throwable e) { + Toast.makeText(getActivity(), "Faild to extract Py3 resource", Toast.LENGTH_SHORT).show(); + } - @Override - public void onNext(Boolean aBoolean) { - NAction.setQPyInterpreter(getActivity(), "3.x"); - py_inter.setSummary(R.string.py3_now); + @Override + public void onNext(Boolean aBoolean) { + NAction.setQPyInterpreter(getActivity(), "3.x"); + py_inter.setSummary(R.string.py3_now); - getActivity().recreate(); - } - }); + getActivity().recreate(); + } + }); } @@ -757,206 +788,209 @@ private void getNotebook() { mLoadingDialog.show(); QBaseApp.getInstance().getAsyncHttpClient().get(getActivity(), NotebookUtil.getNBLink(getActivity()), - null, new JsonHttpResponseHandler() { - @Override - public void onSuccess(int statusCode, Header[] headers, JSONObject result) { - final String KEY_RES = "setting.notebookresource.ver"; - - try { - - final String notebook_resource_ver = NStorage.getSP(getActivity(), KEY_RES); - final String url = result.getString("link"); - final String target = result.getString("target"); - final String vercode = result.getString("vercode"); - final String title = result.getString("title"); - final String vername = result.getString("vername"); - final String path = NotebookUtil.RELEASE_PATH+"/"+target; - - NStorage.setSP(App.getContext(), NotebookUtil.getNbResFk(getActivity()), path); - - Log.d(TAG, "getNotebook:onSuccess:"+notebook_resource_ver+"["+vercode+"]"); + null, new JsonHttpResponseHandler() { + @Override + public void onSuccess(int statusCode, Header[] headers, JSONObject result) { + final String KEY_RES = "setting.notebookresource.ver"; - if (notebook_resource_ver.equals(vercode) && new File(path).exists()) { - mLoadingDialog.dismiss(); + try { - new AlertDialog.Builder(getActivity(), R.style.MyDialog) - .setTitle(title) - .setMessage(R.string.newest_resource) - .setPositiveButton(R.string.ok, (dialog1, which) -> { - try { - extractNotebookRes(path); - } catch (Exception e) { + final String notebook_resource_ver = NStorage.getSP(getActivity(), KEY_RES); + final String url = result.getString("link"); + final String target = result.getString("target"); + final String vercode = result.getString("vercode"); + final String title = result.getString("title"); + final String vername = result.getString("vername"); + final String path = NotebookUtil.RELEASE_PATH + "/" + target; - } - dialog1.dismiss(); - }) - .create() - .show(); + NStorage.setSP(App.getContext(), NotebookUtil.getNbResFk(getActivity()), path); - } else { - App.getDownloader().download(getString(R.string.download_notebook), url, path, new Downloader.Callback() { + Log.d(TAG, "getNotebook:onSuccess:" + notebook_resource_ver + "[" + vercode + "]"); - @Override - public void pending(String name) { - mLoadingDialog.cancel(); + if (notebook_resource_ver.equals(vercode) && new File(path).exists()) { + mLoadingDialog.dismiss(); new AlertDialog.Builder(getActivity(), R.style.MyDialog) - .setTitle(R.string.notice) - .setMessage(R.string.download_progress_need_sometime) - .setPositiveButton(R.string.ok, (dialog1, which) -> { - }) - .create() - .show(); - } + .setTitle(title) + .setMessage(R.string.newest_resource) + .setPositiveButton(R.string.ok, (dialog1, which) -> { + try { + extractNotebookRes(path); + } catch (Exception e) { + + } + dialog1.dismiss(); + }) + .create() + .show(); + + } else { + App.getDownloader().download(getString(R.string.download_notebook), url, path, new Downloader.Callback() { + + @Override + public void pending(String name) { + mLoadingDialog.cancel(); + + new AlertDialog.Builder(getActivity(), R.style.MyDialog) + .setTitle(R.string.notice) + .setMessage(R.string.download_progress_need_sometime) + .setPositiveButton(R.string.ok, (dialog1, which) -> { + }) + .create() + .show(); + } - @Override - public void complete(String name, File installer) { + @Override + public void complete(String name, File installer) { - Log.d(TAG, "getNotebook:complete:" + name + "[" + installer.getAbsolutePath() + "]"); - mLoadingDialog.dismiss(); + Log.d(TAG, "getNotebook:complete:" + name + "[" + installer.getAbsolutePath() + "]"); + mLoadingDialog.dismiss(); - NStorage.setSP(App.getContext(),KEY_RES, vercode); + NStorage.setSP(App.getContext(), KEY_RES, vercode); - // UNZIP resources && install - try { - extractNotebookRes(installer.getAbsolutePath()); - Toast.makeText(App.getContext(), R.string.file_downloaded, Toast.LENGTH_SHORT).show(); + // UNZIP resources && install + try { + extractNotebookRes(installer.getAbsolutePath()); + Toast.makeText(App.getContext(), R.string.file_downloaded, Toast.LENGTH_SHORT).show(); - } catch (Exception e) { + } catch (Exception e) { - } - } + } + } - @Override - public void error(String err) { - mLoadingDialog.cancel(); - try { - Toast.makeText(getActivity(), err, Toast.LENGTH_SHORT).show(); - } catch (Exception e) { + @Override + public void error(String err) { + mLoadingDialog.cancel(); + try { + Toast.makeText(getActivity(), err, Toast.LENGTH_SHORT).show(); + } catch (Exception e) { - } + } + } + }); } - }); + } catch (JSONException e) { + e.printStackTrace(); + } } - } catch (JSONException e) { - e.printStackTrace(); - } - } - @Override - public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { - // waitingWindow.dismiss(); - Log.d(TAG, "Error in checkConfUpdate:" + throwable.getMessage()); - } - }); + + @Override + public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { + // waitingWindow.dismiss(); + Log.d(TAG, "Error in checkConfUpdate:" + throwable.getMessage()); + } + }); } + private void getQPYC(boolean ispy2compatible) { mLoadingDialog.show(); - String conf_url = (ispy2compatible?QPyConstants.QPYC2COMPATIBLE:QPyConstants.QPYC3)+"?"+NAction.getUserUrl(getActivity()); + String conf_url = (ispy2compatible ? QPyConstants.QPYC2COMPATIBLE : QPyConstants.QPYC3) + "?" + NAction.getUserUrl(getActivity()); QBaseApp.getInstance().getAsyncHttpClient().get(getActivity(), conf_url, - null, new JsonHttpResponseHandler() { - @Override - public void onSuccess(int statusCode, Header[] headers, JSONObject result) { - final String KEY_RES = ispy2compatible?QPyConstants.QPYC2COMPATIBLE_VER_KEY:QPyConstants.QPYC3_VER_KEY; - - try { + null, new JsonHttpResponseHandler() { + @Override + public void onSuccess(int statusCode, Header[] headers, JSONObject result) { + final String KEY_RES = ispy2compatible ? QPyConstants.QPYC2COMPATIBLE_VER_KEY : QPyConstants.QPYC3_VER_KEY; - final String py_resource_ver = NStorage.getSP(getActivity(), KEY_RES); + try { - final String url = result.getString("link"); - final String target = result.getString("target"); - final String vercode = result.getString("vercode"); - final String title = result.getString("title"); - final String vername = result.getString("vername"); - final String path = QPyConstants.PY_CACHE_PATH + "/" + target; + final String py_resource_ver = NStorage.getSP(getActivity(), KEY_RES); - NStorage.setSP(App.getContext(), QPyConstants.KEY_PY3_RES, path); + final String url = result.getString("link"); + final String target = result.getString("target"); + final String vercode = result.getString("vercode"); + final String title = result.getString("title"); + final String vername = result.getString("vername"); + final String path = FileUtils.getPyCachePath(App.getContext()) + "/" + target; - Log.d(TAG, "getQPYC:onSuccess:"+py_resource_ver+"["+vercode+"]"); + NStorage.setSP(App.getContext(), QPyConstants.KEY_PY3_RES, path); - if (py_resource_ver.equals(vercode) && new File(path).exists()) { - mLoadingDialog.dismiss(); + Log.d(TAG, "getQPYC:onSuccess:" + py_resource_ver + "[" + vercode + "]"); - new AlertDialog.Builder(getActivity(), R.style.MyDialog) - .setTitle(title) - .setMessage(R.string.newest_resource) - .setPositiveButton(R.string.ok, (dialog1, which) -> { - try { - releaseQPycRes(path); - } catch (Exception e) { + if (py_resource_ver.equals(vercode) && new File(path).exists()) { + mLoadingDialog.dismiss(); + new AlertDialog.Builder(getActivity(), R.style.MyDialog) + .setTitle(title) + .setMessage(R.string.newest_resource) + .setPositiveButton(R.string.ok, (dialog1, which) -> { + try { + releaseQPycRes(path); + } catch (Exception e) { + + } + + dialog1.dismiss(); + }) + .create() + .show(); + + } else { + + App.getDownloader().download(getString(R.string.download_py), url, path, new Downloader.Callback() { + + @Override + public void pending(String name) { + mLoadingDialog.cancel(); + + new AlertDialog.Builder(getActivity(), R.style.MyDialog) + .setTitle(R.string.notice) + .setMessage(R.string.download_progress_need_sometime) + //.setNegativeButton(R.string.cancel, (dialog1, which) -> dialog1.dismiss()) + .setPositiveButton(R.string.ok, (dialog1, which) -> { + }) + .create() + .show(); } - dialog1.dismiss(); - }) - .create() - .show(); - - } else { - - App.getDownloader().download(getString(R.string.download_py), url, path, new Downloader.Callback() { - - @Override - public void pending(String name) { - mLoadingDialog.cancel(); + @Override + public void complete(String name, File installer) { - new AlertDialog.Builder(getActivity(), R.style.MyDialog) - .setTitle(R.string.notice) - .setMessage(R.string.download_progress_need_sometime) - //.setNegativeButton(R.string.cancel, (dialog1, which) -> dialog1.dismiss()) - .setPositiveButton(R.string.ok, (dialog1, which) -> { - }) - .create() - .show(); - } + Log.d(TAG, "getQPYC:complete:" + name + "[" + installer.getAbsolutePath() + "]"); + mLoadingDialog.dismiss(); - @Override - public void complete(String name, File installer) { + NStorage.setSP(App.getContext(), KEY_RES, vercode); + // UNZIP resources && install + try { + releaseQPycRes(installer.getAbsolutePath()); - Log.d(TAG, "getQPYC:complete:" + name + "[" + installer.getAbsolutePath() + "]"); - mLoadingDialog.dismiss(); - - NStorage.setSP(App.getContext(),KEY_RES, vercode); - // UNZIP resources && install - try { - releaseQPycRes(installer.getAbsolutePath()); - - } catch (Exception e) { + } catch (Exception e) { + } } - } - @Override - public void error(String err) { - mLoadingDialog.cancel(); - try { - Toast.makeText(getActivity(), err, Toast.LENGTH_SHORT).show(); - } catch (Exception e) { + @Override + public void error(String err) { + mLoadingDialog.cancel(); + try { + Toast.makeText(getActivity(), err, Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + } } - } - }); + }); + } + } catch (JSONException e) { + e.printStackTrace(); } - } catch (JSONException e) { - e.printStackTrace(); - } - } - @Override - public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { - // waitingWindow.dismiss(); - Log.d(TAG, "Error in getQPYC:" + throwable.getMessage()); - } - }); + } + + @Override + public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { + // waitingWindow.dismiss(); + Log.d(TAG, "Error in getQPYC:" + throwable.getMessage()); + } + }); } private boolean isQPycRelease(boolean ispy2compatible) { boolean isRelease = true; - String[] py3Mp3File = getActivity().getResources().getStringArray(ispy2compatible?R.array.qpy2compatible_zip:R.array.qpy3_zip); + String[] py3Mp3File = getActivity().getResources().getStringArray(ispy2compatible ? R.array.qpy2compatible_zip : R.array.qpy3_zip); for (String s : py3Mp3File) { - isRelease = isRelease && new File(QPyConstants.PY_CACHE_PATH + "/" + s).exists(); + isRelease = isRelease && new File(FileUtils.getPyCachePath(App.getContext()) + "/" + s).exists(); } return isRelease; } @@ -964,8 +998,8 @@ private boolean isQPycRelease(boolean ispy2compatible) { private void removeQPyc2Core() { Log.d(TAG, "removeQPyc2Core"); String files = getActivity().getFilesDir().getAbsolutePath(); - String[] files2del = {files+"/lib/notebook.zip", files+"/lib/python27.zip", files+"/lib/python2.7"}; - for (int i=0;i { - File file = new File(QPyConstants.ABSOLUTE_PATH + "/" + (isQpy3? QPyConstants.DFROM_QPY3:QPyConstants.DFROM_QPY2) + "/" + name); + File file = new File(FileUtils.getAbsolutePath(App.getContext()) + "/" + (isQpy3? QPyConstants.DFROM_QPY3:QPyConstants.DFROM_QPY2) + "/" + name); if (file.exists()) { Crouton.showText(this, R.string.file_exists, Style.ALERT); @@ -924,7 +926,7 @@ protected void newProject(final String type) { Stack curArtistDir = new Stack<>(); final boolean isQpy3 = NAction.isQPy3(getApplicationContext()); - curArtistDir.push(QPyConstants.ABSOLUTE_PATH + curArtistDir.push(FileUtils.getAbsolutePath(App.getContext()) + "/" + (isQpy3 ? QPyConstants.DFROM_PRJ3 : QPyConstants.DFROM_PRJ2) + "/" + name); File fileN = new File(curArtistDir.peek()); diff --git a/qpython/src/main/java/org/qpython/qpy/texteditor/MFTPSettingActivity.java b/qpython/src/main/java/org/qpython/qpy/texteditor/MFTPSettingActivity.java index b3b45626..b77b1ea9 100644 --- a/qpython/src/main/java/org/qpython/qpy/texteditor/MFTPSettingActivity.java +++ b/qpython/src/main/java/org/qpython/qpy/texteditor/MFTPSettingActivity.java @@ -17,10 +17,12 @@ import android.widget.Toast; import com.quseit.base.QBaseDialog; +import com.quseit.util.FileUtils; import com.quseit.util.NAction; import com.quseit.util.NUtil; import org.qpython.qpy.R; +import org.qpython.qpy.main.app.App; import org.qpython.qpysdk.QPyConstants; import org.swiftp.Defaults; @@ -80,7 +82,7 @@ protected void onCreate(Bundle savedInstanceState) { //initWidgetTabItem(4); - String externalStorage = new File(Environment.getExternalStorageDirectory(), QPyConstants.BASE_PATH).getAbsolutePath(); + String externalStorage = new File(FileUtils.getPath(App.getContext()), QPyConstants.BASE_PATH).getAbsolutePath(); String frv = MessageFormat.format(getString(R.string.ftp_root), externalStorage); TextView fr = (TextView)findViewById(R.id.ftp_root_value); fr.setText(frv); @@ -125,7 +127,8 @@ public void onFTPOp(View v) { private void startServer() { Context context = getApplicationContext(); - NAction.setFtpRoot(context, Environment.getExternalStorageDirectory()+"/"+ QPyConstants.BASE_PATH); + NAction.setFtpRoot(context, FileUtils.getPath(App.getContext())+"/"+ QPyConstants.BASE_PATH); + Intent serverService = new Intent(context, FTPServerService.class); if (!org.swiftp.FTPServerService.isRunning()) { startService(serverService); diff --git a/qpython/src/main/java/org/qpython/qpy/texteditor/TedFragment.java b/qpython/src/main/java/org/qpython/qpy/texteditor/TedFragment.java index 7b1ee0ce..5437225d 100644 --- a/qpython/src/main/java/org/qpython/qpy/texteditor/TedFragment.java +++ b/qpython/src/main/java/org/qpython/qpy/texteditor/TedFragment.java @@ -25,6 +25,7 @@ import android.view.ViewGroup; import android.widget.Toast; +import com.quseit.util.FileUtils; import com.quseit.util.NAction; import com.quseit.util.NStorage; @@ -330,7 +331,7 @@ private void initSearchBarListener() { public void SnippetsList() { boolean isQpy3 = NAction.isQPy3(getContext()); List listItems = new ArrayList<>(); - String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath() + String baseDir = FileUtils.getQyPath(App.getContext()) + "/" + QPyConstants.BASE_PATH; String path = baseDir + (isQpy3 ? "/snippets3/" : "/snippets/"); String files; @@ -367,7 +368,7 @@ public void SnippetsList() { */ public void insertSnippet(String snippetName) throws IOException { boolean isQPy3 = NAction.isQPy3(getContext()); - String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath() + String baseDir = FileUtils.getQyPath(App.getContext()) + "/" + QPyConstants.BASE_PATH; String path = baseDir + (isQPy3 ? "/snippets3/" : "/snippets/"); String s; @@ -401,7 +402,7 @@ public void insertSnippet(String snippetName) throws IOException { public void saveCodeSnippet(String selectText) { boolean isQPy3 = NAction.isQPy3(getContext()); - String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath() + String baseDir = FileUtils.getQyPath(App.getContext()) + "/" + QPyConstants.BASE_PATH; String path = baseDir + (isQPy3 ? "/snippets3/" : "/snippets/"); diff --git a/qpython/src/main/java/org/qpython/qpy/texteditor/androidlib/data/FileUtils.java b/qpython/src/main/java/org/qpython/qpy/texteditor/androidlib/data/FileUtils.java index 58b343f4..7846df31 100644 --- a/qpython/src/main/java/org/qpython/qpy/texteditor/androidlib/data/FileUtils.java +++ b/qpython/src/main/java/org/qpython/qpy/texteditor/androidlib/data/FileUtils.java @@ -18,14 +18,13 @@ @SuppressLint("DefaultLocale") public class FileUtils { /** File of the external storage data */ - public static final File STORAGE = Environment - .getExternalStorageDirectory(); - /** Path to the external storage data */ - public static final String STORAGE_PATH = STORAGE.getAbsolutePath(); - - /** default android Download folder */ - public static final String DOWNLOAD_FOLDER = (STORAGE.getAbsolutePath() - + File.separator + "Download"); +// public static final File STORAGE = Environment.getExternalStorageDirectory(); +// /** Path to the external storage data */ +// public static final String STORAGE_PATH = STORAGE.getAbsolutePath(); +// +// /** default android Download folder */ +// public static final String DOWNLOAD_FOLDER = (STORAGE.getAbsolutePath() +// + File.separator + "Download"); /** * Copy all files in the given asset folder to the destination folder (must diff --git a/qpython/src/main/java/org/qpython/qpy/texteditor/androidlib/ui/activity/BrowsingActivity.java b/qpython/src/main/java/org/qpython/qpy/texteditor/androidlib/ui/activity/BrowsingActivity.java index c1058cc9..640dd347 100644 --- a/qpython/src/main/java/org/qpython/qpy/texteditor/androidlib/ui/activity/BrowsingActivity.java +++ b/qpython/src/main/java/org/qpython/qpy/texteditor/androidlib/ui/activity/BrowsingActivity.java @@ -27,238 +27,243 @@ import java.util.List; /** - * + * */ public abstract class BrowsingActivity extends Activity implements - OnItemClickListener { - - /** The list of files to display */ - protected ArrayList mList; - /** the dialog's list view */ - protected ListView mFilesList; - /** The list adapter */ - protected FileListAdapter mListAdapter; - - /** the current folder */ - protected File mCurrentFolder; - - /** the current file sort */ - protected Comparator mComparator; - - protected boolean mShowFoldersOnly = false; - protected boolean mShowHiddenFiles = true; - protected boolean mHideNonWriteableFiles = false; - protected List mExtensionsWhiteList; - protected List mExtensionsBlackList; - - /** - * @see android.app.Activity#onCreate(android.os.Bundle) - */ - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mExtensionsWhiteList = new ArrayList<>(); - mExtensionsBlackList = new ArrayList<>(); - mComparator = new ComparatorFilesAlpha(); - mListAdapter = new FileListAdapter(this, new LinkedList<>(), null); - } - - /** - * @see android.app.Activity#onResume() - */ - protected void onResume() { - super.onResume(); - - // Setup the widget - mFilesList = findViewById(android.R.id.list); - mFilesList.setOnItemClickListener(this); - - // set adapter - mFilesList.setAdapter(mListAdapter); - - // initial folder - File folder; - if (mCurrentFolder != null) { - folder = mCurrentFolder; - } else if ((FileUtils.STORAGE.exists()) - && (FileUtils.STORAGE.canRead())) { - folder = FileUtils.STORAGE; - } else { - folder = new File("/"); - } - - fillFolderView(folder); - } - - /** - * @see android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget.AdapterView, - * android.view.View, int, long) - */ - public void onItemClick(AdapterView parent, View view, int position, - long id) { - File file, canon; - - file = mList.get(position); - canon = new File(FileUtils.getCanonizePath(file)); - - // safe check : file exists - if (file.exists()) { - if (file.isDirectory()) { - if (onFolderClick(file)) { - fillFolderView(canon); - } - } else { - onFileClick(canon); - } - } - } - - /** - * @param folder - * the folder being clicked - * @return if the folder should be opened in the browsing list view - */ - protected abstract boolean onFolderClick(File folder); - - /** - * @param file - * the file being clicked (it is not a folder) - */ - protected abstract void onFileClick(File file); - - /** - * Folder view has been filled - */ - protected abstract void onFolderViewFilled(); - - /** - * Fills the files list with the specified folder - * - * @param file - * the file of the folder to display - */ - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - protected void fillFolderView(File file) { - file = new File(FileUtils.getCanonizePath(file)); - - if (!file.exists()) { - Crouton.makeText(this, R.string.toast_folder_doesnt_exist, - Style.ALERT).show(); - return; - } - - if (!file.isDirectory()) { - Crouton.makeText(this, R.string.toast_folder_not_folder, - Style.ALERT).show(); - return; - } - - if (!file.canRead()) { - Crouton.makeText(this, R.string.toast_folder_cant_read, Style.ALERT) - .show(); - return; - } - - listFiles(file); - - // create string list adapter - // mListAdapter = new FileListAdapter(this, mList, file); - mListAdapter.clear(); - mListAdapter.setCurrentFolder(file); - mListAdapter.addAll(mList); - mFilesList.scrollTo(0, 0); - - // update path - mCurrentFolder = file; - setTitle(file.getName()); - - onFolderViewFilled(); - } - - /** - * List the files in the given folder and store them in the list of files to - * display - * - * @param folder - * the folder to analyze - */ - protected void listFiles(File folder) { - File file; - - // get files list as array list - if ((folder == null) || (!folder.isDirectory())) { - mList = new ArrayList<>(); - return; - } - - mList = new ArrayList<>(Arrays.asList(folder.listFiles())); - - // filter files - for (int i = (mList.size() - 1); i >= 0; i--) { - file = mList.get(i); - - // remove - if (!(isFileVisible(file) && isFileTypeAllowed(file))) { - mList.remove(i); - } - } - - // Sort list - if (mComparator != null) { - Collections.sort(mList, mComparator); - } - - // Add parent folder - if (!folder.getPath().equals("/")) { - mList.add(0, folder.getParentFile()); - } - } - - protected boolean isFileVisible(File file) { - - boolean visible = true; - - // filter hidden files - if ((!mShowHiddenFiles) && (file.getName().startsWith("."))) { - visible = false; - } - - // filter non folders - if (mShowFoldersOnly && (!file.isDirectory())) { - visible = false; - } - - return visible; - } - - /** - * Filters files based on their extensions and white list / black list - * - * @param file - * the file to test - * @return if the file can be shown (either appear in white list or doesn't - * appear on blacklist) - */ - protected boolean isFileTypeAllowed(File file) { - boolean allow = true; - String ext; - - if (file.isFile()) { - ext = FileUtils.getFileExtension(file); - if ((mExtensionsWhiteList != null) - && (mExtensionsWhiteList.size() > 0) - && (!mExtensionsWhiteList.contains(ext))) { - allow = false; - } - - if ((mExtensionsBlackList != null) - && (mExtensionsBlackList.size() > 0) - && (mExtensionsBlackList.contains(ext))) { - allow = false; - } - } - - return allow; - } + OnItemClickListener { + + /** + * The list of files to display + */ + protected ArrayList mList; + /** + * the dialog's list view + */ + protected ListView mFilesList; + /** + * The list adapter + */ + protected FileListAdapter mListAdapter; + + /** + * the current folder + */ + protected File mCurrentFolder; + + /** + * the current file sort + */ + protected Comparator mComparator; + + protected boolean mShowFoldersOnly = false; + protected boolean mShowHiddenFiles = true; + protected boolean mHideNonWriteableFiles = false; + protected List mExtensionsWhiteList; + protected List mExtensionsBlackList; + + /** + * @see android.app.Activity#onCreate(android.os.Bundle) + */ + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mExtensionsWhiteList = new ArrayList<>(); + mExtensionsBlackList = new ArrayList<>(); + mComparator = new ComparatorFilesAlpha(); + mListAdapter = new FileListAdapter(this, new LinkedList<>(), null); + } + + /** + * @see android.app.Activity#onResume() + */ + protected void onResume() { + super.onResume(); + + // Setup the widget + mFilesList = findViewById(android.R.id.list); + mFilesList.setOnItemClickListener(this); + + // set adapter + mFilesList.setAdapter(mListAdapter); + + // initial folder + File folder; + if (mCurrentFolder != null) { + folder = mCurrentFolder; + } else if ((com.quseit.util.FileUtils.getPath(getApplicationContext()).exists()) + && (com.quseit.util.FileUtils.getPath(getApplicationContext()).canRead())) { + folder = com.quseit.util.FileUtils.getPath(getApplicationContext()); + } else { + folder = new File("/"); + } + + fillFolderView(folder); + } + + /** + * @see android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget.AdapterView, + * android.view.View, int, long) + */ + public void onItemClick(AdapterView parent, View view, int position, + long id) { + File file, canon; + + file = mList.get(position); + canon = new File(FileUtils.getCanonizePath(file)); + + // safe check : file exists + if (file.exists()) { + if (file.isDirectory()) { + if (onFolderClick(file)) { + fillFolderView(canon); + } + } else { + onFileClick(canon); + } + } + } + + /** + * @param folder the folder being clicked + * @return if the folder should be opened in the browsing list view + */ + protected abstract boolean onFolderClick(File folder); + + /** + * @param file the file being clicked (it is not a folder) + */ + protected abstract void onFileClick(File file); + + /** + * Folder view has been filled + */ + protected abstract void onFolderViewFilled(); + + /** + * Fills the files list with the specified folder + * + * @param file the file of the folder to display + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + protected void fillFolderView(File file) { + file = new File(FileUtils.getCanonizePath(file)); + + if (!file.exists()) { + Crouton.makeText(this, R.string.toast_folder_doesnt_exist, + Style.ALERT).show(); + return; + } + + if (!file.isDirectory()) { + Crouton.makeText(this, R.string.toast_folder_not_folder, + Style.ALERT).show(); + return; + } + + if (!file.canRead()) { + Crouton.makeText(this, R.string.toast_folder_cant_read, Style.ALERT) + .show(); + return; + } + + listFiles(file); + + // create string list adapter + // mListAdapter = new FileListAdapter(this, mList, file); + mListAdapter.clear(); + mListAdapter.setCurrentFolder(file); + mListAdapter.addAll(mList); + mFilesList.scrollTo(0, 0); + + // update path + mCurrentFolder = file; + setTitle(file.getName()); + + onFolderViewFilled(); + } + + /** + * List the files in the given folder and store them in the list of files to + * display + * + * @param folder the folder to analyze + */ + protected void listFiles(File folder) { + File file; + + // get files list as array list + if ((folder == null) || (!folder.isDirectory())) { + mList = new ArrayList<>(); + return; + } + + mList = new ArrayList<>(Arrays.asList(folder.listFiles())); + + // filter files + for (int i = (mList.size() - 1); i >= 0; i--) { + file = mList.get(i); + + // remove + if (!(isFileVisible(file) && isFileTypeAllowed(file))) { + mList.remove(i); + } + } + + // Sort list + if (mComparator != null) { + Collections.sort(mList, mComparator); + } + + // Add parent folder + if (!folder.getPath().equals("/")) { + mList.add(0, folder.getParentFile()); + } + } + + protected boolean isFileVisible(File file) { + + boolean visible = true; + + // filter hidden files + if ((!mShowHiddenFiles) && (file.getName().startsWith("."))) { + visible = false; + } + + // filter non folders + if (mShowFoldersOnly && (!file.isDirectory())) { + visible = false; + } + + return visible; + } + + /** + * Filters files based on their extensions and white list / black list + * + * @param file the file to test + * @return if the file can be shown (either appear in white list or doesn't + * appear on blacklist) + */ + protected boolean isFileTypeAllowed(File file) { + boolean allow = true; + String ext; + + if (file.isFile()) { + ext = FileUtils.getFileExtension(file); + if ((mExtensionsWhiteList != null) + && (mExtensionsWhiteList.size() > 0) + && (!mExtensionsWhiteList.contains(ext))) { + allow = false; + } + + if ((mExtensionsBlackList != null) + && (mExtensionsBlackList.size() > 0) + && (mExtensionsBlackList.contains(ext))) { + allow = false; + } + } + + return allow; + } } diff --git a/qpython/src/main/java/org/qpython/qpy/texteditor/androidlib/ui/adapter/FileListAdapter.java b/qpython/src/main/java/org/qpython/qpy/texteditor/androidlib/ui/adapter/FileListAdapter.java index 9c7de160..d7c44508 100644 --- a/qpython/src/main/java/org/qpython/qpy/texteditor/androidlib/ui/adapter/FileListAdapter.java +++ b/qpython/src/main/java/org/qpython/qpy/texteditor/androidlib/ui/adapter/FileListAdapter.java @@ -14,6 +14,7 @@ import android.widget.TextView; import org.qpython.qpy.R; +import org.qpython.qpy.main.app.App; import org.qpython.qpy.texteditor.androidlib.common.UIUtils; import org.qpython.qpy.texteditor.androidlib.data.FileUtils; @@ -100,7 +101,7 @@ public View getView(int position, View convertView, ViewGroup parent) { // } else { if (FileUtils.isSymLink(file)) { File target = FileUtils.getSymLinkTarget(file); - if (target.equals(FileUtils.STORAGE)) { + if (target.equals(com.quseit.util.FileUtils.getPath(App.getContext()))) { icon = R.drawable.prev; } else if (target.isDirectory()) { icon = R.drawable.ic_editor_folder_little; diff --git a/qpython/src/main/java/org/qpython/qpy/texteditor/common/Constants.java b/qpython/src/main/java/org/qpython/qpy/texteditor/common/Constants.java index 19581089..e2194b71 100644 --- a/qpython/src/main/java/org/qpython/qpy/texteditor/common/Constants.java +++ b/qpython/src/main/java/org/qpython/qpy/texteditor/common/Constants.java @@ -72,15 +72,14 @@ public interface Constants { */ int MENU_ID_QUIT = 666; - /** - * File of the external storage data - */ - File STORAGE = Environment - .getExternalStorageDirectory(); - /** - * Path to the external storage data - */ - String STORAGE_PATH = STORAGE.getAbsolutePath(); +// /** +// * File of the external storage data +// */ +// File STORAGE = Environment.getExternalStorageDirectory(); +// /** +// * Path to the external storage data +// */ +// String STORAGE_PATH = STORAGE.getAbsolutePath(); /** * name of the backup file */ diff --git a/qpython/src/main/java/org/qpython/qpy/utils/FileUtils.java b/qpython/src/main/java/org/qpython/qpy/utils/FileUtils.java index d5d1b718..5c455977 100644 --- a/qpython/src/main/java/org/qpython/qpy/utils/FileUtils.java +++ b/qpython/src/main/java/org/qpython/qpy/utils/FileUtils.java @@ -15,6 +15,11 @@ import java.io.OutputStream; public class FileUtils { + + public static String getAbsolutePath(Context context){ + return context.getExternalFilesDir(null).getPath() + "/qpython"; + } + public static boolean copyToFile(InputStream inputStream, File destFile) { try { if (destFile.exists()) { diff --git a/qpython/src/main/java/org/qpython/qpy/utils/NotebookUtil.java b/qpython/src/main/java/org/qpython/qpy/utils/NotebookUtil.java index 1717f9e5..97c74fa9 100644 --- a/qpython/src/main/java/org/qpython/qpy/utils/NotebookUtil.java +++ b/qpython/src/main/java/org/qpython/qpy/utils/NotebookUtil.java @@ -8,6 +8,7 @@ import com.loopj.android.http.AsyncHttpResponseHandler; import com.quseit.base.QBaseApp; +import com.quseit.util.FileUtils; import com.quseit.util.NAction; import com.quseit.util.NStorage; import com.quseit.util.NUtil; @@ -41,12 +42,12 @@ public class NotebookUtil { private static final String TAG = "NotebookUtil"; - public static final String RELEASE_PATH = QPyConstants.ABSOLUTE_PATH + "/.notebook"; + public static final String RELEASE_PATH = FileUtils.getAbsolutePath(App.getContext()) + "/.notebook"; public static final String NB_SERVER = "http://127.0.0.1:13000"; public static final String KILL_SERVER = NB_SERVER + "/__exit"; public static final String NOTEBOOK_SERVER = NB_SERVER + "/notebooks/"; - public static final String NOTEBOOK_DIR = QPyConstants.ABSOLUTE_PATH+"/"; + public static final String NOTEBOOK_DIR = FileUtils.getAbsolutePath(App.getContext())+"/"; public static final String ext = ".ipynb"; public static final String Untitled = "Untitled"; @@ -226,7 +227,7 @@ public void onResponse(Call call, Response response) throws IOException { private static String getTempFilePath(String url) { //LogUtil.d("NotebookUtil", "getTempFilePath:"+url); - File dir = new File(QPyConstants.ABSOLUTE_PATH, "notebooks"); + File dir = new File(FileUtils.getAbsolutePath(App.getContext()), "notebooks"); if (!dir.exists()) { dir.mkdirs(); } diff --git a/qpython/src/main/java/org/qpython/qpy/utils/ShortcutUtil.java b/qpython/src/main/java/org/qpython/qpy/utils/ShortcutUtil.java new file mode 100644 index 00000000..d8fbd50c --- /dev/null +++ b/qpython/src/main/java/org/qpython/qpy/utils/ShortcutUtil.java @@ -0,0 +1,55 @@ +package org.qpython.qpy.utils; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; +import android.os.Build; +import android.support.annotation.RequiresApi; + +import java.util.ArrayList; +import java.util.List; + +public class ShortcutUtil { + + public static List getAllTheLauncher(Context context) { + List names = new ArrayList<>(); + List packs = context.getPackageManager().getInstalledPackages(0); + for (int i = 0; i < packs.size(); i++) { + PackageInfo p = packs.get(i); + + + if (p.versionName == null) { + continue; + } + names.add(p.packageName); +// newInfo.appname = p.applicationInfo.loadLabel(context.getPackageManager()).toString(); +// newInfo.pname = p.packageName; +// newInfo.classname = p.applicationInfo.className; +// newInfo.versionCode = p.versionCode; +// newInfo.icon = p.applicationInfo.loadIcon(context.getPackageManager()); +// List names = null; +// PackageManager pkgMgt = context.getPackageManager(); +// Intent it = new Intent(Intent.ACTION_MAIN); +// it.addCategory(Intent.CATEGORY_LAUNCHER); +// List ra = pkgMgt.queryIntentActivities(it, 0); +// if (ra.size() != 0) { +// names = new ArrayList(); +// } +// for (int i = 0; i < ra.size(); i++) { +// String packageName = ra.get(i).activityInfo.packageName; +// names.add(packageName); +// } + } + return names; + } + + @RequiresApi(api = Build.VERSION_CODES.N_MR1) + public static List getShortcutInfo(Context context){ + ShortcutManager mShortcutManager = context.getSystemService(ShortcutManager.class); + return mShortcutManager.getPinnedShortcuts(); + } +} diff --git a/qpython/src/main/java/org/qpython/qpy/utils/UpdateHelper.java b/qpython/src/main/java/org/qpython/qpy/utils/UpdateHelper.java index 8b221d3b..b6a22862 100644 --- a/qpython/src/main/java/org/qpython/qpy/utils/UpdateHelper.java +++ b/qpython/src/main/java/org/qpython/qpy/utils/UpdateHelper.java @@ -24,6 +24,7 @@ import com.quseit.common.db.UserLog; import com.quseit.util.DateTimeHelper; import com.quseit.util.FileHelper; +import com.quseit.util.FileUtils; import com.quseit.util.NAction; import com.quseit.util.NUtil; import com.quseit.util.VeDate; @@ -169,7 +170,7 @@ public static void checkConfUpdate(Context context, String root) { checkConfUpdate(context.getApplicationContext()); // 清空图片目录的缓存 - String cacheDir = Environment.getExternalStorageDirectory() + "/" + root + "/" + BASE_CONF.DCACHE + "/"; + String cacheDir = FileUtils.getPath(context.getApplicationContext()) + "/" + root + "/" + BASE_CONF.DCACHE + "/"; FileHelper.clearDir(cacheDir, 0, false); } diff --git a/qpython/src/main/java/org/qpython/qpylib/MPyApi.java b/qpython/src/main/java/org/qpython/qpylib/MPyApi.java index c814676e..daa779fe 100644 --- a/qpython/src/main/java/org/qpython/qpylib/MPyApi.java +++ b/qpython/src/main/java/org/qpython/qpylib/MPyApi.java @@ -23,10 +23,12 @@ import org.qpython.qpy.console.ScriptExec; import com.quseit.util.FileHelper; +import com.quseit.util.FileUtils; import com.quseit.util.NAction; import com.quseit.util.NUtil; import org.qpython.qpy.main.activity.BaseActivity; +import org.qpython.qpy.main.app.App; import org.qpython.qpysdk.QPyConstants; import java.io.File; @@ -47,7 +49,7 @@ public void handleMessage(Message msg) { } }; private boolean live = false; - private String logF = QPyConstants.ABSOLUTE_LOG; + private String logF = FileUtils.getAbsoluteLogPath(App.getContext()); private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { @@ -139,7 +141,7 @@ private void process() { } else { runMode = 1; } - String script = QPyConstants.ABSOLUTE_PATH + "/cache/last.py"; + String script = FileUtils.getAbsolutePath(App.getContext()) + "/cache/last.py"; FileHelper.putFileContents(this, script, pycode); ScriptExec.getInstance().playScript(this,script, null, false); } @@ -226,7 +228,7 @@ protected void onAPIEnd() { } else { try { - String root = QBaseApp.getInstance().getOrCreateRoot(QPyConstants.DFROM_RUN); + String root = QBaseApp.getInstance().getOrCreateRoot(App.getContext(),QPyConstants.DFROM_RUN); File f = new File(root, ".last_tmp.py"); param = f.getAbsolutePath().toString(); } catch (Exception e) { @@ -242,7 +244,7 @@ protected void onAPIEnd() { rBundle.putString("result", result); rBundle.putString("param", param); rBundle.putString("flag", flag); - rBundle.putString("log", QPyConstants.ABSOLUTE_LOG); + rBundle.putString("log", FileUtils.getAbsoluteLogPath(App.getContext())); rIntent.putExtras(rBundle); diff --git a/qpython/src/main/java/org/qpython/qpylib/MPyService.java b/qpython/src/main/java/org/qpython/qpylib/MPyService.java index d0b6dc58..f64633ab 100644 --- a/qpython/src/main/java/org/qpython/qpylib/MPyService.java +++ b/qpython/src/main/java/org/qpython/qpylib/MPyService.java @@ -8,6 +8,7 @@ import android.support.annotation.Nullable; import com.quseit.util.FileHelper; +import com.quseit.util.FileUtils; import com.quseit.util.NAction; import com.quseit.util.NUtil; @@ -79,7 +80,7 @@ private void process(Intent intent) { } else { runMode = 1; } - String script = QPyConstants.ABSOLUTE_PATH + "/cache/last.py"; + String script = FileUtils.getAbsolutePath(App.getContext()) + "/cache/last.py"; FileHelper.putFileContents(this, script, pycode); ScriptExec.getInstance().playScript(MPyService.this, script, null, false); } diff --git a/qpython/src/main/res/drawable/agree_confirm_btn.xml b/qpython/src/main/res/drawable/agree_confirm_btn.xml new file mode 100644 index 00000000..c61413a4 --- /dev/null +++ b/qpython/src/main/res/drawable/agree_confirm_btn.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/qpython/src/main/res/drawable/top_white_r10.xml b/qpython/src/main/res/drawable/top_white_r10.xml new file mode 100644 index 00000000..01a09b4b --- /dev/null +++ b/qpython/src/main/res/drawable/top_white_r10.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/qpython/src/main/res/layout/activity_local_app.xml b/qpython/src/main/res/layout/activity_local_app.xml index ef51f7e5..754f8318 100644 --- a/qpython/src/main/res/layout/activity_local_app.xml +++ b/qpython/src/main/res/layout/activity_local_app.xml @@ -1,8 +1,8 @@ - - + + + + + + + + android:background="@null" /> \ No newline at end of file diff --git a/qpython/src/main/res/layout/activity_splash.xml b/qpython/src/main/res/layout/activity_splash.xml index ce6f6cd4..2ef88d58 100644 --- a/qpython/src/main/res/layout/activity_splash.xml +++ b/qpython/src/main/res/layout/activity_splash.xml @@ -1,16 +1,109 @@ - + - - - + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/qpython/src/main/res/values-ja/strings.xml b/qpython/src/main/res/values-ja/strings.xml index 18a6d9fb..4e3b1e22 100644 --- a/qpython/src/main/res/values-ja/strings.xml +++ b/qpython/src/main/res/values-ja/strings.xml @@ -486,4 +486,10 @@ 現在Python2を使用中です。Python3に切り替えますか? 設定に移動 ネットワークにラグが発生しています。しばらくお待ちください。 + You can run QPython script even it\'s in background if this mode is enabled. + Disable + QPython will restart to apply changes. + "Please grant the creat shortcut permission. " + 服务协议 + 隐私政策 diff --git a/qpython/src/main/res/values-ru/strings.xml b/qpython/src/main/res/values-ru/strings.xml index 33a26d74..cc23b367 100644 --- a/qpython/src/main/res/values-ru/strings.xml +++ b/qpython/src/main/res/values-ru/strings.xml @@ -430,4 +430,10 @@ w Подтвердить Я Необходимо получить разрешение для доступа к хранилищу. Не могу подключиться к серверу Google. Пожалуйста, проверьте ваше соединение. + You can run QPython script even it\'s in background if this mode is enabled. + Disable + QPython will restart to apply changes. + "Please grant the creat shortcut permission. " + 服务协议 + 隐私政策 \ No newline at end of file diff --git a/qpython/src/main/res/values-v11/strings.xml b/qpython/src/main/res/values-v11/strings.xml new file mode 100644 index 00000000..55344e51 --- /dev/null +++ b/qpython/src/main/res/values-v11/strings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/qpython/src/main/res/values-v21/strings.xml b/qpython/src/main/res/values-v21/strings.xml new file mode 100644 index 00000000..55344e51 --- /dev/null +++ b/qpython/src/main/res/values-v21/strings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/qpython/src/main/res/values-w820dp/strings.xml b/qpython/src/main/res/values-w820dp/strings.xml new file mode 100644 index 00000000..55344e51 --- /dev/null +++ b/qpython/src/main/res/values-w820dp/strings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/qpython/src/main/res/values-zh-rCN/strings.xml b/qpython/src/main/res/values-zh-rCN/strings.xml index aeff98b2..c3a06cfb 100644 --- a/qpython/src/main/res/values-zh-rCN/strings.xml +++ b/qpython/src/main/res/values-zh-rCN/strings.xml @@ -450,5 +450,11 @@ 运行 请先从QPYPI中安装kivy库 获得帮助 + You can run QPython script even it\'s in background if this mode is enabled. + Disable + QPython will restart to apply changes. + "Please grant the creat shortcut permission. " + 服务协议 + 隐私政策 diff --git a/qpython/src/main/res/values-zh-rTW/strings.xml b/qpython/src/main/res/values-zh-rTW/strings.xml index 6677a532..8049cb06 100644 --- a/qpython/src/main/res/values-zh-rTW/strings.xml +++ b/qpython/src/main/res/values-zh-rTW/strings.xml @@ -291,5 +291,11 @@ 衆籌 衆籌中 獲得幫助 + You can run QPython script even it\'s in background if this mode is enabled. + Disable + QPython will restart to apply changes. + "Please grant the creat shortcut permission. " + 服务协议 + 隐私政策 \ No newline at end of file diff --git a/qpython/src/main/res/values/colors.xml b/qpython/src/main/res/values/colors.xml index 2516e6f8..0ff89cbd 100644 --- a/qpython/src/main/res/values/colors.xml +++ b/qpython/src/main/res/values/colors.xml @@ -33,4 +33,5 @@ #ffffff #000000 + #498fdd diff --git a/qpython/src/main/res/values/strings.xml b/qpython/src/main/res/values/strings.xml index 1c38275b..97c44fd1 100644 --- a/qpython/src/main/res/values/strings.xml +++ b/qpython/src/main/res/values/strings.xml @@ -720,6 +720,7 @@ Project\'s main.py does not exit root + keep alive sl4a qpypi update_py3 @@ -734,5 +735,12 @@ Do you want to override? + You can run QPython script even it\'s in background if this mode is enabled. + Disable + Keep Alive Mode + QPython will restart to apply changes. + "Please grant the creat shortcut permission. " + 隐私政策 + 服务协议 diff --git a/qpython/src/main/res/xml/qpython_setting.xml b/qpython/src/main/res/xml/qpython_setting.xml index 245731f7..777fdbc4 100644 --- a/qpython/src/main/res/xml/qpython_setting.xml +++ b/qpython/src/main/res/xml/qpython_setting.xml @@ -109,6 +109,12 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qpython/src/od/java/org/qpython/qpy/codeshare/ShareCodeUtil.java b/qpython/src/od/java/org/qpython/qpy/codeshare/ShareCodeUtil.java new file mode 100644 index 00000000..2837ad82 --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/codeshare/ShareCodeUtil.java @@ -0,0 +1,749 @@ +package org.qpython.qpy.codeshare; + +import android.app.Activity; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.util.Log; +import android.widget.Toast; + +import com.google.common.reflect.TypeToken; +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.database.ValueEventListener; +import com.quseit.util.ACache; +import com.quseit.util.FileHelper; +import com.quseit.util.FileUtils; +import com.quseit.util.NetStateUtil; + +import org.json.JSONObject; +import org.qpython.qpy.R; +import org.qpython.qpy.codeshare.pojo.BookmarkerList; +import org.qpython.qpy.codeshare.pojo.CloudFile; +import org.qpython.qpy.codeshare.pojo.Gist; +import org.qpython.qpy.codeshare.pojo.GistBase; +import org.qpython.qpy.main.app.App; +import org.qpython.qpy.texteditor.common.TextFileUtils; +import org.qpython.qpysdk.utils.DateTimeHelper; + +import java.io.File; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import rx.Observable; +import rx.functions.Action1; + +import static org.qpython.qpy.codeshare.CONSTANT.DOT_REPLACE; +import static org.qpython.qpy.codeshare.CONSTANT.SLASH_REPLACE; +import static org.qpython.qpy.main.server.CacheKey.CLOUD_FILE; + +/** + * FireBase database util + * Created by Hmei on 2017-08-10. + */ + +public class ShareCodeUtil { + private static final boolean CLEAR = false; + + private static final String CLOUD = "cloud"; + private static final String GIST = "gist"; + private static final String USER = "user"; + private static final String BASE = "base"; + private static final String GIST_LIST = "gist_list"; + private static final String HISTORY = "history"; + private static final String COMMENT = "comment"; + private static final String BOOKMARK = "bookmark"; + private static final String COMMIT = "commit"; + private static final String INDEX = "index"; + private static final String USAGE = "usage"; + private static final String PROJECT = "project"; + private static final String SCRIPT = "script"; + private static final String OTHER = "other"; + private static final String PROJECT_PATH = "/projects/"; + private static final String SCRIPTS_PATH = "/scripts/"; + + private static final int MAX_FILE = 100; + + private DatabaseReference reference; + private String email; + private String userName; + private String avatarUrl; + + private SharedPreferences sharedPreferences; + private int currentFileCount = -1; + + private ShareCodeUtil() { + reference = FirebaseDatabase.getInstance().getReference(); + if (App.getUser() != null) { + email = App.getUser().getEmail().replace(".", "_"); + userName = App.getUser().getUserName(); + avatarUrl = App.getUser().getAvatarUrl(); + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(App.getContext()); + initUsage(null); + } else { + email = ""; + userName = ""; + avatarUrl = ""; + } + //LogUtil.d("ShareCodeUtil", "ShareCodeUtil:"+App.getUser().getEmail()); + + Log.d("ShareCodeUtil", "ShareCodeUtil:"+email); + } + + public static ShareCodeUtil getInstance() { + return ShareCodeHolder.INSTANCE; + } + + @SuppressWarnings({"DEBUG_ONLY"}) + public void clearAll() { + if (CLEAR) { + reference.child(CLOUD).child(email).removeValue( + (databaseError, databaseReference) -> + Toast.makeText(App.getContext(), "Clear", Toast.LENGTH_SHORT).show()); + } + } + + public void createScriptGist(String title, String desc, String msg, String content, DatabaseReference.CompletionListener listener) { + String date = DateTimeHelper.getDate(); + // Add to all gist repo + Gist gist = new Gist(); + gist.setTitle(title); + gist.setAuthor(userName); + gist.setAvatar(avatarUrl); + gist.setDescribe(desc); + gist.setDate(date); + DatabaseReference path = reference.child(GIST).child(SCRIPT).push();// gist/script/ + String gistId = path.getKey(); + path.setValue(gist, listener); + commitGist(gistId, msg, content, null, null); + + // Add to user repo + GistBase baseGist = new GistBase(); + baseGist.setAuthor(userName); + baseGist.setAvatar(avatarUrl); + baseGist.setDate(date); + baseGist.setTitle(title); + reference.child(USER).child(email).child(GIST_LIST).child(SCRIPT).child(gistId).setValue(baseGist);// user//gist_list/script/ + + // Add to base repo + reference.child(BASE).child(SCRIPT).child(gistId).setValue(baseGist); // base/script/ + } + + public void createProjectGist(String projectName, String desc, String msg, List paths, DatabaseReference.CompletionListener completionListener) { + for (String path : paths) { + File file = new File(path); + String code = TextFileUtils.readTextFile(file); + String fileName = file.getName().replace(".", DOT_REPLACE); + + String date = DateTimeHelper.getDate(); + // Add to all gist repo + Gist gist = new Gist(); + gist.setTitle(fileName); + gist.setAuthor(userName); + gist.setAvatar(avatarUrl); + gist.setDescribe(desc); + gist.setDate(date); + DatabaseReference subReference = reference.child(GIST).child(PROJECT);// gist/script/ + String gistId = subReference.getKey(); + subReference.child(gistId).child(projectName).child(fileName).setValue(gist, completionListener);// gist/project/// + commitGist(gistId, msg, code, projectName, fileName); + + // Add to user repo + GistBase baseGist = new GistBase(); + baseGist.setAuthor(userName); + baseGist.setAuthor(avatarUrl); + baseGist.setDate(date); + baseGist.setTitle(fileName); + // user//gist_list/project/// + reference.child(USER).child(email).child(GIST_LIST).child(PROJECT).child(gistId).child(projectName).child(fileName).setValue(baseGist); + + // Add to base repo + // base/project/// + reference.child(BASE).child(PROJECT).child(gistId).child(projectName).child(fileName).setValue(baseGist); + } + } + + public void commitGist(String gistId, String msg, String content, String projName, String fileName) { + Gist.HistoryBean historyBean = new Gist.HistoryBean(); + historyBean.setData(DateTimeHelper.getDate()); + historyBean.setMassage(msg); + historyBean.setContent(content); + if (projName != null) { + DatabaseReference subRe = reference.child(GIST).child(PROJECT).child(gistId).child(projName).child(fileName).child(HISTORY).push(); + String historyId = subRe.getKey(); + subRe.child(historyId).setValue(historyBean); // gist//history/} + } else { + DatabaseReference subRe = reference.child(GIST).child(SCRIPT).child(gistId).child(HISTORY).push(); + String historyId = subRe.getKey(); + subRe.child(historyId).setValue(historyBean); // gist//history/} + reference.child(GIST).child(SCRIPT).child(gistId).child("lastCommitCode").setValue(content); + } + } + + public void sendComment(String gistId, String comment, boolean isProj, CommentCallback callback) { + sendComment(gistId, "", comment, "", isProj, callback); + } + + /** + * @param comment 评论 + * @param reComment 被回复的评论 + */ + public void sendComment(String gistId, String to, String comment, String reComment, boolean isProj, CommentCallback callback) { + Gist.CommentBean commentBean = new Gist.CommentBean(); + commentBean.setFrom_content(comment); + commentBean.setData(DateTimeHelper.getDate()); + commentBean.setFrom(userName); + commentBean.setAvatar(avatarUrl); + commentBean.setRe(to); + commentBean.setRe_content(reComment); + callback.commentBean(commentBean); + DatabaseReference subRe = reference.child(GIST).child(isProj ? PROJECT : SCRIPT).child(gistId).child(COMMENT).push(); + String commentId = subRe.getKey(); + subRe.child(commentId).setValue(commentBean); + } + + public void bookmark(String gistId) { + reference.child(GIST).child(gistId).child(BOOKMARK).child(email).setValue(userName); + reference.child(USER).child(email).child(BOOKMARK).child(gistId).setValue(true); + } + + public void cancelBookmark(String gistId) { + reference.child(GIST).child(gistId).child(BOOKMARK).child(email).removeValue(); + reference.child(USER).child(email).child(BOOKMARK).child(gistId).removeValue(); + } + + public void getBaseScriptGistList(Action1> callback) { + reference.child(BASE).child(SCRIPT).addListenerForSingleValueEvent(new SimpleValueEventListener() { + @Override + public void onDataGet(HashMap value) { + handleKey(value,callback); + } + }); + } + + private void handleKey(HashMap value,Action1> callback){ + List dataList = new ArrayList<>(); + for (Object key : value.keySet()) { + GistBase gistBase = App.getGson().fromJson(new JSONObject((Map) value.get(key)).toString(), GistBase.class); + gistBase.setId((String) key); + dataList.add(gistBase); + } + Observable.just(dataList) + .subscribe(callback); + } + + public void getBaseProjectGistList(Action1> callback) { + reference.child(BASE).child(PROJECT).addListenerForSingleValueEvent(new SimpleValueEventListener() { + @Override + public void onDataGet(HashMap value) { +// List dataList = new ArrayList<>(); +// for (Object key : value.keySet()) { +// GistBase gistBase = App.getGson().fromJson(new JSONObject((Map) value.get(key)).toString(), GistBase.class); +// gistBase.setId((String) key); +// dataList.add(gistBase); +// } +// Observable.just(dataList) +// .subscribe(callback); + handleKey(value,callback); + } + }); + } + + public void getGistDetail(String gistId, boolean isProj, Action1 callback) { + reference.child(GIST).child(isProj ? PROJECT : SCRIPT).child(gistId).addListenerForSingleValueEvent(new SimpleValueEventListener() { + @Override + public void onDataGet(HashMap value) { + JSONObject jsonObject = new JSONObject(value); + Gist gist = App.getGson().fromJson(String.valueOf(jsonObject), Gist.class); + + HashMap historyMap = (HashMap) value.get(HISTORY); + if (historyMap != null) { + List historyList = new ArrayList<>(); + for (Object historyKey : historyMap.keySet()) { + Gist.HistoryBean historyBean = App.getGson().fromJson(String.valueOf(new JSONObject((Map) historyMap.get(historyKey))), Gist.HistoryBean.class); + historyBean.setHistoryId((String) historyKey); + historyList.add(historyBean); + } + gist.setHistory(historyList); + } + + HashMap commentMap = (HashMap) value.get(COMMENT); + if (commentMap != null) { + List commentList = new ArrayList<>(); + for (Object commentKey : commentMap.keySet()) { + Gist.CommentBean commentBean = App.getGson().fromJson(String.valueOf(new JSONObject((Map) commentMap.get(commentKey))), Gist.CommentBean.class); + commentBean.setId((String) commentKey); + commentList.add(commentBean); + } + gist.setComment(commentList); + } + + HashMap bookmarkMap = (HashMap) value.get(BOOKMARK); + if (bookmarkMap != null) { + List bookmarkerList = new ArrayList<>(); + for (Object bookmarkKey : bookmarkMap.keySet()) { + Gist.BookmarkerBean bookmarkerBean = App.getGson().fromJson(String.valueOf(new JSONObject((Map) bookmarkMap.get(bookmarkKey))), Gist.BookmarkerBean.class); + bookmarkerBean.setId((String) bookmarkKey); + bookmarkerList.add(bookmarkerBean); + } + gist.setBookmarker(bookmarkerList); + } + Observable.just(gist) + .subscribe(callback); + } + }); + } + + // TODO: 2017-08-14 Need to fix + public void getMyGistList(Action1> callback) { + reference.child(USER).child(GIST_LIST).addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + HashMap object = (HashMap) dataSnapshot.getValue(); + List userGist = new ArrayList<>(); + if (object == null) { + return; + } + for (Object key : object.keySet()) { + userGist.add(App.getGson().fromJson(new JSONObject((Map) object.get(key)).toString(), GistBase.class)); + } + Observable.just(userGist) + .subscribe(callback); + } + + @Override + public void onCancelled(DatabaseError databaseError) { + + } + }); + } + + public void getMyBookmarkList(Action1> callback) { + reference.child(USER).child(BOOKMARK).addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + HashMap object = (HashMap) dataSnapshot.getValue(); + List userGist = new ArrayList<>(); + if (object == null) { + return; + } + for (Object key : object.keySet()) { + userGist.add(App.getGson().fromJson(new JSONObject((Map) object.get(key)).toString(), BookmarkerList.class)); + } + Observable.just(userGist) + .subscribe(callback); + } + + @Override + public void onCancelled(DatabaseError databaseError) { + + } + }); + } + + public void getGistCommentList(String gistId, Action1> callback) { + reference.child(GIST).child(gistId).child(COMMENT).addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + HashMap object = (HashMap) dataSnapshot.getValue(); + List commentList = new ArrayList<>(); + if (object == null) { + return; + } + for (Object key : object.keySet()) { + commentList.add(App.getGson().fromJson(new JSONObject((Map) object.get(key)).toString(), Gist.CommentBean.class)); + } + Observable.just(commentList) + .subscribe(callback); + } + + @Override + public void onCancelled(DatabaseError databaseError) { + + } + }); + } + + public boolean uploadFolder(String path, DatabaseReference.CompletionListener completionListener, int[] size) { + File folder = new File(path); + // 获取该文件夹下符合后缀文件及非隐藏文件 + + if (!folder.isDirectory()) { + return false; + } + List files = FileHelper.filterExt(folder, App.getContext().getResources().getStringArray(R.array.support_file_ext), size[0]); + if (files.size() == 0) { + Toast.makeText(App.getContext(), R.string.no_file, Toast.LENGTH_SHORT).show(); + return false; + } + + if (!hasSpace(files.size())) { + return false; + } + + for (File file : files) { + uploadFile(file.getAbsolutePath(), completionListener); + } + +// // 如果在project目录下 +// if ("projects".equals(folder.getName())) { +// DatabaseReference projNode = reference.child(CLOUD).child(email).child(PROJECT).child(folder.getName().replace(".", DOT_REPLACE)); +// for (int i = 0; i < files.size(); i++) { +// String date = DateTimeHelper.AGO_FULL_DATE_FORMATTER.format(new Date(files.get(i).lastModified())); +// String subPath = files.get(i).getAbsolutePath(); +// String subKey = subPath.substring(subPath.indexOf(folder.getName()) + folder.getName().length(), subPath.length()).replace(".", DOT_REPLACE).replace("/", SLASH_REPLACE); +// if (i == files.size() - 1) { +// projNode.child(subKey) +// .child(date) +// .setValue(TextFileUtils.readTextFile(files.get(i)), completionListener); +// } else { +// projNode.child(subKey) +// .child(date) +// .setValue(TextFileUtils.readTextFile(files.get(i))); +// } +// reference.child(CLOUD) +// .child(email) +// .child(INDEX) +// .child(PROJECT) +// .child(folder.getName().replace(".", DOT_REPLACE) + subKey) +// .setValue(date); +// } +// } else if ("scripts".equals(folder.getName())) { +// // 上传整个scripts文件夹 +// for (int i = 0; i < files.size(); i++) { +// String abs_path = files.get(i).getAbsolutePath(); +// String key = abs_path +// .substring(abs_path.indexOf(SCRIPTS_PATH) + SCRIPTS_PATH.length()) +// .replace(".", DOT_REPLACE) +// .replace("/", SLASH_REPLACE); +// DatabaseReference content = reference.child(CLOUD) +// .child(email) +// .child(SCRIPT) +// .child(key) +// .child(DateTimeHelper.AGO_FULL_DATE_FORMATTER.format(new Date(files.get(i).lastModified()))); +// if (i == files.size() - 1) { +// content.setValue(TextFileUtils.readTextFile(files.get(i)), completionListener); +// } else { +// content.setValue(TextFileUtils.readTextFile(files.get(i))); +// } +// +// reference.child(CLOUD) +// .child(email) +// .child(INDEX) +// .child(SCRIPT) +// .child(key) +// .setValue(DateTimeHelper.AGO_FULL_DATE_FORMATTER.format(new Date(files.get(i).lastModified()))); +// } +// } else { +// // 普通文件夹 +// int fileListSize = files.size(); +// for (File file : files) { +// fileListSize--; +// final String rootNode = "/qpython/"; +// String node = file.getAbsolutePath().substring(path.indexOf(rootNode) + rootNode.length()); +// DatabaseReference child = +// reference +// .child(CLOUD) +// .child(email)/*.child(folder.getName().replace(".", DOT_REPLACE))*/ +// .child(OTHER) +// .child(node.replace(".", DOT_REPLACE).replace("/", SLASH_REPLACE)) +// .child(DateTimeHelper.getDate()); +// if (fileListSize == 0) { +// child.setValue(TextFileUtils.readTextFile(file), completionListener); +// } else { +// child.setValue(TextFileUtils.readTextFile(file)); +// } +// +// reference.child(CLOUD) +// .child(email) +// .child(INDEX) +// .child(OTHER) +// .child(node.replace(".", DOT_REPLACE).replace("/", SLASH_REPLACE)) +// .setValue(DateTimeHelper.getDate()); +// } +// } + return true; + } + + public boolean uploadFile(String path, DatabaseReference.CompletionListener completionListener) { + File file = new File(path); + + if (!hasSpace(1)) { + return false; + } + + if (path.contains(PROJECT_PATH)) { + String subPath = path.substring(path.indexOf(PROJECT_PATH) + PROJECT_PATH.length()); + String projName = subPath.split("/")[0]; + reference.child(CLOUD) + .child(email) + .child(PROJECT) + .child(projName) + .child(subPath.replace(".", DOT_REPLACE).replace("/", SLASH_REPLACE).replace(projName, "")) + .child(DateTimeHelper.AGO_FULL_DATE_FORMATTER.format(new Date(file.lastModified()))) + .setValue(TextFileUtils.readTextFile(file), completionListener); + + reference.child(CLOUD) + .child(email) + .child(INDEX) + .child(PROJECT) + .child(subPath.replace(".", DOT_REPLACE).replace("/", SLASH_REPLACE)) + .setValue(DateTimeHelper.AGO_FULL_DATE_FORMATTER.format(new Date(file.lastModified()))); + } else if (path.contains(SCRIPTS_PATH)) { + reference.child(CLOUD) + .child(email) + .child(SCRIPT) + .child(file.getName().replace(".", DOT_REPLACE)) + .child(DateTimeHelper.AGO_FULL_DATE_FORMATTER.format(new Date(file.lastModified()))) + .setValue(TextFileUtils.readTextFile(file), completionListener); + + reference.child(CLOUD) + .child(email) + .child(INDEX) + .child(SCRIPT) + .child(file.getName().replace(".", DOT_REPLACE)) + .setValue(DateTimeHelper.AGO_FULL_DATE_FORMATTER.format(new Date(file.lastModified()))); + } else { + final String rootNode = "/qpython/"; + String uploadKey = path.substring(path.indexOf(rootNode) + rootNode.length()); + reference.child(CLOUD) + .child(email) + .child(OTHER) + .child(uploadKey.replace(".", DOT_REPLACE).replace("/", SLASH_REPLACE)) + .child(DateTimeHelper.getDate()) + .setValue(TextFileUtils.readTextFile(file), completionListener); + + reference.child(CLOUD) + .child(email) + .child(INDEX) + .child(OTHER) + .child(uploadKey.replace(".", DOT_REPLACE).replace("/", SLASH_REPLACE)) + .setValue(DateTimeHelper.getDate()); + } + return true; + } + + public void getUploadedScripts(boolean forceRefresh, Activity context, Action1> callback) { + if (CLEAR) { + return; + } + String content = FileHelper.getFileContents(FileUtils.getCloudMapCachePath(context.getApplicationContext())); + List cloudFiles = content == null ? null : App.getGson().fromJson(content, new TypeToken>() { + }.getType()); + if (cloudFiles != null && !forceRefresh) { + Observable.just(cloudFiles) + .subscribe(callback); + } else { + email = App.getUser()!=null?App.getUser().getEmail().replace(".","_"):""; + if (email.equals("")) { + Toast.makeText(context, "Waiting the firebase to initializ...",Toast.LENGTH_SHORT).show(); + + } else { + reference.child(CLOUD).child(email).child(INDEX).addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + HashMap value = (HashMap) dataSnapshot.getValue(); + if (value == null) { + resetUsage(0); + Observable.just(new ArrayList()) + .subscribe(callback); + return; + } + List cloudFiles = new ArrayList<>(); + for (String node : value.keySet()) { + switch (node) { + case SCRIPT: + HashMap script; + script = (HashMap) value.get(SCRIPT); + for (String o : script.keySet()) { + CloudFile cloudFile = new CloudFile(); + cloudFile.setName(o); + cloudFile.setKey(SCRIPT + "/" + o); + cloudFile.setPath(SCRIPTS_PATH); + cloudFile.setUploadTime((String) script.get(o)); + cloudFiles.add(cloudFile); + } + break; + case PROJECT: + HashMap project = (HashMap) value.get(PROJECT); + for (String s : project.keySet()) { + CloudFile cloudFile = new CloudFile(); + cloudFile.setKey(PROJECT + "/" + s); + String[] nodes = s.split(SLASH_REPLACE); + cloudFile.setName(nodes[nodes.length - 1]); + cloudFile.setProjectName(nodes[0]); + cloudFile.setPath(s.replace(nodes[0], "")); + cloudFile.setUploadTime((String) project.get(s)); + cloudFiles.add(cloudFile); + } + break; + default: + HashMap other = (HashMap) value.get(OTHER); + for (String s : other.keySet()) { + CloudFile cloudFile = new CloudFile(); + cloudFile.setPath(s); + cloudFile.setKey(OTHER + "/" + s); + String[] nodes = s.split(SLASH_REPLACE); + boolean index = false; + for (String node1 : nodes) { + if (index) { + cloudFile.setProjectName(node1); + break; + } else if (node1.equals("projects3")) { + index = true; + } + } + cloudFile.setName(nodes[nodes.length - 1]); + cloudFile.setUploadTime((String) other.get(s)); + cloudFiles.add(cloudFile); + } + break; + } + } + resetUsage(cloudFiles.size()); + Observable.just(cloudFiles) + .subscribe(callback); + ACache.get(context).put(CLOUD_FILE, App.getGson().toJson(cloudFiles)); + + } + + @Override + public void onCancelled(DatabaseError databaseError) { + + } + }); + } + } + } + + public void getFileContent(String path, Action1 callback) { + reference.child(CLOUD) + .child(email) + .child(path) + .addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + HashMap dateContent = (HashMap) dataSnapshot.getValue(); + String latest = ""; + for (String s : dateContent.keySet()) { + latest = DateTimeHelper.isLater(s, latest) ? s : latest; + } + + Observable.just(dateContent.get(latest)) + .subscribe(callback); + } + + @Override + public void onCancelled(DatabaseError databaseError) { + + } + }); + } + + public void deleteUploadScript(CloudFile cloudFile, DatabaseReference.CompletionListener listener) { + reference.child(CLOUD) + .child(email) + .child(cloudFile.getKey()) + .removeValue(listener); + reference.child(CLOUD) + .child(email) + .child(INDEX) + .child(cloudFile.getKey()) + .removeValue(); + changeUsage(-1); + } + + public void initUsage(Action1 callback) { + if (CLEAR) { + return; + } + if (currentFileCount == -1) { + if (NetStateUtil.isConnected(App.getContext())) { + reference.child(CLOUD) + .child(email) + .child(USAGE) + .addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + if (dataSnapshot.getValue() != null) { + currentFileCount = ((Long) dataSnapshot.getValue()).intValue(); + } else { + currentFileCount = 0; + } + if (callback != null) { + Observable.just(currentFileCount) + .subscribe(callback); + } + } + + @Override + public void onCancelled(DatabaseError databaseError) { + + } + }); + } + } else { + callback.call(currentFileCount); + } + } + + public int changeUsage(int change) { + currentFileCount += change; + reference.child(CLOUD) + .child(email) + .child(USAGE) + .setValue(currentFileCount); + return currentFileCount; + } + + public void resetUsage(int value) { + currentFileCount = value; + reference.child(CLOUD) + .child(email) + .child(USAGE) + .setValue(currentFileCount); + } + + private boolean hasSpace(int waiting) { + if (currentFileCount < 0) { + Toast.makeText(App.getContext(), R.string.usage_not_init, Toast.LENGTH_SHORT).show(); + return false; + } else if (currentFileCount + waiting > MAX_FILE) { + Toast.makeText(App.getContext(), R.string.no_space, Toast.LENGTH_SHORT).show(); + return false; + } else { + return true; + } + } + + public interface CommentCallback { + void commentBean(Gist.CommentBean comment); + } + + private static class ShareCodeHolder { + private static final ShareCodeUtil INSTANCE = new ShareCodeUtil(); + } + + abstract class SimpleValueEventListener implements ValueEventListener { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + HashMap object = (HashMap) dataSnapshot.getValue(); + if (object == null) { + return; + } + onDataGet(object); + } + + @Override + public void onCancelled(DatabaseError databaseError) { + + } + + public abstract void onDataGet(HashMap value); + } +} \ No newline at end of file diff --git a/qpython/src/od/java/org/qpython/qpy/codeshare/pojo/CloudFile.java b/qpython/src/od/java/org/qpython/qpy/codeshare/pojo/CloudFile.java new file mode 100644 index 00000000..1231486b --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/codeshare/pojo/CloudFile.java @@ -0,0 +1,100 @@ +package org.qpython.qpy.codeshare.pojo; + + +import com.quseit.util.FileUtils; + +import org.qpython.qpy.codeshare.CONSTANT; +import org.qpython.qpy.main.app.App; +import org.qpython.qpysdk.QPyConstants; + +import java.io.Serializable; + +public class CloudFile implements Serializable { + private String projectName; + private String path; + private String uploadTime; + private String name; + private String content; + private String key; + private boolean uploading; + + public String getPath() { + if (projectName == null) { + if (path.contains("scripts3")) { + return "/" + path.replace(CONSTANT.SLASH_REPLACE, "/").replace(CONSTANT.DOT_REPLACE, "."); + } else if (path.contains("scripts")) { + return path.replace(CONSTANT.SLASH_REPLACE, "/") + getName(); + } else { + return "/" + path.replace(CONSTANT.SLASH_REPLACE, "/").replace(CONSTANT.DOT_REPLACE, "."); + } + } else { + String projNode = path.contains("projects3") ? "projects3/" : "projects/"; + return "/" + projNode + getProjectName() + path + .replace(CONSTANT.SLASH_REPLACE, "/") + .replace(CONSTANT.DOT_REPLACE, ".") + .replace(projNode, "") + .replace(getProjectName(), ""); + } + } + + public void setPath(String path) { + this.path = path; + } + + public String getUploadTime() { + return uploadTime; + } + + public void setUploadTime(String uploadTime) { + this.uploadTime = uploadTime; + } + + public String getName() { + return name.replace(CONSTANT.DOT_REPLACE, "."); + } + + public void setName(String name) { + this.name = name; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public long getContentSize() { + return content == null ? 0 : content.length(); + } + + public String getProjectName() { + return projectName == null ? null : projectName. + replace(CONSTANT.SLASH_REPLACE, "/").replace(CONSTANT.DOT_REPLACE, "."); + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public boolean isUploading() { + return uploading; + } + + public void setUploading(boolean uploading) { + this.uploading = uploading; + } + + public String getKey() { + return key == null ? getName().replace(".", CONSTANT.DOT_REPLACE) : key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getAbsolutePath() { + return FileUtils.getAbsolutePath(App.getContext()) + getPath(); + } +} diff --git a/qpython/src/od/java/org/qpython/qpy/main/PayActivity.java b/qpython/src/od/java/org/qpython/qpy/main/PayActivity.java new file mode 100644 index 00000000..c1a7b8d2 --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/main/PayActivity.java @@ -0,0 +1,188 @@ +package org.qpython.qpy.main; + +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.GridLayoutManager; +import android.view.MenuItem; +import android.view.View; +import android.widget.Toast; + +import com.android.vending.billing.IInAppBillingService; +import com.quseit.util.ImageUtil; + +import org.json.JSONException; +import org.json.JSONObject; +import org.qpython.qpy.R; +import org.qpython.qpy.main.activity.PurchaseActivity; +import org.qpython.qpy.main.server.MySubscriber; +import org.qpython.qpy.main.widget.GridSpace; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; + +/** + * Created by Hmei + * 1/30/18. + */ + +public class PayActivity extends AppCompatActivity { + public static final int BUY_REQUEST_CODE = 2333; + private IInAppBillingService mService; + private ServiceConnection mServiceConn; + private ArrayList skuList; + private MySubscriber callback; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + void initIAB(ArrayList skuList, MySubscriber callback) { + this.skuList = skuList; + this.callback = callback; + mServiceConn = new ServiceConnection() { + @Override + public void onServiceDisconnected(ComponentName name) { + mService = null; + } + + @Override + public void onServiceConnected(ComponentName name, + IBinder service) { + mService = IInAppBillingService.Stub.asInterface(service); + getPrices(skuList, callback); + } + }; + + Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND"); + serviceIntent.setPackage("com.android.vending"); + bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE); + } + + /** + * 从Google服务器获取不同国家价格表并显示 + */ + private void getPrices(ArrayList skuList, MySubscriber callback) { + if (mService == null) { + Toast.makeText(this, R.string.lose_google_server, Toast.LENGTH_SHORT).show(); + if (findViewById(R.id.pb) != null) findViewById(R.id.pb).setVisibility(View.GONE); + return; + } +// ArrayList skuList; +// if (!isCrowdFunding) { +// skuList = new ArrayList<>(Arrays.asList(getResources().getStringArray(R.array.sku))); +// } else { +// skuList = new ArrayList<>(Arrays.asList(getResources().getStringArray(R.array.crowdfunding))); +// int index = (int) (getIntent().getIntExtra(PERCENT, 0) / 100.00 * 4); +// String sku = skuList.get(index); +// skuList.clear(); +// skuList.add(sku); +// } + Bundle querySkus = new Bundle(); + querySkus.putStringArrayList("ITEM_ID_LIST", skuList); + try { + Observable.just(mService.getSkuDetails(3, getPackageName(), "inapp", querySkus)) + .map(bundle -> { + ArrayList responseList = bundle.getStringArrayList("DETAILS_LIST"); + if (responseList == null) { + return null; + } + String[] prices = new String[responseList.size()]; + for (int i = 0; i < responseList.size(); i++) { + JSONObject object; + try { + object = new JSONObject(responseList.get(i)); + String price = object.getString("price"); + prices[i] = price; + } catch (JSONException e) { + e.printStackTrace(); + } + } + Arrays.sort(prices, (o1, o2) -> Integer.parseInt(o1.replaceAll("[^0-9]", "")) - Integer.parseInt(o2.replaceAll("[^0-9]", ""))); + return prices; + } + ) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(callback +// new MySubscriber() { +// @Override +// public void onNext(String[] o) { +// super.onNext(o); +// if (o == null) { +// return; +// } +// invalidateData(); +// GridSpace gridSpace = new GridSpace(2, (int) ImageUtil.dp2px(16), false); +// binding.list.setLayoutManager(new GridLayoutManager(PurchaseActivity.this, 2)); +// binding.list.addItemDecoration(gridSpace); +// binding.list.setAdapter(new PurchaseActivity.ListAdapter(o)); +// } +// } + ); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + private void purchase(String sku) { +// String[] skus; +// if (isCrowdFunding) { +// skus = getResources().getStringArray(R.array.crowdfunding); +// } else { +// skus = getResources().getStringArray(R.array.sku); +// } + try { + if (mService == null) { + Toast.makeText(this, R.string.lose_google_server, Toast.LENGTH_SHORT).show(); + return; + } + Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(), + sku, "inapp", "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ"); + switch (buyIntentBundle.getInt("RESPONSE_CODE")) { + case 0: + //BILLING_RESPONSE_RESULT_OK + PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT"); + startIntentSenderForResult(pendingIntent.getIntentSender(), + BUY_REQUEST_CODE, new Intent(), 0, 0, 0); + break; + + } + } catch (RemoteException | IntentSender.SendIntentException | NullPointerException e) { + e.printStackTrace(); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.refresh_menu) { + if (findViewById(R.id.pb) != null) findViewById(R.id.pb).setVisibility(View.VISIBLE); + getPrices(skuList, callback); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (mService != null) { + unbindService(mServiceConn); + } + } + +} diff --git a/qpython/src/od/java/org/qpython/qpy/main/activity/BaseActivity.java b/qpython/src/od/java/org/qpython/qpy/main/activity/BaseActivity.java new file mode 100644 index 00000000..20c1e0e5 --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/main/activity/BaseActivity.java @@ -0,0 +1,302 @@ +package org.qpython.qpy.main.activity; + +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.support.v4.util.ArrayMap; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.widget.ArrayAdapter; +import android.widget.Toast; +import android.text.TextUtils; + +import com.quseit.util.FileUtils; +import com.quseit.util.NAction; +import com.quseit.util.NUtil; + +import org.qpython.qpy.R; +import org.qpython.qpy.console.ShellTermSession; +import org.qpython.qpy.console.util.TermSettings; +import org.renpy.android.ResourceManager; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.qpython.qpy.main.server.gist.TokenManager; + +public class BaseActivity extends AppCompatActivity { + // QPython interfaces + private static final int SCRIPT_CONSOLE_CODE = 1237; + private static final int PID_INIT_VALUE = -1; + private static final int DEFAULT_BUFFER_SIZE = 8192; + private static final int LOG_NOTIFICATION_ID = (int) System.currentTimeMillis(); + + + //private FirebaseAnalytics mFirebaseAnalytics; + private ArrayList mArguments = new ArrayList<>(); + private InputStream mIn; + private OutputStream mOut; + private Map mActionMap = new ArrayMap<>(); + private TermSettings mSettings; + private ShellTermSession session; + + private boolean permissionGrant = true; + + protected static ShellTermSession createTermSession(Context context, TermSettings settings, String initialCommand, String path) { + ShellTermSession session = null; + try { + session = new ShellTermSession(context, settings, initialCommand, path); + session.setProcessExitMessage(context.getString(R.string.process_exit_message)); + + } catch (IOException e) { + e.printStackTrace(); + } + + return session; + } + + protected void toast(String content) { + Toast.makeText(this, content, Toast.LENGTH_SHORT).show(); + } + + @Override + protected void onResume() { + super.onResume(); + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + //mFirebaseAnalytics = FirebaseAnalytics.getInstance(this); + } + + @Override + protected void onPause() { + super.onPause(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + PermissionAction action = mActionMap.get(requestCode); + if (action != null) { + if (grantResults.length > 0 && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) { + action.onGrant(); + } else { + action.onDeny(); + } + + } + mActionMap.remove(action); + } + + public final void checkPermissionDo(String[] permissions, PermissionAction action) { + if (Build.VERSION.SDK_INT >= 23) { + boolean granted = true; + for (String permission : permissions) { + int checkPermission = ContextCompat.checkSelfPermission(this, permission); + granted = checkPermission == PackageManager.PERMISSION_GRANTED; + } + if (!granted) { + int code = permissions.hashCode() & 0xffff; + mActionMap.put(code, action); + ActivityCompat.requestPermissions(this, permissions, code); + } else { + action.onGrant(); + } + } else { + action.onGrant(); + } + } + + + // feedback + public void onFeedback(String feedback) { + String app = getString(R.string.app_name); + int ver = NUtil.getVersinoCode(getApplicationContext()); + String subject = MessageFormat.format(getString(com.quseit.android.R.string.feeback_email_title), app, ver, Build.PRODUCT); + + String lastError = ""; + String code = NAction.getCode(getApplicationContext()); + File log = new File(FileUtils.getPath(getApplicationContext()) + "/" + code + "_last_err.log"); + if (log.exists()) { + lastError = com.quseit.util.FileHelper.getFileContents(log.getAbsolutePath()); + } + + String body = feedback.isEmpty() ? feedback : MessageFormat.format(getString(R.string.feedback_email_body), Build.PRODUCT, + Build.VERSION.RELEASE, Build.VERSION.SDK, lastError, feedback); + + + Intent twitterIntent = getPackageManager().getLaunchIntentForPackage("com.twitter.android"); + if (twitterIntent != null) { + List list = new ArrayList<>(); + list.add("Feedback with Twitter"); + list.add("Feedback with Email"); + new AlertDialog.Builder(this) + .setTitle(R.string.feedback) + .setNegativeButton(R.string.close, (dialog, which) -> dialog.dismiss()) + .setAdapter(new ArrayAdapter<>(this, R.layout.dialog_feedback, list), (dialog, which) -> { + if (which == 0) { + twitter(body); + } else { + email(subject, body); + } + }) + .show(); + } else { + email(subject, body); + } + } + + private void email(String subject, String body) { + Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:" + getString(R.string.ui_feedback_mail))); + + intent.putExtra(Intent.EXTRA_SUBJECT, subject); + intent.putExtra(Intent.EXTRA_TEXT, session == null ? body : session.getTranscriptText().trim()); + try { + startActivity(Intent.createChooser(intent, + getString(R.string.email_transcript_chooser_title))); + } catch (ActivityNotFoundException e) { + Toast.makeText(this, + R.string.email_transcript_no_email_activity_found, + Toast.LENGTH_LONG) + .show(); + } + } + + private void twitter(String message) { + Intent tweetIntent = new Intent(Intent.ACTION_SEND); + if (message.isEmpty()){ + tweetIntent.putExtra(Intent.EXTRA_TEXT, "@qpython,"); + }else { + tweetIntent.putExtra(Intent.EXTRA_TEXT, "@qpython\n" + message); + } + tweetIntent.setType("text/plain"); + + PackageManager packManager = getPackageManager(); + List resolvedInfoList = packManager.queryIntentActivities(tweetIntent, PackageManager.MATCH_DEFAULT_ONLY); + + boolean resolved = false; + for (ResolveInfo resolveInfo : resolvedInfoList) { + if (resolveInfo.activityInfo.packageName.startsWith("com.twitter.android")) { + tweetIntent.setClassName( + resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name); + resolved = true; + break; + } + } + if (resolved) { + startActivity(tweetIntent); + } else { + Intent i = new Intent(); + i.putExtra(Intent.EXTRA_TEXT, message); + i.setAction(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://twitter.com/intent/tweet?text=" + urlEncode(message))); + startActivity(i); + Toast.makeText(this, "Twitter app isn't found", Toast.LENGTH_LONG).show(); + } + } + + private String urlEncode(String s) { + try { + return URLEncoder.encode(s, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + return ""; + } + } + + protected boolean checkExpired(final String resource, String filesDir, String tag) { + ResourceManager resourceManager = new ResourceManager(this); + + String data_version = resourceManager.getString(resource + "_version"); + String disk_version = "0"; + + // If no version, no unpacking is necessary. + if (data_version == null) { + return false; + } + + // Check the current disk version, if any. + String disk_version_fn = filesDir + "/" + tag + "_" + resource + ".version"; + + try { + byte buf[] = new byte[64]; + InputStream is = new FileInputStream(disk_version_fn); + int len = is.read(buf); + disk_version = new String(buf, 0, len); + is.close(); + } catch (Exception e) { + + disk_version = "0"; + + } + + if (!NUtil.isNumeric(disk_version)) { + disk_version = "0"; + + } + + if ((int) (Double.parseDouble(data_version) - Double.parseDouble(disk_version)) > 0 || disk_version.equals("0")) { + try { + FileOutputStream os = new FileOutputStream(disk_version_fn); + try { + os.write(data_version.getBytes()); + os.close(); + + } catch (IOException e) { + e.printStackTrace(); + //Mint.logException(e); + + } + + } catch (FileNotFoundException e) { + e.printStackTrace(); + //Mint.logException(e); + + } + return true; + } else { + return false; + } + } + + public interface PermissionAction { + void onGrant(); + + void onDeny(); + } + + public void ifLogin(Login afterLogin) { + if (!TextUtils.isEmpty(TokenManager.getToken())) { + afterLogin.process(); + } else { + Toast.makeText(this, R.string.login_first, Toast.LENGTH_SHORT).show(); + } + } + public interface Login { + void process(); + } +} \ No newline at end of file diff --git a/qpython/src/od/java/org/qpython/qpy/main/activity/FundingPurchaseActivity.java b/qpython/src/od/java/org/qpython/qpy/main/activity/FundingPurchaseActivity.java new file mode 100644 index 00000000..6e6cf10a --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/main/activity/FundingPurchaseActivity.java @@ -0,0 +1,143 @@ +package org.qpython.qpy.main.activity; + +import android.content.Context; +import android.content.Intent; +import android.databinding.DataBindingUtil; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.Menu; +import android.view.View; +import android.widget.Toast; + +import com.quseit.util.VeDate; + +import org.json.JSONException; +import org.json.JSONObject; +import org.qpython.qpy.R; +import org.qpython.qpy.databinding.ActivityFundingPurchaseBinding; +import org.qpython.qpy.main.app.App; +import org.qpython.qpy.main.server.MySubscriber; +import org.qpython.qpy.main.server.model.GooglePurchaseModel; +import org.qpython.qpy.utils.UpdateHelper; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Created by Hmei + * 1/30/18. + */ + +public class FundingPurchaseActivity extends PayActivity { + private static final String ARTICLE_ID = "article_id"; + private static final String FUNDING_COUNT = "fundingCount"; + // private final String[] prices = get + private ActivityFundingPurchaseBinding binding; + private String sku; + + public static void startSupport(Context context, String articleId, int fundingPercent) { + Intent starter = new Intent(context, FundingPurchaseActivity.class); + starter.putExtra(ARTICLE_ID, articleId); + starter.putExtra(FUNDING_COUNT, fundingPercent); + context.startActivity(starter); + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = DataBindingUtil.setContentView(this, R.layout.activity_funding_purchase); + binding.pb.setVisibility(View.VISIBLE); + initView(); + initPrice(); + initListener(); + } + + private void initView() { + setSupportActionBar(binding.lt.toolbar); + binding.lt.toolbar.setNavigationIcon(R.drawable.ic_back); + binding.lt.toolbar.setNavigationOnClickListener(v -> finish()); + setTitle(R.string.reward); + } + + private void initPrice() { + String[] skus = getResources().getStringArray(R.array.crowdfunding); + int fundingCount = getIntent().getIntExtra(FUNDING_COUNT, 0); + if (fundingCount < 100) { + sku = skus[0]; + } else if (fundingCount < 500) { + sku = skus[1]; + } else if (fundingCount < 2000) { + sku = skus[2]; + } else { + sku = skus[3]; + } + ArrayList skuList = new ArrayList<>(); + skuList.add(sku); + initIAB(skuList, new MySubscriber() { + @Override + public void onNext(String[] o) { + super.onNext(o); + if (o == null) { + return; + } + invalidateData(true); + binding.price.setText(o[0]); + } + }); + } + + private void initListener() { + binding.price.setOnClickListener(v -> payUtil.purchase(sku)); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.refresh_menu, menu); + return true; + } + + private void invalidateData(boolean showData) { + binding.price.setVisibility(showData ? View.VISIBLE : View.GONE); + binding.tvThanks.setVisibility(showData ? View.VISIBLE : View.GONE); + binding.noData.llRoot.setVisibility(showData ? View.GONE : View.VISIBLE); + binding.pb.setVisibility(View.GONE); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == BUY_REQUEST_CODE) { + if (resultCode == RESULT_OK) { + GooglePurchaseModel model = App.getGson().fromJson(data.getStringExtra("INAPP_PURCHASE_DATA"), GooglePurchaseModel.class); + if (model == null) { + return; + } + switch (model.getProductId()) { + default: + Toast.makeText(this, R.string.thanks_your_support, Toast.LENGTH_SHORT).show(); + break; + } + // 统计赞赏数据 + JSONObject jsonObject = new JSONObject(); + try { + jsonObject.put("type", model.getProductId()); + jsonObject.put("time", VeDate.getStringDateHourAsInt()); + int percent = getIntent().getIntExtra(FUNDING_COUNT, 0); + String[] fundingCount = getResources().getStringArray(R.array.funding_count_divider); + jsonObject.put("crowdfunding", + percent > (Integer.parseInt(fundingCount[1]) / Integer.parseInt(fundingCount[2])) ? 3 : + percent > (Integer.parseInt(fundingCount[0]) / Integer.parseInt(fundingCount[1])) ? 2 : 1);//0 非众筹/ 1: 0-100 /2: 100-500/3 500-2000 + jsonObject.put("articleId", getIntent().getStringExtra(ARTICLE_ID)); + if (App.getUser() != null) { + jsonObject.put("account", App.getUser().getEmail()); + } + } catch (JSONException e) { + e.printStackTrace(); + } + UpdateHelper.submitIAPLog(this, model.getOrderId(), App.getGson().toJson(jsonObject)); + payUtil.digestPurchase(model.getPurchaseToken()); + finish(); + } + } + } +} diff --git a/qpython/src/od/java/org/qpython/qpy/main/activity/PayActivity.java b/qpython/src/od/java/org/qpython/qpy/main/activity/PayActivity.java new file mode 100644 index 00000000..c637254e --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/main/activity/PayActivity.java @@ -0,0 +1,54 @@ +package org.qpython.qpy.main.activity; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.view.MenuItem; +import android.view.View; + +import org.qpython.qpy.R; +import org.qpython.qpy.main.server.MySubscriber; +import org.qpython.qpy.main.service.PayUtil; + +import java.util.ArrayList; + +/** + * Created by Hmei + * 1/30/18. + */ + +public class PayActivity extends AppCompatActivity { + public static final int BUY_REQUEST_CODE = 2333; + private ArrayList skuList; + private MySubscriber callback; + protected PayUtil payUtil; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + payUtil = new PayUtil(this); + } + + protected void initIAB(ArrayList skuList, MySubscriber callback) { + this.skuList = skuList; + this.callback = callback; + payUtil.initIAP(() -> payUtil.getPrices(skuList,callback)); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.refresh_menu) { + if (findViewById(R.id.pb) != null) findViewById(R.id.pb).setVisibility(View.VISIBLE); + payUtil.getPrices(skuList, callback); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onDestroy() { + super.onDestroy(); + payUtil.unbindPayService(); + } + +} diff --git a/qpython/src/od/java/org/qpython/qpy/main/activity/PurchaseActivity.java b/qpython/src/od/java/org/qpython/qpy/main/activity/PurchaseActivity.java new file mode 100644 index 00000000..74335cc2 --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/main/activity/PurchaseActivity.java @@ -0,0 +1,158 @@ +package org.qpython.qpy.main.activity; + +import android.content.Context; +import android.content.Intent; +import android.databinding.DataBindingUtil; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import com.quseit.util.ImageUtil; +import com.quseit.util.VeDate; + +import org.json.JSONException; +import org.json.JSONObject; +import org.qpython.qpy.R; +import org.qpython.qpy.databinding.ActivityPurchaseBinding; +import org.qpython.qpy.databinding.ItemPriceBinding; +import org.qpython.qpy.main.adapter.MyViewHolder; +import org.qpython.qpy.main.app.App; +import org.qpython.qpy.main.server.MySubscriber; +import org.qpython.qpy.main.server.model.GooglePurchaseModel; +import org.qpython.qpy.main.widget.GridSpace; +import org.qpython.qpy.utils.UpdateHelper; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Google purchase activity + * Created by Hmei on 2017-07-19. + */ + +public class PurchaseActivity extends PayActivity { + + private static final String ARTICLE_ID = "article_id"; + + private ActivityPurchaseBinding binding; + private ArrayList skus; + + public static void start(Context context, String articleId) { + Intent starter = new Intent(context, PurchaseActivity.class); + starter.putExtra(ARTICLE_ID, articleId); + context.startActivity(starter); + } + + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = DataBindingUtil.setContentView(this, R.layout.activity_purchase); + skus = new ArrayList<>(Arrays.asList(getResources().getStringArray(R.array.sku))); + + initIAB(skus, new MySubscriber() { + @Override + public void onNext(String[] o) { + super.onNext(o); + if (o == null) { + return; + } + invalidateData(); + GridSpace gridSpace = new GridSpace(2, (int) ImageUtil.dp2px(16), false); + binding.list.setLayoutManager(new GridLayoutManager(PurchaseActivity.this, 2)); + binding.list.addItemDecoration(gridSpace); + binding.list.setAdapter(new PurchaseActivity.ListAdapter(o)); + } + }); + + initView(); + } + + private void initView() { + setSupportActionBar(binding.lt.toolbar); + binding.lt.toolbar.setNavigationIcon(R.drawable.ic_back); + binding.lt.toolbar.setNavigationOnClickListener(v -> finish()); + setTitle(R.string.reward); + } + + private void invalidateData() { + binding.tvThanks.setVisibility(View.VISIBLE); + binding.list.setVisibility(View.VISIBLE); + binding.noData.llRoot.setVisibility(View.GONE); + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.refresh_menu, menu); + return true; + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == BUY_REQUEST_CODE) { + if (resultCode == RESULT_OK) { + GooglePurchaseModel model = App.getGson().fromJson(data.getStringExtra("INAPP_PURCHASE_DATA"), GooglePurchaseModel.class); + if (model == null) { + return; + } + switch (model.getProductId()) { + default: + Toast.makeText(this, R.string.thanks_your_support, Toast.LENGTH_SHORT).show(); + break; + } + // 统计赞赏数据 + JSONObject jsonObject = new JSONObject(); + try { + jsonObject.put("type", model.getProductId()); + jsonObject.put("time", VeDate.getStringDateHourAsInt()); + jsonObject.put("crowdfunding", 0);//0 非众筹/ 1: 0-100 /2: 100-500/3 500-2000 + jsonObject.put("articleId", getIntent().getStringExtra(ARTICLE_ID)); + if (App.getUser() != null) { + jsonObject.put("account", App.getUser().getEmail()); + } + } catch (JSONException e) { + e.printStackTrace(); + } + UpdateHelper.submitIAPLog(this, model.getOrderId(), App.getGson().toJson(jsonObject)); + payUtil.digestPurchase(model.getPurchaseToken()); + finish(); + } + } + } + + private class ListAdapter extends RecyclerView.Adapter> { + String[] dataList; + + ListAdapter(String[] dataList) { + this.dataList = dataList; + } + + @Override + public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + ItemPriceBinding binding = DataBindingUtil.inflate(LayoutInflater.from(PurchaseActivity.this), R.layout.item_price, parent, false); + MyViewHolder holder = new MyViewHolder<>(binding.getRoot()); + holder.setBinding(binding); + return holder; + } + + @Override + public void onBindViewHolder(MyViewHolder holder, int position) { + ItemPriceBinding binding = holder.getBinding(); + binding.tvPrices.setText(dataList[position]); + binding.tvPrices.setOnClickListener(v -> payUtil.purchase(skus.get(position))); + } + + @Override + public int getItemCount() { + return dataList.length; + } + } +} diff --git a/qpython/src/od/java/org/qpython/qpy/main/activity/SignInActivity.java b/qpython/src/od/java/org/qpython/qpy/main/activity/SignInActivity.java new file mode 100644 index 00000000..b5fc5d2c --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/main/activity/SignInActivity.java @@ -0,0 +1,184 @@ +package org.qpython.qpy.main.activity; + +import android.content.Intent; +import android.databinding.DataBindingUtil; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.text.Html; +import android.util.Log; +import android.view.View; +import android.widget.Toast; + +import com.google.android.gms.auth.api.Auth; +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.auth.api.signin.GoogleSignInOptions; +import com.google.android.gms.auth.api.signin.GoogleSignInResult; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.firebase.auth.AuthCredential; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.auth.GoogleAuthProvider; + +import org.qpython.qpy.R; +import org.qpython.qpy.databinding.ActivitySignInBinding; +import org.qpython.qpy.main.app.App; +import org.qpython.qpy.main.app.CONF; +import org.qpython.qpy.main.app.User; +import org.qpython.qpy.main.server.gist.loginScreen.LoginControler; +import org.qpython.qpy.main.server.gist.loginScreen.LoginView; + +/** + * SignIn + * Created by Hmei on 2017-08-04. + */ + +public class SignInActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener ,LoginView{ + private static final int RC_SIGN_IN = 54503; + + private GoogleApiClient mGoogleApiClient; + private ActivitySignInBinding binding; + private FirebaseAuth mAuth; + + private LoginControler mLoginControler; + + { + mAuth = FirebaseAuth.getInstance(); + FirebaseUser currentUser = mAuth.getCurrentUser(); + initUserInfo(currentUser); + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = DataBindingUtil.setContentView(this, R.layout.activity_sign_in); + binding.textView3.setText(Html.fromHtml(getString(R.string.by_signing_in_you_agree_to_out_privacy_policy_term_of_service))); + GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) + .requestIdToken(CONF.GOOGLE_ID_TOKEN) + .requestEmail() + .build(); + + mGoogleApiClient = new GoogleApiClient.Builder(this) + .enableAutoManage(this, this) + .addApi(Auth.GOOGLE_SIGN_IN_API, gso) + .addOnConnectionFailedListener(connectionResult -> showToast(getString(R.string.lost_google_hint))) + .build(); + initListener(); + + mLoginControler = new LoginControler(this); + } + + private void initListener() { + binding.textView3.setOnClickListener(v -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.privacy_html))))); + binding.button2.setOnClickListener(v -> signIn()); + binding.button3.setOnClickListener(v -> finish()); + } + + private void signIn() { + Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); + startActivityForResult(signInIntent, RC_SIGN_IN); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...); + if (requestCode == RC_SIGN_IN) { + GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); + if (result.isSuccess()) { + GoogleSignInAccount account = result.getSignInAccount(); + firebaseAuthWithGoogle(account); + } else { + Log.e("LOGIN", result.getStatus().toString()); + try { + String msg = result.getStatus().getStatusMessage().trim(); + showToast(getString(R.string.login_error) + (msg.equals("") ? "" : ": ") + msg); + initUserInfo(null); + } catch (NullPointerException ignore) { + showToast(getString(R.string.no_google)); + } + } + } + } + + private void firebaseAuthWithGoogle(GoogleSignInAccount acct) { + showLoading(); + AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), "45:FD:60:98:01:9A:37:D9:84:03:06:36:02:F6:85:2C:A2:1F:B8:67"); + mAuth.signInWithCredential(credential) + .addOnCompleteListener(this, task -> { + if (task.isSuccessful()) { + FirebaseUser user = mAuth.getCurrentUser(); + initUserInfo(user); + } else { + showToast(getString(R.string.auth_failed)); + initUserInfo(null); + hideLoading(); + } + }); + } + + private void initUserInfo(FirebaseUser currentUser) { + if (currentUser == null) { + App.setUser(null); + return; + } + Log.d("SingInActivity", "NICK:"+currentUser.getDisplayName()+"-UN:"+currentUser.getEmail()); + User user = new User(); + user.setUserId(currentUser.getUid()); + user.setAvatarUrl(currentUser.getPhotoUrl() == null ? "" : currentUser.getPhotoUrl().toString()); + user.setEmail(currentUser.getEmail()); + user.setUserName(currentUser.getEmail()); + user.setNick(currentUser.getDisplayName()); + App.setUser(user); + if (mLoginControler!=null) { + mLoginControler.login(user); + } else { + Toast.makeText(this, R.string.signin_error, Toast.LENGTH_SHORT).show(); + } + } + + @Override + public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { + initUserInfo(null); + } + + @Override + public void showLoading() { + binding.progressBar2.setVisibility(View.VISIBLE); + } + + @Override + public void hideLoading() { + binding.progressBar2.setVisibility(View.GONE); + } + + @Override + public void showToast(String msg) { + Toast.makeText(SignInActivity.this, msg, Toast.LENGTH_SHORT) + .show(); + } + + @Override + public void loginSuccess() { + Log.d("SignInActivity", "loginSuccess"); +// setResult(RESULT_OK); +// this.finish(); + + setResult(RESULT_OK); + binding.progressBar2.setVisibility(View.GONE); + showToast("login successfully"); + finish(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (mLoginControler!=null) { + + mLoginControler.onDestroy(); + } + } +} \ No newline at end of file diff --git a/qpython/src/od/java/org/qpython/qpy/main/activity/UserActivity.java b/qpython/src/od/java/org/qpython/qpy/main/activity/UserActivity.java new file mode 100644 index 00000000..09607c79 --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/main/activity/UserActivity.java @@ -0,0 +1,77 @@ +package org.qpython.qpy.main.activity; + +import android.content.Context; +import android.content.Intent; +import android.databinding.DataBindingUtil; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +import com.google.firebase.auth.FirebaseAuth; +import com.quseit.util.FileUtils; +import com.quseit.util.ImageDownLoader; + +import org.qpython.qpy.R; +import org.qpython.qpy.codeshare.CONSTANT; +import org.qpython.qpy.codeshare.ShareCodeUtil; +import org.qpython.qpy.databinding.ActivityUserBinding; +import org.qpython.qpy.main.app.App; +import org.qpython.qpy.main.app.CONF; + +import java.io.File; + +public class UserActivity extends AppCompatActivity { + ActivityUserBinding binding; + + public static void start(Context context) { + Intent starter = new Intent(context, UserActivity.class); + context.startActivity(starter); + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = DataBindingUtil.setContentView(this, R.layout.activity_user); + setSupportActionBar(binding.toolbar); + setTitle(getString(R.string.me)); + binding.toolbar.setNavigationIcon(R.drawable.ic_back); + binding.toolbar.setNavigationOnClickListener(v -> finish()); + + ImageDownLoader.setImageFromUrl(this, binding.avatar, App.getUser().getAvatarUrl()); + binding.name.setText(App.getUser().getNick()); + //binding.email.setText(App.getUser().getEmail()); + + binding.usage.setText(R.string.my_space_empty); + binding.logout.setOnClickListener(v -> logout()); + binding.myShareLayout.setOnClickListener(v -> MyGistActivity.startMyShare(UserActivity.this)); + setUsage(); + } + + private void setUsage() { + ShareCodeUtil.getInstance().initUsage(integer -> + binding.usage.setText(getString(R.string.my_space, integer == null ? 0 : integer))); + } + + private void logout() { + new AlertDialog.Builder(this, R.style.MyDialog) + .setTitle(R.string.lout) + .setPositiveButton(R.string.yes, (dialog, which) -> { + FirebaseAuth.getInstance().signOut(); + App.setUser(null); + getPreferences(MODE_PRIVATE).edit().putBoolean(CONSTANT.IS_UPLOAD_INIT, false) + .putString(CONSTANT.CLOUDED_MAP, "") + .apply(); + File cloud_cache = new File(FileUtils.getCloudMapCachePath(getApplicationContext())); + if (cloud_cache.exists()) { + cloud_cache.delete(); + } + finish(); + }) + .setNegativeButton(R.string.no, null) + .create() + .show(); + + } +} \ No newline at end of file diff --git a/qpython/src/od/java/org/qpython/qpy/main/app/AppInit.java b/qpython/src/od/java/org/qpython/qpy/main/app/AppInit.java new file mode 100644 index 00000000..76cc52ab --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/main/app/AppInit.java @@ -0,0 +1,37 @@ +package org.qpython.qpy.main.app; + +import android.content.Context; +import android.util.Log; + +import com.google.firebase.FirebaseApp; +import com.google.firebase.iid.FirebaseInstanceId; +import com.quseit.config.BASE_CONF; + +import org.qpython.qpy.codeshare.ShareCodeUtil; + +/** + * 文 件 名: AppInit + * 创 建 人: ZhangRonghua + * 创建日期: 2018/3/8 15:13 + * 修改时间: + * 修改备注: + */ + +public class AppInit { + + public static void init(Context context){ + + FirebaseApp.initializeApp(context); + if (BASE_CONF.DEBUG) { + try { + FirebaseInstanceId xx = FirebaseInstanceId.getInstance(); + if (xx != null) { + Log.e("Firebase", xx.getToken()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + ShareCodeUtil.getInstance(); + } +} diff --git a/qpython/src/od/java/org/qpython/qpy/main/fragment/ExplorerFragment.java b/qpython/src/od/java/org/qpython/qpy/main/fragment/ExplorerFragment.java new file mode 100644 index 00000000..5713b1ec --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/main/fragment/ExplorerFragment.java @@ -0,0 +1,505 @@ +package org.qpython.qpy.main.fragment; + +import android.content.Intent; +import android.databinding.DataBindingUtil; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import android.os.Environment; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v7.app.AlertDialog; +import android.support.v7.widget.LinearLayoutManager; +import android.text.TextUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import com.google.firebase.database.DatabaseReference; +import com.quseit.util.FileHelper; +import com.quseit.util.ImageUtil; +import com.yanzhenjie.recyclerview.swipe.SwipeMenuCreator; +import com.yanzhenjie.recyclerview.swipe.SwipeMenuItem; + +import org.qpython.qpy.R; +import org.qpython.qpy.codeshare.ShareCodeUtil; +import org.qpython.qpy.codeshare.pojo.CloudFile; +import org.qpython.qpy.databinding.FragmentExplorerBinding; +import org.qpython.qpy.main.activity.NotebookActivity; +import org.qpython.qpy.main.activity.SettingActivity; +import org.qpython.qpy.main.activity.SignInActivity; +import org.qpython.qpy.main.app.App; +import org.qpython.qpy.main.app.CONF; +import org.qpython.qpy.texteditor.EditorActivity; +import org.qpython.qpy.texteditor.TedLocalActivity; +import org.qpython.qpy.texteditor.common.CommonEnums; +import org.qpython.qpy.texteditor.common.RecentFiles; +import org.qpython.qpy.texteditor.ui.adapter.FolderAdapter; +import org.qpython.qpy.texteditor.ui.adapter.bean.FolderBean; +import org.qpython.qpy.texteditor.ui.view.EnterDialog; +import org.qpython.qpy.texteditor.widget.crouton.Crouton; +import org.qpython.qpy.texteditor.widget.crouton.Style; +import org.qpython.qpy.utils.FileUtils; +import org.qpython.qpy.utils.NotebookUtil; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.quseit.util.FolderUtils.sortTypeByName; + +public class ExplorerFragment extends Fragment { + private static final int REQUEST_SAVE_AS = 107; + private static final int REQUEST_HOME_PAGE = 109; + private static final int REQUEST_RECENT = 111; + private static final int LOGIN_REQUEST = 2741; + + private static final String TYPE = "type"; + + private int WIDTH = (int) ImageUtil.dp2px(60); + + private FragmentExplorerBinding binding; + private List folderList; + private FolderAdapter adapter; + private Map cloudedMap = new HashMap<>(); + + private boolean openable = true; // 是否可打开文件 + private boolean uploadable; + + private int type; + private String curPath; + + public static ExplorerFragment newInstance(int type) { + ExplorerFragment myFragment = new ExplorerFragment(); + + Bundle args = new Bundle(); + args.putInt(TYPE, type); + myFragment.setArguments(args); + + return myFragment; + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_explorer, null); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + binding = DataBindingUtil.bind(view); + type = getArguments().getInt(TYPE); + initView(); + initListener(); + initCloud(); + switch (type) { + case REQUEST_RECENT: + binding.rlPath.setVisibility(View.GONE); + uploadable = false; + break; + case REQUEST_SAVE_AS: + binding.ivNewFolder.setVisibility(View.VISIBLE); + uploadable = false; + break; + case REQUEST_HOME_PAGE: + binding.ivNewFolder.setVisibility(View.VISIBLE); + uploadable = true; + break; + } + } + + private void initView() { + SwipeMenuCreator swipeMenuCreator = (leftMenu, rightMenu, viewType) -> { +// SwipeMenuItem uploadItem = new SwipeMenuItem(getContext()) +// .setBackgroundColor(Color.parseColor("#FF4798F3")) +// .setImage(R.drawable.ic_cloud_upload) +// .setHeight(ViewGroup.LayoutParams.MATCH_PARENT) +// .setWidth(WIDTH); + + SwipeMenuItem renameItem = new SwipeMenuItem(getContext()) + .setBackgroundColor(Color.parseColor("#FF4BAC07")) + .setImage(R.drawable.ic_file_rename) + .setHeight(ViewGroup.LayoutParams.MATCH_PARENT) + .setWidth(WIDTH); + + SwipeMenuItem deleteItem = new SwipeMenuItem(getContext()) + .setBackgroundColor(Color.parseColor("#FFD14136")) + .setImage(R.drawable.ic_editor_filetree_close) + .setHeight(ViewGroup.LayoutParams.MATCH_PARENT) + .setWidth(WIDTH); + + switch (type) { + case REQUEST_RECENT: + rightMenu.addMenuItem(deleteItem); + break; + case REQUEST_SAVE_AS: + rightMenu.addMenuItem(deleteItem); + break; + case REQUEST_HOME_PAGE: + //rightMenu.addMenuItem(uploadItem); + rightMenu.addMenuItem(renameItem); + rightMenu.addMenuItem(deleteItem); + break; + } + }; + + folderList = new ArrayList<>(); + adapter = new FolderAdapter(folderList, getArguments().getInt(TYPE) == REQUEST_RECENT); + adapter.setCloudMap(cloudedMap); + binding.swipeList.setLayoutManager(new LinearLayoutManager(getContext())); + binding.swipeList.setSwipeMenuCreator(swipeMenuCreator); + openDir(FileUtils.getAbsolutePath(App.getContext())); + } + + private void initListener() { + binding.ivNewFolder.setOnClickListener(v -> doNewDir()); + binding.prevFolder.setOnClickListener(v -> { + try { + //采用Environment来获取sdcard路径 + String parentPath = new File(curPath).getParent(); + + if (parentPath.length() >= com.quseit.util.FileUtils.getQyPath(App.getContext()).length()) { + openDir(parentPath); + } + } catch (Exception e) { + e.printStackTrace(); + } + }); + binding.swipeList.setSwipeMenuItemClickListener(menuBridge -> { + binding.swipeList.smoothCloseMenu(); + switch (menuBridge.getPosition()) { + case 0: +// if (uploadable) { +// uploadFile(menuBridge.getAdapterPosition()); +// } else { +// deleteFile(menuBridge.getAdapterPosition()); +// } +// break; +// case 1: + renameFile(menuBridge.getAdapterPosition()); + break; + case 1: + deleteFile(menuBridge.getAdapterPosition()); + break; + default:break; + } + }); + adapter.setClickListener(new FolderAdapter.Click() { + @Override + public void onItemClick(int position) { + FolderBean item = folderList.get(position); + if (item.getType().equals(CommonEnums.FileType.FILE)) { + if (!openable) { + Toast.makeText(getActivity(), R.string.cant_open, Toast.LENGTH_SHORT).show(); + return; + } + //判断文件类型 + int lastDot = item.getName().lastIndexOf("."); + if (lastDot != -1) { + String ext = item.getName().substring(lastDot + 1); + openFile(item.getFile(), ext); + } + } else { + openDir(item.getPath()); + } + } + + @Override + public void onLongClick(int position) { + binding.swipeList.smoothOpenRightMenu(position); + } + }); + + binding.swipeList.setAdapter(adapter); + } + + private void gotoSetting() { + SettingActivity.startActivity(getActivity()); + } + + private void openFile(File file, String ext) { + List textExts = Arrays.asList(getContext().getResources().getStringArray(R.array.text_ext)); + if (textExts.contains(ext)) { + EditorActivity.start(getContext(), Uri.fromFile(file)); + } else if (ext.equals("ipynb")) { + boolean notebookenable = NotebookUtil.isNotebookEnable(getActivity()); + if (notebookenable) { + NotebookActivity.start(getActivity(), file.getAbsolutePath(), false); + } else { + + new AlertDialog.Builder(getActivity(), R.style.MyDialog) + .setTitle(R.string.dialog_alert) + .setMessage(getString(R.string.ennable_notebook_first)) + .setNegativeButton(R.string.cancel, (dialog1, which) -> dialog1.dismiss()) + .setPositiveButton(R.string.ok, (dialog1, which) -> gotoSetting()) + .create() + .show(); + + //Toast.makeText(getActivity(), R.string.ennable_notebook_first, Toast.LENGTH_SHORT).show(); + } + } else { + FileUtils.openFile(getContext(), file); + } + } + + private void uploadFile(int adapterPosition) { + File file = folderList.get(adapterPosition).getFile(); + + // only support type in + String ext = ""; + if (file.getName().lastIndexOf(".") > 0) { + ext = file.getName().substring(file.getName().lastIndexOf(".") + 1); + } + boolean isSupport = false; + if (!file.isDirectory()) { + for (String s : getResources().getStringArray(R.array.support_file_ext)) { + if (s.equals(ext)) { + isSupport = true; + break; + } + } + } else { + isSupport = true; + } + + if (!isSupport) { + Toast.makeText(getContext(), R.string.not_support_type_hint, Toast.LENGTH_SHORT).show(); + return; + } + + // only available for already login user + if (App.getUser() == null) { + new AlertDialog.Builder(getActivity(), R.style.MyDialog) + .setTitle(R.string.need_login) + .setMessage(R.string.upload_login_hint) + .setNegativeButton(R.string.no, null) + .setPositiveButton(getString(R.string.login_now), (dialog, which) -> + startActivityForResult(new Intent(getActivity(), SignInActivity.class), LOGIN_REQUEST) + ) + .create() + .show(); + return; + } + + // upload + folderList.get(adapterPosition).setUploading(true); + adapter.notifyItemChanged(adapterPosition); + int[] size = {1}; + DatabaseReference.CompletionListener listener = ((databaseError, databaseReference) -> { + if (databaseError == null) { +// updateClouded(folderList.get(adapterPosition).getFile()); +// adapter.setUploadFile(adapterPosition); +// ((TedLocalActivity) getActivity()).setNewUpload(); +// Toast.makeText(getActivity(), R.string.upload_suc, Toast.LENGTH_SHORT).show(); +// ShareCodeUtil.getInstance().changeUsage(size[0]); + } else { + Toast.makeText(getActivity(), databaseError.getMessage(), Toast.LENGTH_SHORT).show(); + } + folderList.get(adapterPosition).setUploading(false); + adapter.notifyDataSetChanged(); + }); + if (folderList.get(adapterPosition).getFile().isDirectory()) { + // 如果上传整个projects目录,则将该目录下项目分开上传 + if (folderList.get(adapterPosition).getName().contains("projects")) { + File[] files = folderList.get(adapterPosition).getFile().listFiles(); + for (int i = 0; i < files.length; i++) { + if (!ShareCodeUtil.getInstance().uploadFolder(files[i].getPath(), i == 0 ? null : listener, size)) { + folderList.get(adapterPosition).setUploading(false); + adapter.notifyDataSetChanged(); + } + } + } else { + if (!ShareCodeUtil.getInstance().uploadFolder(folderList.get(adapterPosition).getPath(), listener, size)) { + folderList.get(adapterPosition).setUploading(false); + adapter.notifyDataSetChanged(); + } + } + } else { + if (!ShareCodeUtil.getInstance().uploadFile(folderList.get(adapterPosition).getPath(), listener)) { + folderList.get(adapterPosition).setUploading(false); + adapter.notifyDataSetChanged(); + } + } + } + + private void renameFile(int adapterPosition) { + new EnterDialog(getContext()) + .setTitle(getString(R.string.rename)) + .setConfirmListener(name -> { + File oldFile = folderList.get(adapterPosition).getFile(); + File newFile = new File(oldFile.getParent(), name); + boolean renameSuc = oldFile.renameTo(newFile); + if (renameSuc) { + folderList.set(adapterPosition, new FolderBean(newFile)); + adapter.notifyItemChanged(adapterPosition); + return true; + } else { + Toast.makeText(getActivity(), R.string.rename_fail, Toast.LENGTH_SHORT).show(); + return false; + } + }) + .setText(folderList.get(adapterPosition).getName()) + .show(); + } + + private void deleteFile(int adapterPosition) { + switch (type) { + case REQUEST_RECENT: + RecentFiles.removePath(folderList.get(adapterPosition).getPath()); + folderList.remove(adapterPosition); + adapter.notifyDataSetChanged(); + break; + case REQUEST_HOME_PAGE: + case REQUEST_SAVE_AS: + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.MyDialog); + builder.setTitle(R.string.warning) + .setMessage(R.string.delete_file_hint) + .setNegativeButton(R.string.no, null) + .setPositiveButton(R.string.yes, (dialog, which) -> { + String dir = folderList.get(adapterPosition).getFile().getParent(); + FileHelper.clearDir(folderList.get(adapterPosition).getFile().getAbsolutePath(), 0, true); + openDir(dir); + +// folderList.remove(adapterPosition); +// adapter.notifyItemRemoved(adapterPosition); + }) + .show(); + break; + } + } + + private void openDir(String dirPath) { + if (type == REQUEST_RECENT) { + folderList.clear(); + for (String path : RecentFiles.getRecentFiles()) { + folderList.add(new FolderBean(new File(path))); + } + + if (folderList.size() == 0) { + binding.emptyHint.setVisibility(View.VISIBLE); + } else { + binding.emptyHint.setVisibility(View.GONE); + adapter.notifyDataSetChanged(); + } + } else { + binding.tvPath.setText(dirPath); + curPath = dirPath; + File dir = new File(dirPath); + if (dir.exists()) { + File[] files = dir.listFiles(); + if (files != null) { + Arrays.sort(files, sortTypeByName); + folderList.clear(); + adapter.notifyDataSetChanged(); + for (File file : files) { + if (!dir.getName().equals(CONF.BASE_PATH)) { + if (!file.getName().startsWith(".")) { + folderList.add(new FolderBean(file)); + } + + } else { + folderList.add(new FolderBean(file)); + + } + + } + adapter.notifyDataSetChanged(); + } + } else { + Toast.makeText(getContext(), R.string.file_not_exists, Toast.LENGTH_SHORT).show(); + } + } + } + + private void doNewDir() { + new EnterDialog(getContext()) + .setTitle(getString(R.string.new_folder)) + .setHint(getString(R.string.folder_name)) + .setConfirmListener(name -> { + File dirN = new File(curPath, name.equals("") ? getString(R.string.untitled_folder) : name); + if (dirN.exists()) { + Crouton.makeText(getActivity(), getString(R.string.toast_folder_exist), Style.ALERT).show(); + return false; + } else { + if (dirN.mkdirs()) { + openDir(curPath + "/" + name); + } else { + Toast.makeText(getContext(), R.string.mkdir_fail, Toast.LENGTH_SHORT).show(); + } + return true; + } + }) + .show(); + } + + public void backToPrev() { + Log.d("ExplorerFragment", "backToPrev:" + curPath); + String qpyDir = com.quseit.util.FileUtils.getQyPath(App.getContext()) + "/qpython"; + if (curPath == null || qpyDir.equals(curPath) || com.quseit.util.FileUtils.getQyPath(App.getContext()).equals(curPath)) { + getActivity().finish(); + } else { + String parentPath = new File(curPath).getParent(); + if (!TextUtils.isEmpty(parentPath)) openDir(parentPath); + } + } + + private void updateClouded(File file) { + if (file.getParent().contains("projects")) { + String parent = file.getParent() + "/"; + String subPath = file.getPath().substring(file.getPath().indexOf(parent) + parent.length()); + String projName = subPath.contains("/") ? subPath.substring(0, subPath.indexOf("/")) : "/" + subPath; + cloudedMap.put(FileUtils.getAbsolutePath(App.getContext()) + projName, true); + } + if (file.isDirectory()) { + for (File file1 : FileHelper.filterExt(file, getResources().getStringArray(R.array.support_file_ext))) { + cloudedMap.put(file1.getPath(), true); + } + } else { + cloudedMap.put(file.getPath(), true); + } + } + + public String getCurPath() { + return curPath; + } + + private void initCloud() { + if (App.getUser() == null) { + return; + } + ShareCodeUtil.getInstance().getUploadedScripts(false, getActivity(), cloudFiles -> { + if (cloudFiles == null || cloudFiles.size() == 0) { + return; + } + for (CloudFile cloudFile : cloudFiles) { + if (cloudFile.getPath().contains("/projects")) { + String projNode = cloudFile.getPath().contains("/projects3/") ? "/projects3/" : "/projects/"; + cloudedMap.put(FileUtils.getAbsolutePath(App.getContext()) + projNode + cloudFile.getProjectName(), true); + } + cloudedMap.put(FileUtils.getAbsolutePath(App.getContext()) + cloudFile.getPath(), true); + } + adapter.setCloudMap(cloudedMap); + adapter.notifyDataSetChanged(); + }); + } + + public void deleteCloudedMap(String absolutePath) { + if (cloudedMap.containsKey(absolutePath)) { + cloudedMap.remove(absolutePath); + adapter.notifyDataSetChanged(); + } + } + + public void updateCloudedFiles(Map map) { + if (map != null) { + cloudedMap.putAll(map); + } + adapter.notifyDataSetChanged(); + } +} diff --git a/qpython/src/od/java/org/qpython/qpy/main/fragment/MyProjectFragment.java b/qpython/src/od/java/org/qpython/qpy/main/fragment/MyProjectFragment.java new file mode 100644 index 00000000..5d250ec0 --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/main/fragment/MyProjectFragment.java @@ -0,0 +1,259 @@ +package org.qpython.qpy.main.fragment; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.databinding.DataBindingUtil; +import android.graphics.Color; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v7.app.AlertDialog; +import android.support.v7.widget.LinearLayoutManager; +import android.text.Html; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import com.quseit.util.DateTimeHelper; +import com.quseit.util.ImageUtil; +import com.yanzhenjie.recyclerview.swipe.SwipeMenuCreator; +import com.yanzhenjie.recyclerview.swipe.SwipeMenuItem; + +import org.qpython.qpy.R; +import org.qpython.qpy.codeshare.CONSTANT; +import org.qpython.qpy.codeshare.ShareCodeUtil; +import org.qpython.qpy.codeshare.pojo.CloudFile; +import org.qpython.qpy.databinding.FragmentRefreshRvBinding; +import org.qpython.qpy.main.adapter.CloudScriptAdapter; +import org.qpython.qpy.main.app.App; +import org.qpython.qpy.texteditor.TedLocalActivity; +import org.qpython.qpy.utils.FileUtils; +import org.qpython.qpysdk.QPyConstants; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; + +public class MyProjectFragment extends Fragment { + private int WIDTH = (int) ImageUtil.dp2px(60); + private FragmentRefreshRvBinding binding; + private CloudScriptAdapter adapter; + private List scriptList = new ArrayList<>(); + private TedLocalActivity activity; + + public boolean isLoading; + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + binding = DataBindingUtil.bind(LayoutInflater.from(getContext()).inflate(R.layout.fragment_refresh_rv, null)); + return binding.getRoot(); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + scriptList = new ArrayList<>(); + adapter = new CloudScriptAdapter(scriptList); + ShareCodeUtil.getInstance().clearAll(); + initView(); + initListener(); + retry(true); + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + activity = (TedLocalActivity) context; + } + + @Override + public void onResume() { + super.onResume(); + if (adapter != null) { + adapter.notifyDataSetChanged(); + } + } + + @Override + public void onStop() { + super.onStop(); +// activity.locatedCloud(scriptList); + } + + public void retry(boolean forceRefresh) { + if (scriptList != null && adapter != null) { + scriptList.clear(); + adapter.notifyDataSetChanged(); + } + startProgressBar(); + isLoading = true; + ShareCodeUtil.getInstance().getUploadedScripts(forceRefresh, getActivity(), cloudFiles -> { + isLoading = false; + if (cloudFiles == null || cloudFiles.size() == 0) { + showEmpty(); + return; + } +// ((TedLocalActivity) getActivity()).updateCloudFiles(cloudFiles); + scriptList.clear(); + scriptList.addAll(cloudFiles); + adapter.notifyDataSetChanged(); + binding.progressBar.setVisibility(View.GONE); + binding.netError.setVisibility(View.GONE); + }); + } + + public void notifyDataSetChange() { + if (adapter != null) adapter.notifyDataSetChanged(); + } + + private void startProgressBar() { + binding.progressBar.setVisibility(View.VISIBLE); + binding.netError.setVisibility(View.GONE); + Observable.just(null) + .delay(5, TimeUnit.SECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .doOnNext(o -> showNetErrorText()) + .subscribe(); + } + + private void showEmpty() { + if (binding.progressBar.getVisibility() == View.VISIBLE) { + binding.progressBar.setVisibility(View.GONE); + binding.netError.setText(R.string.cloud_empty_hint); + binding.netError.setVisibility(View.VISIBLE); + } + } + + private void showNetErrorText() { + if (binding.progressBar.getVisibility() == View.VISIBLE) { + binding.progressBar.setVisibility(View.GONE); + binding.netError.setText(R.string.net_lagging); + binding.netError.setVisibility(View.VISIBLE); + } + } + + private void initView() { + SwipeMenuCreator swipeMenuCreator = (leftMenu, rightMenu, viewType) -> { + SwipeMenuItem deleteItem = new SwipeMenuItem(getContext()) + .setBackgroundColor(Color.parseColor("#FFD14136")) + .setImage(R.drawable.ic_editor_filetree_close) + .setHeight(ViewGroup.LayoutParams.MATCH_PARENT) + .setWidth(WIDTH); + + SwipeMenuItem downloadItem = new SwipeMenuItem(getContext()) + .setBackgroundColor(Color.parseColor("#FF4798F3")) + .setImage(R.drawable.ic_cloud_download) + .setHeight(ViewGroup.LayoutParams.MATCH_PARENT) + .setWidth(WIDTH); + rightMenu.addMenuItem(downloadItem); + rightMenu.addMenuItem(deleteItem); + }; + binding.swipeList.setLayoutManager(new LinearLayoutManager(getContext())); + binding.swipeList.setSwipeMenuCreator(swipeMenuCreator); + } + + @SuppressLint("StringFormatMatches") + private void initListener() { + binding.netError.setOnClickListener(v -> retry(true)); + binding.swipeList.setSwipeMenuItemClickListener(menuBridge -> { + menuBridge.closeMenu(); + switch (menuBridge.getPosition()) { + default:break; + case 0: + // download + CloudFile cloudFile = scriptList.get(menuBridge.getAdapterPosition()); + cloudFile.setUploading(true); + adapter.notifyItemChanged(menuBridge.getAdapterPosition()); + String path; + path = FileUtils.getAbsolutePath(App.getContext()) + cloudFile.getPath(); + File file = new File(path); + if (file.exists()) { + new AlertDialog.Builder(getContext(), R.style.MyDialog) + .setTitle(R.string.override_hint) + .setMessage(Html.fromHtml(getString(R.string.conflict_hint, + cloudFile.getUploadTime(), + DateTimeHelper.AGO_FULL_DATE_FORMATTER.format(new Date(file.lastModified()))))) + .setNegativeButton(R.string.no, (dialog, which) ->{ + cloudFile.setUploading(false); + adapter.notifyItemChanged(menuBridge.getAdapterPosition()); + }) + .setPositiveButton(R.string.yes, + (dialog, which) -> + getRemoteContentNWrite(cloudFile, path, menuBridge.getAdapterPosition())) + .create() + .show(); + } else { + getRemoteContentNWrite(cloudFile, path, menuBridge.getAdapterPosition()); + } + break; + case 1: + // delete + scriptList.get(menuBridge.getAdapterPosition()).setUploading(true); + adapter.notifyItemChanged(menuBridge.getAdapterPosition()); + ShareCodeUtil.getInstance().deleteUploadScript(scriptList.get(menuBridge.getAdapterPosition()), (databaseError, databaseReference) -> { +// if (databaseError == null && getContext() != null) { +// Toast.makeText(getContext(), R.string.delete_remote_suc, Toast.LENGTH_SHORT).show(); +// adapter.notifyItemRemoved(menuBridge.getAdapterPosition()); +// ((TedLocalActivity) getActivity()).deleteCloudFile(scriptList.get(menuBridge.getAdapterPosition()).getAbsolutePath()); +// scriptList.remove(menuBridge.getAdapterPosition()); +// if (scriptList.size() == 0) { +// binding.netError.setText(R.string.cloud_empty_hint); +// binding.netError.setVisibility(View.VISIBLE); +// } else { +// binding.netError.setVisibility(View.INVISIBLE); +// } +// } else { +// Toast.makeText(getContext(), databaseError.getMessage(), Toast.LENGTH_SHORT).show(); +// } + }); + break; + } + }); + adapter.setCallback((position) -> binding.swipeList.smoothOpenRightMenu(position)); + binding.swipeList.setAdapter(adapter); + } + + private void getRemoteContentNWrite(CloudFile cloudFile, String path, int position) { + String key = cloudFile.getKey(); + if (cloudFile.getProjectName() != null && !cloudFile.getKey().contains("projects3")) { + key = key.replace(cloudFile.getProjectName() + CONSTANT.SLASH_REPLACE, cloudFile.getProjectName() + "/" + CONSTANT.SLASH_REPLACE); + } + ShareCodeUtil.getInstance().getFileContent(key, content -> { + writeFile(path, content, position); + scriptList.get(position).setUploading(false); + adapter.notifyItemChanged(position); + }); + } + + private void writeFile(String path, String content, int adapterPosition) { + try { + FileWriter writer = new FileWriter(new File(path), false); + writer.write(content); + writer.close(); + Toast.makeText(getContext(), R.string.file_downloaded, Toast.LENGTH_SHORT).show(); + adapter.notifyItemChanged(adapterPosition); + } catch (IOException e) { + e.printStackTrace(); + Toast.makeText(getContext(), R.string.override_fail_hint, Toast.LENGTH_SHORT).show(); + } + } + + public void needRefresh(boolean isNewUpload) { + if (binding == null) { + // not init yet + return; + } + if (isNewUpload) { + retry(true); + } + } +} diff --git a/qpython/src/od/java/org/qpython/qpy/main/receiver/LeancloudReceiver.java b/qpython/src/od/java/org/qpython/qpy/main/receiver/LeancloudReceiver.java new file mode 100644 index 00000000..6f12375e --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/main/receiver/LeancloudReceiver.java @@ -0,0 +1,18 @@ +package org.qpython.qpy.main.receiver; + +import android.annotation.SuppressLint; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +/** + * 处理leancloud的消息推送 + */ +public class LeancloudReceiver extends BroadcastReceiver { + + @SuppressLint("UnsafeProtectedBroadcastReceiver") + @Override + public void onReceive(Context context, Intent intent) { + + } +} \ No newline at end of file diff --git a/qpython/src/od/java/org/qpython/qpy/main/service/MyFirebaseInstanceIDService.java b/qpython/src/od/java/org/qpython/qpy/main/service/MyFirebaseInstanceIDService.java new file mode 100644 index 00000000..362c2336 --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/main/service/MyFirebaseInstanceIDService.java @@ -0,0 +1,15 @@ +package org.qpython.qpy.main.service; + +import com.google.firebase.iid.FirebaseInstanceId; +import com.google.firebase.iid.FirebaseInstanceIdService; + +/** + * Created by Hmei on 2017-06-29. + */ + +public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService { + @Override + public void onTokenRefresh() { + String refreshedToken = FirebaseInstanceId.getInstance().getToken(); + } +} diff --git a/qpython/src/od/java/org/qpython/qpy/main/service/MyFirebaseMessagingService.java b/qpython/src/od/java/org/qpython/qpy/main/service/MyFirebaseMessagingService.java new file mode 100644 index 00000000..9f333943 --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/main/service/MyFirebaseMessagingService.java @@ -0,0 +1,88 @@ +package org.qpython.qpy.main.service; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.os.Bundle; +import android.preference.PreferenceManager; + +import com.google.firebase.messaging.FirebaseMessagingService; +import com.google.firebase.messaging.RemoteMessage; + +import org.json.JSONException; +import org.json.JSONObject; +import org.qpython.qpy.R; +import org.qpython.qpy.main.activity.QWebViewActivity; +import org.qpython.qpy.main.app.App; +import org.qpython.qpy.main.app.CONF; +import org.qpython.qpy.main.receiver.NotificationBean; + +import java.util.Map; + + +public class MyFirebaseMessagingService extends FirebaseMessagingService { + private static final int LOG_NOTIFICATION_ID = (int) System.currentTimeMillis(); + + private boolean handled = false; + + @Override + public void onMessageReceived(RemoteMessage remoteMessage) { + super.onMessageReceived(remoteMessage); + Map data = remoteMessage.getData(); + String json = App.getGson().toJson(data); + NotificationBean bean = App.getGson().fromJson(json, NotificationBean.class); + if (!bean.isForce()&&!PreferenceManager.getDefaultSharedPreferences(this).getBoolean(getString(R.string.key_hide_push), true)) { + return; + } + Intent intent; + if (bean.getType().equals("ext")) { + intent = new Intent(Intent.ACTION_VIEW, Uri.parse(bean.getLink())); + } else { + intent = new Intent(App.getContext(), QWebViewActivity.class); + intent.putExtra(QWebViewActivity.TITLE, bean.getTitle()); + intent.putExtra("url", bean.getLink()); + } + + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT); + + Notification.Builder builder = new Notification.Builder(this) + .setSmallIcon(R.drawable.img_home_logo) + .setContentTitle(getString(R.string.app_name)) + .setContentText(bean.getMsg()) + .setContentIntent(pendingIntent) + .setAutoCancel(true); + + Notification notification; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { + notification = builder.build(); + } else { + notification = builder.getNotification(); + } + NotificationManager mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + mNotifyMgr.notify(LOG_NOTIFICATION_ID, notification); + handled = true; + } + + /* @Override + public void handleIntent(Intent intent) { + super.handleIntent(intent); + if (handled) { + return; + } + Bundle bundle = intent.getExtras(); + JSONObject extras = new JSONObject(); + try { + for (String s : bundle.keySet()) { + extras.put(s, bundle.get(s)); + } + } catch (JSONException e) { + e.printStackTrace(); + } + SharedPreferences.Editor editor = getSharedPreferences(CONF.NOTIFICATION_SP_NAME, MODE_PRIVATE).edit(); + editor.putString(CONF.NOTIFICATION_SP_OBJ, extras.toString()); + editor.apply(); + }*/ +} \ No newline at end of file diff --git a/qpython/src/od/java/org/qpython/qpy/main/service/PayUtil.java b/qpython/src/od/java/org/qpython/qpy/main/service/PayUtil.java new file mode 100644 index 00000000..8b88e8cc --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/main/service/PayUtil.java @@ -0,0 +1,150 @@ +package org.qpython.qpy.main.service; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.view.View; +import android.widget.Toast; + +import com.android.vending.billing.IInAppBillingService; + +import org.json.JSONException; +import org.json.JSONObject; +import org.qpython.qpy.R; +import org.qpython.qpy.main.server.MySubscriber; + +import java.util.ArrayList; +import java.util.Arrays; + +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; + +/** + * Created by Hmei + * 1/31/18. + */ + +public class PayUtil { + private static final int BUY_REQUEST_CODE = 2333; + private IInAppBillingService mService; + private ServiceConnection mServiceConn; + private Activity context; + + public PayUtil(Activity context) { + this.context = context; + } + + public void initIAP(PayCallback callback) { + mServiceConn = new ServiceConnection() { + @Override + public void onServiceDisconnected(ComponentName name) { + mService = null; + } + + @Override + public void onServiceConnected(ComponentName name, + IBinder service) { + mService = IInAppBillingService.Stub.asInterface(service); + if (callback != null) callback.doAfterConn(); + } + }; + Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND"); + serviceIntent.setPackage("com.android.vending"); + context.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE); + } + + /** + * 从Google服务器获取不同国家价格表并显示 + */ + public void getPrices(ArrayList skuList, MySubscriber callback) { + if (mService == null) { + Toast.makeText(context, R.string.lose_google_server, Toast.LENGTH_SHORT).show(); + if (context.findViewById(R.id.pb) != null) + context.findViewById(R.id.pb).setVisibility(View.GONE); + return; + } + Bundle querySkus = new Bundle(); + querySkus.putStringArrayList("ITEM_ID_LIST", skuList); + try { + Observable.just(mService.getSkuDetails(3, context.getPackageName(), "inapp", querySkus)) + .map(bundle -> { + ArrayList responseList = bundle.getStringArrayList("DETAILS_LIST"); + if (responseList == null) { + return null; + } + String[] prices = new String[responseList.size()]; + for (int i = 0; i < responseList.size(); i++) { + JSONObject object; + try { + object = new JSONObject(responseList.get(i)); + String price = object.getString("price"); + prices[i] = price; + } catch (JSONException e) { + e.printStackTrace(); + } + } + Arrays.sort(prices, (o1, o2) -> Integer.parseInt(o1.replaceAll("[^0-9]", "")) - Integer.parseInt(o2.replaceAll("[^0-9]", ""))); + return prices; + } + ) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(callback); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + public void digestPurchase(String purchaseToken) { + // 消耗购买,使能重复赞赏同一金额 + try { + Observable.just(mService.consumePurchase(3, context.getPackageName(), purchaseToken)) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + public void purchase(String sku) { + try { + if (mService == null) { + Toast.makeText(context, R.string.lose_google_server, Toast.LENGTH_SHORT).show(); + return; + } + Bundle buyIntentBundle = mService.getBuyIntent(3, context.getPackageName(), + sku, "inapp", "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ"); + switch (buyIntentBundle.getInt("RESPONSE_CODE")) { + case 0: + //BILLING_RESPONSE_RESULT_OK + PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT"); + context.startIntentSenderForResult(pendingIntent.getIntentSender(), + BUY_REQUEST_CODE, new Intent(), 0, 0, 0); + break; + + } + } catch (RemoteException | IntentSender.SendIntentException | NullPointerException e) { + e.printStackTrace(); + } + } + + public void unbindPayService() { + if (mService != null) { + context.unbindService(mServiceConn); + } + } + + public interface PayCallback { + void doAfterConn(); + } + +} diff --git a/qpython/src/od/java/org/qpython/qpy/texteditor/TedLocalActivity.java b/qpython/src/od/java/org/qpython/qpy/texteditor/TedLocalActivity.java new file mode 100644 index 00000000..466f0c8a --- /dev/null +++ b/qpython/src/od/java/org/qpython/qpy/texteditor/TedLocalActivity.java @@ -0,0 +1,308 @@ +package org.qpython.qpy.texteditor; + + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.databinding.DataBindingUtil; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v7.app.AppCompatActivity; +import android.view.MotionEvent; +import android.view.View; +import android.widget.EditText; +import android.widget.Toast; + +import com.google.gson.reflect.TypeToken; +import com.quseit.util.FileHelper; +import com.quseit.util.NAction; + +import org.qpython.qpy.R; +import org.qpython.qpy.codeshare.pojo.CloudFile; +import org.qpython.qpy.databinding.ActivityLocalBinding; +import org.qpython.qpy.main.activity.SignInActivity; +import org.qpython.qpy.main.app.App; +import org.qpython.qpy.main.app.CONF; +import org.qpython.qpy.main.fragment.ExplorerFragment; +import org.qpython.qpy.main.fragment.LocalFragment; +import org.qpython.qpy.main.fragment.MyProjectFragment; +import org.qpython.qpy.utils.NotebookUtil; +import org.qpython.qpysdk.QPyConstants; + +import java.io.File; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TedLocalActivity extends AppCompatActivity { + public static final int REQUEST_SAVE_AS = 107; + public static final int REQUEST_OPEN = 108; + public static final int REQUEST_HOME_PAGE = 109; + public static final int REQUEST_RECENT = 111; + private static final int LOGIN_REQUEST_CODE = 4806; + + private static final String EXTRA_REQUEST_CODE = "request_code"; + private static final String EXTRA_REQUEST_FN = "request_fn"; + + private static final String FRAGMENT_EXPLORER = "explorer"; + private static final String FRAGMENT_CLOUD = "cloud"; + + private ActivityLocalBinding binding; + + private Fragment firstPageFragment; + private MyProjectFragment myProjectFragment; + + private boolean isExplorer = true; + private boolean isNewUpload = false; + //private String defaultFileName = ""; + + public static void start(Context context, int type) { + Intent starter = new Intent(context, TedLocalActivity.class); + starter.putExtra(EXTRA_REQUEST_CODE, type); + context.startActivity(starter); + } + + public static void start(Activity context, int type, int requestCode, String filename) { + Intent starter = new Intent(context, TedLocalActivity.class); + starter.putExtra(EXTRA_REQUEST_CODE, type); + starter.putExtra(EXTRA_REQUEST_FN, filename); + + context.startActivityForResult(starter, requestCode); + } + + public void finishForGetPath(String path) { + Intent intent = new Intent(); + intent.putExtra("path", path); + setResult(Activity.RESULT_OK, intent); + finish(); + } + + public void finishForOpen(String path, boolean isProj) { + Intent intent = new Intent(); + intent.putExtra("path", path); + intent.putExtra("isProj", isProj); + setResult(Activity.RESULT_OK, intent); + finish(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = DataBindingUtil.setContentView(this, R.layout.activity_local); + setSupportActionBar(binding.toolbar); + int type = getIntent().getIntExtra(EXTRA_REQUEST_CODE, REQUEST_HOME_PAGE); + + initView(); + initListener(); + switch (type) { + case REQUEST_RECENT: + setTitle(R.string.recent); + binding.switchBtn.setVisibility(View.GONE); + firstPageFragment = ExplorerFragment.newInstance(type); + break; + case REQUEST_OPEN: + setTitle(R.string.open); + binding.switchBtn.setVisibility(View.GONE); + binding.explore.setVisibility(View.VISIBLE); + firstPageFragment = new LocalFragment(); + break; + case REQUEST_SAVE_AS: + setTitle(R.string.save_as); + binding.vsSave.getRoot().setVisibility(View.VISIBLE); + initSaveListener(); + + String fn = getIntent().getStringExtra(EXTRA_REQUEST_FN); + if (fn!=null) { + binding.vsSave.etName.setText(fn); + } + binding.switchBtn.setVisibility(View.GONE); + firstPageFragment = ExplorerFragment.newInstance(type); + break; + case REQUEST_HOME_PAGE: + setTitle(R.string.explorer); + firstPageFragment = ExplorerFragment.newInstance(type); + break; + } + + getSupportFragmentManager().beginTransaction() + .add(R.id.container, firstPageFragment, FRAGMENT_EXPLORER) + .commit(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + } + + private void initView() { + binding.toolbar.setNavigationIcon(R.drawable.ic_back); + binding.toolbar.setNavigationOnClickListener(v -> finish()); + if (binding.switchBtn.getVisibility() == View.VISIBLE) { + myProjectFragment = new MyProjectFragment(); + } + } + + private void initListener() { + binding.switchBtn.setOnClickListener(v -> { + if (isExplorer) { + if (App.getUser() == null) { + startActivityForResult(new Intent(this, SignInActivity.class), LOGIN_REQUEST_CODE); + return; + } + binding.switchBtn.setImageResource(R.drawable.ic_folder_open); + binding.refresh.setVisibility(View.VISIBLE); + if (getSupportFragmentManager().findFragmentByTag(FRAGMENT_CLOUD) == null) { + getSupportFragmentManager().beginTransaction().add(R.id.container, myProjectFragment, FRAGMENT_CLOUD).commit(); + getSupportFragmentManager().beginTransaction().hide(firstPageFragment).commit(); + } else { + getSupportFragmentManager().beginTransaction() + .hide(firstPageFragment) + .show(myProjectFragment) + .commit(); + } + myProjectFragment.needRefresh(isNewUpload); + isNewUpload = false; + if (!myProjectFragment.isLoading) { + myProjectFragment.notifyDataSetChange(); + } + } else { + binding.switchBtn.setImageResource(R.drawable.ic_cloud_list); + binding.refresh.setVisibility(View.GONE); + getSupportFragmentManager().beginTransaction() + .hide(myProjectFragment) + .show(firstPageFragment) + .commit(); + } + isExplorer = !isExplorer; + }); + + binding.refresh.setOnClickListener(v -> myProjectFragment.retry(true)); + + binding.explore.setOnClickListener(v -> { + start(this, REQUEST_HOME_PAGE); + finish(); + }); + } + + public void doSave(String fn) { + if (fn.length() == 0) { + Toast.makeText(getApplicationContext(), R.string.toast_filename_empty, Toast.LENGTH_SHORT).show(); + } else { + String filename = ((ExplorerFragment) firstPageFragment).getCurPath() + "/" + fn; + final File f = new File(filename); + if (f.exists()) { + Toast.makeText(this, R.string.file_exist_hint, Toast.LENGTH_SHORT).show(); + } else { + setSaveResult(f.getAbsolutePath()); + } + } + } + + protected boolean setSaveResult(String filepath) { + File f = new File(filepath); + if (f.getParentFile().canWrite()) { + finishForGetPath(filepath); + } else { + Toast.makeText(getApplicationContext(), R.string.toast_folder_cant_write, Toast.LENGTH_SHORT).show(); + } + return true; + } + + private void initSaveListener() { + binding.vsSave.btnSave.setOnClickListener(v -> doSave(binding.vsSave.etName.getText().toString())); + binding.vsSave.etName.setOnTouchListener((v, event) -> { + final int DRAWABLE_RIGHT = 2; + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + Drawable deleteText = ((EditText) v).getCompoundDrawables()[DRAWABLE_RIGHT]; + if (deleteText != null) { + if (event.getRawX() >= (v.getRight() - deleteText.getBounds().width())) { + ((EditText) v).setText(""); + return true; + } + } + break; + case MotionEvent.ACTION_UP: + v.performClick(); + break; + default: + break; + } + return false; + }); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultCode == RESULT_OK) { + switch (requestCode) { + case LOGIN_REQUEST_CODE: + binding.switchBtn.setImageResource(R.drawable.ic_folder_open); + if (getSupportFragmentManager().findFragmentByTag(FRAGMENT_CLOUD) == null) { + getSupportFragmentManager().beginTransaction().add(R.id.container, myProjectFragment, FRAGMENT_CLOUD).commit(); + getSupportFragmentManager().beginTransaction().hide(firstPageFragment).commit(); + } else { + getSupportFragmentManager().beginTransaction() + .hide(firstPageFragment) + .show(myProjectFragment) + .commit(); + } + isExplorer = !isExplorer; + break; + } + } + } + + @Override + public void onBackPressed() { + if (firstPageFragment.isVisible() && firstPageFragment instanceof ExplorerFragment) { + ((ExplorerFragment) firstPageFragment).backToPrev(); + } else { + super.onBackPressed(); + } + } + +// public void deleteCloudFile(String path) { +// if (firstPageFragment instanceof ExplorerFragment) { +// ((ExplorerFragment) firstPageFragment).deleteCloudedMap(path); +// } +// } +// +// public void updateCloudFiles(List cloudFiles) { +// Map map = new HashMap<>(); +// boolean isQPy3 = NAction.isQPy3(getBaseContext()); +// String tag = isQPy3?"/projects3/":"/projects/"; +// for (CloudFile cloudFile : cloudFiles) { +// if (cloudFile.getPath().contains(tag)) { +// map.put(QPyConstants.ABSOLUTE_PATH + tag + cloudFile.getProjectName(), true); +// } +// map.put(QPyConstants.ABSOLUTE_PATH + cloudFile.getPath(), true); +// } +// ((ExplorerFragment) firstPageFragment).updateCloudedFiles(map); +// } +// +// public void setNewUpload() { +// isNewUpload = true; +// } + + /** + * 保存云端文件目录到本地 + */ +// public void locatedCloud(List cloudFiles) { +// if (cloudFiles.size() > 0) { +// Type type = new TypeToken>() { +// }.getType(); +// FileHelper.writeToFile(CONF.CLOUD_MAP_CACHE_PATH, App.getGson().toJson(cloudFiles, type)); +// } +// } + + @Override + protected void onDestroy() { + super.onDestroy(); + NotebookUtil.killServer(); + } +} diff --git a/qpython/src/od/res/layout/activity_local.xml b/qpython/src/od/res/layout/activity_local.xml new file mode 100644 index 00000000..abdcb0aa --- /dev/null +++ b/qpython/src/od/res/layout/activity_local.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + +