// FileStack extension for App Inventor by Marco Perrone package FileStack; import com.google.appinventor.components.runtime.*; import android.app.Activity; import android.content.Intent; import com.google.appinventor.components.annotations.DesignerComponent; import com.google.appinventor.components.annotations.DesignerProperty; import com.google.appinventor.components.annotations.PropertyCategory; import com.google.appinventor.components.annotations.SimpleFunction; import com.google.appinventor.components.annotations.SimpleEvent; import com.google.appinventor.components.annotations.SimpleObject; import com.google.appinventor.components.annotations.SimpleProperty; import com.google.appinventor.components.annotations.UsesPermissions; import com.google.appinventor.components.annotations.UsesActivities; import com.google.appinventor.components.annotations.UsesLibraries; import com.google.appinventor.components.annotations.UsesActivityMetadata; import com.google.appinventor.components.annotations.androidmanifest.*; import com.google.appinventor.components.common.ComponentCategory; import com.google.appinventor.components.common.PropertyTypeConstants; import com.google.appinventor.components.common.YaVersion; import com.google.appinventor.components.runtime.util.GingerbreadUtil; import com.google.appinventor.components.runtime.util.SdkLevel; import com.google.appinventor.components.runtime.util.AsynchUtil; import java.io.IOException; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.DataOutputStream; import java.io.BufferedReader; import java.net.HttpURLConnection; import java.net.URL; import java.net.URI; import java.net.URISyntaxException; import java.util.Scanner; import java.net.CookieHandler; import java.util.Base64; import java.util.Date; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.io.FileWriter; import java.io.BufferedWriter; import java.io.StringWriter; import java.io.PrintWriter; @SimpleObject(external=true) @UsesPermissions(permissionNames = "android.permission.INTERNET,android.permission.READ_EXTERNAL_STORAGE,android.permission.WRITE_EXTERNAL_STORAGE") @DesignerComponent(version = YaVersion.WEB_COMPONENT_VERSION, description = "

Non-visible component to manage files to FileStack.", category = ComponentCategory.EXTENSION, nonVisible = true, iconName = "images/nearfield.png") public class FileStack extends AndroidNonvisibleComponent implements Component { private final Activity activity; private final CookieHandler cookieHandler; private String twoHyphens = "--"; private String boundary = "*****"+Long.toString(System.currentTimeMillis())+"*****"; private String lineEnd = "\r\n"; private URL url; @SimpleEvent(description = "Get FileStack Response") public void GetFileStackResponse(int responseCode, String response, String handle) { EventDispatcher.dispatchEvent(this, "GetFileStackResponse", responseCode, response, handle); } @SimpleEvent(description = "Get Policy and Signature ") public void GetPolicyAndSignature(String policy, String signature) { EventDispatcher.dispatchEvent(this, "GetPolicyAndSignature", policy, signature); } @SimpleFunction(description = "Upload File to FileStack.") public void UploadToFileStack(final String objectLocation, final String fileType, final String apiKey) { AsynchUtil.runAsynchronously(new Runnable() { @Override public void run() { try { UploadToFilestack_Exec(objectLocation, fileType, apiKey, ""); } catch(IOException e) { e.printStackTrace(); } } }); } @SimpleFunction(description = "Upload File to FileStack with Secret Key.") public void UploadToFileStackWithSecretKey(final String objectLocation, final String fileType, final String apiKey, final String secretKey) { AsynchUtil.runAsynchronously(new Runnable() { @Override public void run() { try { UploadToFilestack_Exec(objectLocation, fileType, apiKey, secretKey); } catch(IOException e) { e.printStackTrace(); } } }); } @SimpleFunction(description = "Overwrite File to FileStack with Secret Key.") public void OverwriteFileStackWithSecretKey(final String objectLocation, final String handle, final String fileType, final String apiKey, final String secretKey) { AsynchUtil.runAsynchronously(new Runnable() { @Override public void run() { try { OverwriteToFilestack(objectLocation, handle, fileType, apiKey, secretKey); } catch(IOException e) { e.printStackTrace(); } } }); } @SimpleFunction(description = "Delete File to FileStack with Secret Key.") public void DeleteToFileStackWithSecretKey(final String handle, final String apiKey, final String secretKey) { AsynchUtil.runAsynchronously(new Runnable() { @Override public void run() { try { DeleteToFilestack(handle, apiKey, secretKey); } catch(IOException e) { e.printStackTrace(); } } }); } @SimpleFunction(description = "Calculate Policy and Signature.") public void CalculatePolicyAndSignature(final String secretKey) { AsynchUtil.runAsynchronously(new Runnable() { @Override public void run() { try { CalculatePolicySignature(secretKey); } catch(IOException e) { e.printStackTrace(); } } }); } public FileStack(ComponentContainer container) { super(container.$form()); activity = container.$context(); cookieHandler = (SdkLevel.getLevel() >= SdkLevel.LEVEL_GINGERBREAD) ? GingerbreadUtil.newCookieManager() : null; } void UploadToFilestack_Exec(String OBJECT_LOCATION, String FILETYPE, String APIKEY, String SECRETKEY) throws IOException { try { Path path = Paths.get(URI.create(OBJECT_LOCATION)); byte[] buffer = java.nio.file.Files.readAllBytes(path); String Policy = ""; String Signature = ""; if (SECRETKEY.compareTo("") != 0) { Policy = getPolicy(); Signature = getSignature(SECRETKEY,Policy); url = new URL("https://www.filestackapi.com/api/store/S3?key="+APIKEY+"&policy="+Policy+"&signature="+Signature); } else { url = new URL("https://www.filestackapi.com/api/store/S3?key="+APIKEY); } ConnectToFileStack(FILETYPE, buffer); } catch (IOException e) { e.printStackTrace(); StringWriter errors = new StringWriter(); e.printStackTrace(new PrintWriter(errors)); appendLog(errors.toString()); } } void OverwriteToFilestack(String OBJECT_LOCATION, String HANDLE, String FILETYPE, String APIKEY, String SECRETKEY) throws IOException { try { Path path = Paths.get(URI.create(OBJECT_LOCATION)); byte[] buffer = java.nio.file.Files.readAllBytes(path); String Policy = ""; String Signature = ""; Policy = getPolicy(); Signature = getSignature(SECRETKEY,Policy); url = new URL("https://www.filestackapi.com/api/file/"+HANDLE+"?policy="+Policy+"&signature="+Signature); ConnectToFileStack(FILETYPE, buffer); } catch (IOException e) { e.printStackTrace(); StringWriter errors = new StringWriter(); e.printStackTrace(new PrintWriter(errors)); appendLog(errors.toString()); } } void DeleteToFilestack(String HANDLE, String APIKEY, String SECRETKEY) throws IOException { try { String Policy = ""; String Signature = ""; Policy = getPolicy(); Signature = getSignature(SECRETKEY,Policy); url = new URL("https://www.filestackapi.com/api/file/"+HANDLE+"?key="+APIKEY+"&policy="+Policy+"&signature="+Signature); DeleteFile(); } catch (IOException e) { e.printStackTrace(); StringWriter errors = new StringWriter(); e.printStackTrace(new PrintWriter(errors)); appendLog(errors.toString()); } } void CalculatePolicySignature(String SECRETKEY) throws IOException { final String Policy = getPolicy(); final String Signature = getSignature(SECRETKEY,Policy); // Dispatch the event. activity.runOnUiThread(new Runnable() { @Override public void run() { GetPolicyAndSignature(Policy, Signature); } }); } public void DeleteFile() { HttpURLConnection connection = null; InputStream inputStream = null; try { connection = (HttpURLConnection) url.openConnection(); connection.setDoInput(true); connection.setDoOutput(true); connection.setUseCaches(false); connection.setRequestMethod("DELETE"); connection.setRequestProperty("Connection", "Keep-Alive"); connection.setRequestProperty("User-Agent", "Android Multipart HTTP Client 1.0"); connection.connect(); inputStream = connection.getInputStream(); final int status = connection.getResponseCode(); if (status == HttpURLConnection.HTTP_OK) { BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String inputLine; final StringBuffer response = new StringBuffer(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } inputStream.close(); connection.disconnect(); final String responseStr = response.toString(); final String handleFin = ""; // Dispatch the event. activity.runOnUiThread(new Runnable() { @Override public void run() { GetFileStackResponse(status, responseStr, handleFin); } }); } else { // Dispatch the event. activity.runOnUiThread(new Runnable() { @Override public void run() { GetFileStackResponse(status, "Error!", ""); } }); } } catch (IOException e) { e.printStackTrace(); StringWriter errors = new StringWriter(); e.printStackTrace(new PrintWriter(errors)); appendLog(errors.toString()); } } public void ConnectToFileStack(String FileType, byte[] buffer) { HttpURLConnection connection = null; DataOutputStream outputStream = null; InputStream inputStream = null; int bufferSize; try { connection = (HttpURLConnection) url.openConnection(); connection.setDoInput(true); connection.setDoOutput(true); connection.setUseCaches(false); connection.setRequestMethod("POST"); connection.setRequestProperty("Connection", "Keep-Alive"); connection.setRequestProperty("User-Agent", "Android Multipart HTTP Client 1.0"); connection.setRequestProperty("Content-Type", FileType); try { outputStream = new DataOutputStream(connection.getOutputStream()); } catch (IOException e) { e.printStackTrace(); StringWriter errors = new StringWriter(); e.printStackTrace(new PrintWriter(errors)); appendLog(errors.toString()); } bufferSize = buffer.length; try { outputStream.write(buffer, 0, bufferSize); } catch (IOException e) { e.printStackTrace(); StringWriter errors = new StringWriter(); e.printStackTrace(new PrintWriter(errors)); appendLog(errors.toString()); } outputStream.writeBytes(lineEnd); outputStream.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); inputStream = connection.getInputStream(); final int status = connection.getResponseCode(); String handle = ""; if (status == HttpURLConnection.HTTP_OK) { BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String inputLine; final StringBuffer response = new StringBuffer(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } inputStream.close(); connection.disconnect(); //fileInputStream.close(); outputStream.flush(); outputStream.close(); final String responseStr = response.toString(); if (responseStr.indexOf("cdn.filestackcontent.com") != -1) { handle = responseStr.substring(responseStr.indexOf("cdn.filestackcontent.com")); handle = handle.substring(handle.indexOf("/")+1); handle = handle.substring(0,handle.indexOf(",")-1); } final String handleFin = handle; // Dispatch the event. activity.runOnUiThread(new Runnable() { @Override public void run() { GetFileStackResponse(status, responseStr, handleFin); } }); } else { // Dispatch the event. activity.runOnUiThread(new Runnable() { @Override public void run() { GetFileStackResponse(status, "Error!", ""); } }); } } catch (IOException e) { e.printStackTrace(); StringWriter errors = new StringWriter(); e.printStackTrace(new PrintWriter(errors)); appendLog(errors.toString()); } } public String getPolicy() { Long data = System.currentTimeMillis() + 86400000; //Add 1 day to have a valid policy for the date String dataStr = data.toString(); dataStr = "{\"expiry\":"+dataStr+"}"; String Policy = Base64.getUrlEncoder().encodeToString(dataStr.getBytes()); return Policy; } public String getSignature(String SecretKey, String Policy) { String Signature = ""; try { SecretKeySpec signingKey = new SecretKeySpec(SecretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(signingKey); byte[] rawHmac = mac.doFinal(Policy.getBytes(StandardCharsets.UTF_8)); byte[] hexArray = {(byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f'}; byte[] hexChars = new byte[rawHmac.length * 2]; for ( int j = 0; j < rawHmac.length; j++ ) { int v = rawHmac[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } Signature = new String(hexChars); } catch (Exception e) { throw new RuntimeException(e); } return Signature; } public void appendLog(String text) { File logFile = new File("sdcard/FileStackLog.txt"); if (!logFile.exists()) { try { logFile.createNewFile(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } try { //BufferedWriter for performance, true to set append to file flag BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true)); buf.append(text); buf.newLine(); buf.flush(); buf.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }