package com.localtransfer; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.provider.OpenableColumns; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; import androidx.preference.PreferenceManager; import com.google.android.material.snackbar.Snackbar; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.ProtocolException; import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.text.CharacterIterator; import java.text.SimpleDateFormat; import java.text.StringCharacterIterator; import java.util.ArrayList; import java.util.ConcurrentModificationException; import java.util.Date; import java.util.List; import java.util.Timer; import java.util.TimerTask; import static java.lang.Integer.valueOf; public class Transfer { public static String host; public static Integer port; public static String root; public static String protocol; public static String local_storage; public static Activity activity; public static final int STATE_STANDBY = 100; public static final int STATE_RUNNING = 101; public static final int STATE_SUCCESS = 102; public static final int STATE_FAILED = 103; public static final int TYPE_DOWNLOAD = 200; public static final int TYPE_UPLOAD = 201; public static final int NOTIF_SERVICE = 1000; public static final int NOTIF_GROUP = 1001; public static boolean runningTransfer = false; private static List instances = new ArrayList<>(); public static List getInstances() { return instances; } public int state; public String error; public int type; public Integer id; public int drawable; public String info; public String message; public String fileName; public long fileSize; public String fileSizeHuman; public long loaded; public String loadedHuman = "0"; public long percent; public Transfer(int type) { id = View.generateViewId(); switch (type) { case TYPE_DOWNLOAD: drawable = R.drawable.ic_download_24; info = "Download complete"; break; case TYPE_UPLOAD: drawable = R.drawable.ic_upload_24; info = "Upload complete"; break; default: throw new IllegalStateException("Unexpected value: " + type); } state = STATE_STANDBY; this.type = type; } public void AddTransfer() { instances.add(this); if(!runningTransfer) { Intent serviceIntent = new Intent(activity, TransferService.class); ContextCompat.startForegroundService(activity, serviceIntent); runningTransfer = true; new Thread(() -> StartTransfer(this)).start(); } } private static void StartTransfer(Transfer tr) { try { switch (tr.type) { case Transfer.TYPE_DOWNLOAD: tr.downloadFile(); break; case Transfer.TYPE_UPLOAD: tr.uploadFile(); break; default: throw new IllegalStateException("Unexpected value: " + tr.type); } } catch (IOException e) { final String ExceptionName = e.getClass().getSimpleName(); final String ExceptionMess = e.getMessage(); tr.state = Transfer.STATE_FAILED; tr.error = ExceptionName + ": " + ExceptionMess; Progress.PostProgress(tr); if(ExceptionName != null && ExceptionMess != null) { Transfer.error(ExceptionMess); } } try { for (Object obj : Transfer.getInstances()) { Transfer transfer = (Transfer) obj; if (transfer.state == Transfer.STATE_STANDBY) StartTransfer(transfer); } } catch (ConcurrentModificationException e) { e.printStackTrace(); try { Thread.sleep(10); } catch (InterruptedException InterrupEx) { InterrupEx.printStackTrace(); } for (Object obj : Transfer.getInstances()) { Transfer transfer = (Transfer) obj; if (transfer.state == Transfer.STATE_STANDBY) StartTransfer(transfer); } } Intent serviceIntent = new Intent(Transfer.activity, TransferService.class); Transfer.activity.stopService(serviceIntent); Transfer.runningTransfer = false; } public static void parameter(Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); host = prefs.getString("host", null); port = valueOf(prefs.getString("port", null)); root = prefs.getString("root", null); local_storage = prefs.getString("local_storage", null); if(prefs.getBoolean("protocol", false)) protocol = "https"; else protocol = "http"; } public Uri uri; public static void handleSendFile(Uri uri) { if (uri != null) { Transfer tr = new Transfer(Transfer.TYPE_UPLOAD); tr.uri = uri; Cursor cursor = activity.getContentResolver().query(uri, null, null, null, null); cursor.moveToFirst(); tr.fileName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); tr.fileSize = cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE)); tr.fileSizeHuman = Transfer.humanReadableByteCountBin(tr.fileSize); tr.AddTransfer(); } } public static void handleSendText(String sharedText) { if (sharedText != null) { SimpleDateFormat sdfDate = new SimpleDateFormat("yyyyMMdd_HHmmss"); Date now = new Date(); String strDate = sdfDate.format(now); Uri uri = null; try { File outputFile = new File(activity.getCacheDir(), strDate + ".txt"); FileOutputStream fileOut = new FileOutputStream(outputFile); OutputStreamWriter OutWriter = new OutputStreamWriter(fileOut); OutWriter.append(sharedText); OutWriter.close(); fileOut.flush(); fileOut.close(); uri = FileProvider.getUriForFile(activity, activity.getPackageName(), outputFile); } catch (IOException e) { e.printStackTrace(); } handleSendFile(uri); } } public String uploadFile() throws IOException { URL url = null; HttpURLConnection conn = null; DataOutputStream request = null; String boundary = "*****"; int maxBufferSize = 1 * 1024 * 1024; byte[] buffer = new byte[maxBufferSize]; int bufferLength; loaded = 0; String fileNameUTF8 = new String(fileName.getBytes(), StandardCharsets.ISO_8859_1); String type = activity.getContentResolver().getType(uri); url = new URL(protocol, host, port, root + "/upload.php"); url = checkRedirection(url); Progress.PreProgress(this); if(url != null) { Log.d("URL", url.toString()); conn = (HttpURLConnection) url.openConnection(); conn.setDoInput(true); // Allow Inputs conn.setDoOutput(true); // Allow Outputs conn.setUseCaches(false); // Don't use a Cached Copy conn.setChunkedStreamingMode(maxBufferSize); conn.setRequestMethod("POST"); conn.setRequestProperty("Connection", "keep-alive"); conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); conn.connect(); request = new DataOutputStream(conn.getOutputStream()); request.writeBytes("--" + boundary + "\r\n"); request.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"" + fileNameUTF8 + "\"\r\n"); request.writeBytes("Content-Type: " + type + "\r\n\r\n"); InputStream in = activity.getContentResolver().openInputStream(uri); while ((bufferLength = in.read(buffer)) > 0) { request.write(buffer, 0, bufferLength); loaded+= (long) bufferLength; loadedHuman = Transfer.humanReadableByteCountBin(loaded); percent = ((loaded * 100) / fileSize); Progress.ProgressUpdateDelay(this); } request.writeBytes("\r\n"); request.writeBytes("--" + boundary + "--\r\n"); in.close(); int responseCode = conn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { message = "File " + fileName + " successful upload"; state = Transfer.STATE_SUCCESS; Progress.PostProgress(this); } else { String s = conn.getResponseMessage(); throw new HttpErrorException("HTTP Error code " + responseCode + " " + s); } request.flush(); request.close(); conn.disconnect(); } return message; } public File file; public String href; public Button button; public void downloadFile() throws IOException { URL url = null; HttpURLConnection conn = null; int maxBufferSize = 1 * 1024 * 1024; byte[] buffer = new byte[maxBufferSize]; int bufferLength; loaded = 0; Progress.PreProgress(this); String[] parts = href.split("/"); String path = ""; for(int i=0; i< parts.length - 1; i++) { path = path + parts[i] + "/"; } String query = URLEncoder.encode(parts[parts.length - 1], StandardCharsets.UTF_8.toString()); url = new URL(protocol, host, port, path + query.replace("+", "%20")); conn = (HttpURLConnection) url.openConnection(); conn.setDoOutput(true); conn.setRequestMethod("GET"); conn.setConnectTimeout(2000); Log.d("URL", url.toString()); conn.connect(); int responseCode = conn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { FileOutputStream fileOutput = new FileOutputStream(file); InputStream in = conn.getInputStream(); while ((bufferLength = in.read(buffer)) > 0) { fileOutput.write(buffer, 0, bufferLength); loaded+= (long) bufferLength; loadedHuman = Transfer.humanReadableByteCountBin(loaded); percent = ((loaded * 100) / fileSize); Progress.ProgressUpdateDelay(this); } fileOutput.close(); conn.disconnect(); message = "File " + fileName + " successful download"; state = Transfer.STATE_SUCCESS; Progress.PostProgress(this); } else { String s = conn.getResponseMessage(); throw new HttpErrorException("HTTP Error code " + responseCode + " " + s); } conn.disconnect(); } public static boolean setButton(String name, Button button) { for (Object obj: instances) { Transfer tr = (Transfer) obj; if (name.equals(tr.fileName) && tr.state == STATE_RUNNING) { tr.button = button; return true; } } return false; } public static String getFileList() throws IOException { URL url = null; HttpURLConnection conn = null; String fileList = ""; int buf; url = new URL(protocol, host, port, root + "/list.php"); conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(2000); conn.connect(); int responseCode = conn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { InputStreamReader isw = new InputStreamReader(conn.getInputStream()); while ((buf = isw.read()) > 0) { fileList = fileList + (char) buf; } } else { String s = conn.getResponseMessage(); throw new HttpErrorException("HTTP Error code " + responseCode + " " + s); } conn.disconnect(); return fileList; } public static String deleteFile(String file) throws IOException { URL url = null; HttpURLConnection conn = null; String message; String query = URLEncoder.encode(file, StandardCharsets.UTF_8.toString()); String serverUrl = root + "/delete.php?file=" + query; url = new URL(protocol, host, port, serverUrl); conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(2000); conn.connect(); int responseCode = conn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { message = "File " + file + " successful deleted"; } else { String s = conn.getResponseMessage(); throw new HttpErrorException("HTTP Error code " + responseCode + " " + s); } conn.disconnect(); return message; } private static URL checkRedirection(URL url) throws IOException { String location = null; HttpURLConnection conn = null; int responseCode; int nbRedirect = 0; do { conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setInstanceFollowRedirects(false); conn.setConnectTimeout(2000); conn.connect(); responseCode = conn.getResponseCode(); Log.d("Réponse Code", String.valueOf(responseCode)); if(responseCode != HttpURLConnection.HTTP_MOVED_PERM && responseCode != HttpURLConnection.HTTP_MOVED_TEMP) { break; } location = conn.getHeaderField("Location"); Log.d("Location", location); url = new URL(location); conn.disconnect(); nbRedirect++; if(nbRedirect > 10) { throw new ProtocolException("Too many follow-up requests: " + nbRedirect); } } while (responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_MOVED_TEMP); return url; } public static void error(final String message) { System.out.println(message); if (activity != null) { activity.runOnUiThread(new Runnable() { @Override public void run() { Snackbar.make(activity.findViewById(R.id.view_pager), message, Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); } } public static String humanReadableByteCountBin(long bytes) { long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes); if (absB < 1024) { return bytes + " B"; } long value = absB; CharacterIterator ci = new StringCharacterIterator("KMGTPE"); for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) { value >>= 10; ci.next(); } value *= Long.signum(bytes); return String.format("%.1f %ciB", value / 1024.0, ci.current()); } }