Compare commits

..

No commits in common. "master" and "2.0" have entirely different histories.
master ... 2.0

20 changed files with 805 additions and 1019 deletions

2
.idea/compiler.xml generated
View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
<bytecodeTargetLevel target="1.8" />
</component>
</project>

4
.idea/gradle.xml generated
View File

@ -4,9 +4,10 @@
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="testRunner" value="PLATFORM" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="1.8" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
@ -14,6 +15,7 @@
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
<option name="useQualifiedModuleNames" value="true" />
</GradleProjectSettings>
</option>
</component>

16
.idea/misc.xml generated
View File

@ -1,16 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DesignSurface">
<option name="filePathToZoomLevelMap">
<map>
<entry key="app/src/main/res/layout/file_info.xml" value="0.24479166666666666" />
<entry key="app/src/main/res/layout/layout.xml" value="0.35989583333333336" />
<entry key="app/src/main/res/layout/settings_activity.xml" value="0.215625" />
<entry key="app/src/main/res/xml/network_security_config.xml" value="0.35989583333333336" />
<entry key="app/src/main/res/xml/root_preferences.xml" value="0.35833333333333334" />
</map>
</option>
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="org.jetbrains.annotations.Nullable" />
<option name="myDefaultNotNull" value="androidx.annotation.NonNull" />
@ -36,7 +25,7 @@
</option>
<option name="myNotNulls">
<value>
<list size="14">
<list size="13">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="2" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
@ -50,12 +39,11 @@
<item index="10" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.NonNull" />
<item index="11" class="java.lang.String" itemvalue="io.reactivex.annotations.NonNull" />
<item index="12" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.NonNull" />
<item index="13" class="java.lang.String" itemvalue="lombok.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

12
.idea/runConfigurations.xml generated Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

View File

@ -2,6 +2,7 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.localtransfer"
@ -20,8 +21,8 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

View File

@ -3,17 +3,13 @@
package="com.localtransfer">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
@ -21,7 +17,7 @@
android:name=".TransferService"
android:enabled="true"
android:exported="true"></service>
<!-- android:usesCleartextTraffic="true" -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}"

View File

@ -1,289 +0,0 @@
package com.localtransfer;
import android.animation.ObjectAnimator;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.LinearLayout;
import androidx.core.content.FileProvider;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.sql.Timestamp;
public class DownloadFile extends Transfer {
public String href;
public String mime;
public String type;
public Uri uri;
private Button button;
public DownloadFile(Integer id) {
this.id = id;
instances.add(this);
info = "Download complete";
state = STATE_NOT_REQUESTED;
drawable = R.drawable.ic_download_24;
}
@Override
public void startTransfer() {
super.startTransfer();
startedTime = new Timestamp(System.currentTimeMillis());
state = STATE_RUNNING;
buttonProgressStart();
if (uri == null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
values.put(MediaStore.MediaColumns.MIME_TYPE, mime);
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Transfer.shared_storage);
uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
}
try {
downloadFile();
} catch (IOException | NullPointerException e) {
final String ExceptionName = e.getClass().getSimpleName();
final String ExceptionMess = e.getMessage();
e.printStackTrace();
state = Transfer.STATE_FAILED;
progressEnd();
buttonProgressEnd();
if(ExceptionName != null && ExceptionMess != null) {
errorSnackbar(ExceptionName + ": " + ExceptionMess);
}
}
}
public boolean exist(Context context) {
if (!Transfer.use_shared_storage){
File file = new File(activity.getFilesDir(), name);
uri = FileProvider.getUriForFile(activity, activity.getPackageName(), file);
if (file.exists() && file.length() == size)
return true;
}
else {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
Cursor cursor = context.getContentResolver().query(MediaStore.Downloads.EXTERNAL_CONTENT_URI, null, null, null, null);
while (cursor.moveToNext()) {
String MediaStore_File_name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Downloads.DISPLAY_NAME));
long MediaStore_File_size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Downloads.SIZE));
if(MediaStore_File_name.equals(name) && MediaStore_File_size == size) {
int cursor_id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Downloads._ID));
uri = ContentUris.withAppendedId(MediaStore.Downloads.EXTERNAL_CONTENT_URI, cursor_id);
return true;
}
}
}
else {
String save_location = null;
if (mime.equals("text/plain"))
save_location = context.getCacheDir().toString();
else
save_location = Environment.getExternalStorageDirectory() + "/" + Transfer.shared_storage;
new File(save_location).mkdirs();
File file = new File(save_location, name);
try {
file.createNewFile();
} catch (IOException e) {
final String ExceptionName = e.getClass().getSimpleName();
final String ExceptionMess = e.getMessage();
e.printStackTrace();
if(ExceptionName != null && ExceptionMess != null) {
errorSnackbar(ExceptionName + ": " + ExceptionMess);
}
}
uri = Uri.fromFile(file);
if (file.exists() && file.length() == size)
return true;
}
}
return false;
}
public void setButton(Button button) {
this.button = button;
if (this.state == STATE_STANDBY) {
buttonProgressStart();
}
}
public InputStream getThumbnail() {
InputStream thumbnail = null;
URL url;
try {
String query = URLEncoder.encode(href, StandardCharsets.UTF_8.toString());
String serverUrl = root + "/thumbnail.php?file=" + query;
url = new URL(protocol, host, port, serverUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(2000);
conn.connect();
thumbnail = conn.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
return thumbnail;
}
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;
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) {
OutputStream fileOutput = activity.getContentResolver().openOutputStream(uri);
InputStream in = conn.getInputStream();
while ((bufferLength = in.read(buffer)) > 0) {
fileOutput.write(buffer, 0, bufferLength);
loaded+= bufferLength;
percent = ((loaded * 100) / size);
progressUpdateDelay();
buttonProgressUpdate();
}
fileOutput.close();
conn.disconnect();
message = "File " + name + " successful download";
state = Transfer.STATE_SUCCESS;
progressEnd();
buttonProgressEnd();
}
else {
String s = conn.getResponseMessage();
throw new HttpErrorException("error code: " + responseCode + " " + s);
}
conn.disconnect();
}
public String deleteFile() throws IOException {
URL url = null;
HttpURLConnection conn = null;
String message;
String query = URLEncoder.encode(name, 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 " + name + " successful deleted";
}
else {
String s = conn.getResponseMessage();
throw new HttpErrorException("error code: " + responseCode + " " + s);
}
conn.disconnect();
return message;
}
public void buttonProgressStart() {
activity.runOnUiThread(() -> {
final Drawable image = Transfer.activity.getDrawable(R.drawable.ic_spinner_rotate);
if(button != null) {
int h = image.getIntrinsicHeight();
int w = image.getIntrinsicWidth();
image.setBounds(0, 0, w, h);
button.setCompoundDrawables(button.getCompoundDrawables()[0], null, image, null);
ObjectAnimator anim = ObjectAnimator.ofInt(image, "level", 0, 10000);
anim.setDuration(1000);
anim.setRepeatCount(Animation.INFINITE);
anim.setInterpolator(new LinearInterpolator());
anim.start();
}
});
}
private void buttonProgressUpdate(){
activity.runOnUiThread(() -> {
if (app_started && button != null)
button.setText(String.format("Download in progress %d%%", percent));
});
}
public void buttonProgressEnd() {
activity.runOnUiThread(() -> {
if (button != null) {
final LinearLayout layout = (LinearLayout) button.getParent();
layout.findViewById(R.id.file_view).setVisibility(LinearLayout.VISIBLE);
layout.findViewById(R.id.file_share).setVisibility(LinearLayout.VISIBLE);
button.setVisibility(LinearLayout.GONE);
button.setEnabled(true);
button.setText(activity.getString(R.string.file_download));
button.setCompoundDrawables(button.getCompoundDrawables()[0], null, null, null);
}
});
}
}

View File

@ -2,33 +2,31 @@ package com.localtransfer;
import android.Manifest;
import android.app.Activity;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayout;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager;
import androidx.viewpager.widget.ViewPager;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Environment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import java.io.File;
import java.util.ArrayList;
@ -61,18 +59,7 @@ public class MainActivity extends AppCompatActivity {
}
});
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.Q) {
checkAndRequestPermissions();
}
Transfer.notifBuilder = new Notification.Builder(this);
Transfer.notifiManager = NotificationManagerCompat.from(this);
Transfer.inflater = (LayoutInflater) this.getSystemService(this.LAYOUT_INFLATER_SERVICE);
Transfer.resolver = this.getContentResolver();
Intent notificationIntent = new Intent(this, MainActivity.class);
Transfer.pendingIntent = PendingIntent.getActivity(this,0, notificationIntent, 0);
checkAndRequestPermissions();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
@ -81,8 +68,7 @@ public class MainActivity extends AppCompatActivity {
.putString("host", getString(R.string.server_host_def))
.putString("port", getString(R.string.server_port_def))
.putString("root", getString(R.string.server_root_def))
.putString("shared_storage", getString(R.string.shared_storage_def))
.putBoolean("use_shared_storage", false)
.putString("local_storage", getString(R.string.local_storage_def))
.apply();
Transfer.parameter(this);
@ -96,10 +82,10 @@ public class MainActivity extends AppCompatActivity {
viewPager.setCurrentItem(2);
if ("text/plain".equals(type)) {
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
UploadFile.handleSendText(sharedText);
Transfer.handleSendText(sharedText);
} else {
Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
UploadFile.handleSendFile(uri);
Transfer.handleSendFile(uri);
}
} else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
viewPager.setCurrentItem(2);
@ -107,7 +93,7 @@ public class MainActivity extends AppCompatActivity {
int nbItem = fileUris.size();
Toast.makeText(this, "You select " + nbItem + " files", Toast.LENGTH_SHORT).show();
if (fileUris != null)
for(Uri uri : fileUris) UploadFile.handleSendFile(uri);
for(Uri uri : fileUris) Transfer.handleSendFile(uri);
}
}
@ -125,7 +111,7 @@ public class MainActivity extends AppCompatActivity {
if (type != null) {
String sharedText = data.getStringExtra(Intent.EXTRA_TEXT);
UploadFile.handleSendText(sharedText);
Transfer.handleSendText(sharedText);
}
else {
ArrayList<Uri> fileUris = new ArrayList<>();
@ -141,7 +127,7 @@ public class MainActivity extends AppCompatActivity {
fileUris.add(uri);
}
for (Uri uri : fileUris) {
UploadFile.handleSendFile(uri);
Transfer.handleSendFile(uri);
}
}
}
@ -151,14 +137,14 @@ public class MainActivity extends AppCompatActivity {
public void onResume(){
super.onResume();
Transfer.app_started = true;
Progress.app_started = true;
}
@Override
public void onPause(){
super.onPause();
Transfer.app_started = false;
Progress.app_started = false;
}
@Override
@ -196,7 +182,7 @@ public class MainActivity extends AppCompatActivity {
startActivity(settings);
break;
case R.id.action_folder:
String save_location = Environment.getExternalStorageDirectory() + "/" + Transfer.shared_storage;
String save_location = Environment.getExternalStorageDirectory() + "/" + Transfer.local_storage;
Uri selectedUri = Uri.parse(save_location.toString());
boolean intentSuccess = false;
try {
@ -227,7 +213,7 @@ public class MainActivity extends AppCompatActivity {
public static final int REQUEST_ID_READ_EXTERNAL_STORAGE = 2001;
private boolean checkAndRequestPermissions() {
private boolean checkAndRequestPermissions() {
if (ContextCompat.checkSelfPermission(
this, Manifest.permission.READ_EXTERNAL_STORAGE) ==

View File

@ -0,0 +1,161 @@
package com.localtransfer;
import android.animation.ObjectAnimator;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.core.app.NotificationManagerCompat;
import com.google.android.material.snackbar.Snackbar;
import java.util.ConcurrentModificationException;
public class Progress {
private static Notification.Builder bl = new Notification.Builder(Transfer.activity);
private static NotificationManagerCompat notifiManager = NotificationManagerCompat.from(Transfer.activity);
private static final LayoutInflater inflater = (LayoutInflater) Transfer.activity.getSystemService(Transfer.activity.LAYOUT_INFLATER_SERVICE);
private static Intent notificationIntent = new Intent(Transfer.activity, MainActivity.class);
private static PendingIntent pendingIntent = PendingIntent.getActivity(Transfer.activity,0, notificationIntent, 0);
public static View root;
public static boolean fragment_on = false;
public static boolean app_started = false;
private static long savedTimeMillis;
public static void PreProgress(Transfer tr) {
Transfer.activity.runOnUiThread(new Runnable() {
@Override
public void run() {
final Drawable image = Transfer.activity.getDrawable(R.drawable.ic_spinner_rotate);
if(tr.button != null) {
int h = image.getIntrinsicHeight();
int w = image.getIntrinsicWidth();
image.setBounds(0, 0, w, h);
tr.button.setCompoundDrawables(tr.button.getCompoundDrawables()[0], null, image, null);
ObjectAnimator anim = ObjectAnimator.ofInt(image, "level", 0, 10000);
anim.setDuration(1000);
anim.setRepeatCount(Animation.INFINITE);
anim.setInterpolator(new LinearInterpolator());
anim.start();
}
}
});
}
public static void ProgressUpdateDelay(Transfer tr) {
// Run only every second to reduce CPU usage
if (System.currentTimeMillis() > (savedTimeMillis + 1000)) {
savedTimeMillis = System.currentTimeMillis();
ProgressUpdate(tr);
}
}
private static void ProgressUpdate(Transfer tr){
Transfer.activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (Progress.app_started && tr.button != null)
tr.button.setText(String.format("Download in progress %d%%", tr.percent));
try {
bl.setSmallIcon(R.drawable.ic_upload_and_download_from_the_cloud)
.setContentTitle(tr.fileName)
.setContentText(String.format("%d%% %s/%s", tr.percent, tr.loadedHuman, tr.fileSizeHuman))
.setProgress(100, (int) tr.percent, false)
.setContentIntent(pendingIntent);
notifiManager.notify(Transfer.NOTIF_SERVICE, bl.build());
if (app_started && fragment_on)
showProgressFragment(tr);
} catch (ConcurrentModificationException e) {
e.printStackTrace();
}
}
});
}
public static void PostProgress(Transfer tr) {
ProgressUpdate(tr);
Transfer.activity.runOnUiThread(new Runnable() {
@Override
public void run() {
notifiManager.cancel(Transfer.NOTIF_SERVICE);
if (!app_started) {
bl.setSmallIcon(R.drawable.ic_upload_and_download_from_the_cloud)
.setContentTitle(tr.fileName)
.setContentText(tr.info)
.setProgress(0, 0, false)
.setContentIntent(pendingIntent);
notifiManager.notify(tr.id, bl.build());
}
if (tr.button != null) {
final LinearLayout layout = (LinearLayout) tr.button.getParent();
layout.findViewById(R.id.file_view).setVisibility(LinearLayout.VISIBLE);
layout.findViewById(R.id.file_share).setVisibility(LinearLayout.VISIBLE);
tr.button.setVisibility(LinearLayout.GONE);
tr.button.setEnabled(true);
tr.button.setText(Transfer.activity.getString(R.string.file_download));
tr.button.setCompoundDrawables(tr.button.getCompoundDrawables()[0], null, null, null);
}
System.out.println(tr.message);
/*Snackbar.make(Transfer.activity.findViewById(R.id.view_pager), tr.message, Snackbar.LENGTH_LONG)
.setAction("Action", null).show();*/
Toast.makeText(Transfer.activity, tr.message, Toast.LENGTH_SHORT).show();
}
});
}
public static void showProgressFragment(Transfer tr){
LinearLayout groot = root.findViewById(R.id.groot);
final View progress;
if (groot.findViewById(tr.id) != null) {
progress = groot.findViewById(tr.id);
} else {
progress = inflater.inflate(R.layout.progress, null);
progress.setId(tr.id);
Transfer.activity.runOnUiThread(() -> {
groot.addView(progress, 0);
});
}
((ProgressBar) progress.findViewById(R.id.progressBar)).setProgress((int) tr.percent);
((ImageView) progress.findViewById(R.id.transferType)).setImageResource(tr.drawable);
((TextView) progress.findViewById(R.id.progressText)).setText(tr.percent + " %");
((TextView) progress.findViewById(R.id.fileName)).setText(tr.fileName);
((TextView) progress.findViewById(R.id.fileSize)).setText(String.format("%s/%s", tr.loadedHuman, tr.fileSizeHuman));
if (tr.state == Transfer.STATE_SUCCESS)
((TextView) progress.findViewById(R.id.fileName)).setTextColor(Color.GREEN);
else if (tr.state == Transfer.STATE_FAILED)
((TextView) progress.findViewById(R.id.fileName)).setTextColor(Color.RED);
}
}

View File

@ -1,10 +1,8 @@
package com.localtransfer;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
@ -12,12 +10,9 @@ import android.view.MenuItem;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
import androidx.preference.SwitchPreference;
import lib.folderpicker.FolderPicker;
@ -47,32 +42,8 @@ public class SettingsActivity extends AppCompatActivity {
setPreferencesFromResource(R.xml.root_preferences, rootKey);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
directory = findPreference("shared_storage");
directory.setSummary(prefs.getString("shared_storage", null));
SwitchPreference shared;
shared = findPreference("use_shared_storage");
if(shared.isChecked()) {
if(checkAndRequestPermissions())
directory.setVisible(true);
else
shared.setChecked(false);
}
else
directory.setVisible(false);
shared.setOnPreferenceClickListener(preference -> {
if(shared.isChecked()) {
if(checkAndRequestPermissions())
directory.setVisible(true);
else
shared.setChecked(false);
}
else
directory.setVisible(false);
return false;
});
directory = findPreference("local_storage");
directory.setSummary(prefs.getString("local_storage", null));
directory.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
@ -83,35 +54,6 @@ public class SettingsActivity extends AppCompatActivity {
});
}
private boolean checkAndRequestPermissions() {
if (ContextCompat.checkSelfPermission(
getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE) ==
PackageManager.PERMISSION_GRANTED) {
Log.d("READ_EXTERNAL_STORAGE", "already granted");
return true;
}
else {
ActivityCompat.requestPermissions(getActivity(), new String[] {android.Manifest.permission.READ_EXTERNAL_STORAGE}, MainActivity.REQUEST_ID_READ_EXTERNAL_STORAGE);
}
return false;
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[],
int[] grantResults) {
switch (requestCode) {
case MainActivity.REQUEST_ID_READ_EXTERNAL_STORAGE:
if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d("READ_EXTERNAL_STORAGE", "granted");
} else {
Log.d("READ_EXTERNAL_STORAGE", "No granted");
}
return;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
@ -123,7 +65,7 @@ public class SettingsActivity extends AppCompatActivity {
String save_location = currentPath.replace(Environment.getExternalStorageDirectory() + "/", "");
Log.d("Path", save_location);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
prefs.edit().putString("shared_storage", save_location).apply();
prefs.edit().putString("local_storage", save_location).apply();
directory.setSummary(save_location);
}
}

View File

@ -1,269 +1,453 @@
package com.localtransfer;
import static java.lang.Integer.valueOf;
import android.app.Activity;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.core.app.NotificationManagerCompat;
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.sql.Timestamp;
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.Comparator;
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 {
protected static Notification.Builder notifBuilder;
protected static NotificationManagerCompat notifiManager;
protected static LayoutInflater inflater;
protected static ContentResolver resolver;
protected static PendingIntent pendingIntent;
public static View fragment_progress;
public static boolean fragment_on = false;
public static boolean app_started = false;
protected static long savedTimeMillis;
public static String host;
public static Integer port;
public static String root;
public static String protocol;
public static String shared_storage;
public static boolean use_shared_storage;
public static String local_storage;
public static Activity activity;
public int state;
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<Transfer> 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 String sizeSI;
public long fileSize;
public String fileSizeHuman;
public long loaded;
public String loadedSI = "0";
public String loadedHuman = "0";
public long percent;
public static final int NOTIF_SERVICE = 1000;
public Transfer(int type) {
public static final int STATE_NOT_REQUESTED = 100;
public static final int STATE_STANDBY = 110;
public static final int STATE_RUNNING = 120;
public static final int STATE_SUCCESS = 130;
public static final int STATE_FAILED = 140;
id = View.generateViewId();
public static Thread thread = new Thread();
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);
}
Integer id;
state = STATE_STANDBY;
this.type = type;
public String name;
}
public long size;
public void AddTransfer() {
instances.add(this);
Timestamp addedTime;
Timestamp startedTime;
if(!runningTransfer) {
Intent serviceIntent = new Intent(activity, TransferService.class);
ContextCompat.startForegroundService(activity, serviceIntent);
runningTransfer = true;
new Thread(() -> StartTransfer(this)).start();
}
}
static List<Transfer> instances = new ArrayList<>();
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();
public static List<Transfer> getInstances() {
List<Transfer> in = new ArrayList<>();
for (Transfer file: instances) {
//if(Arrays.asList(STATE_STANDBY, STATE_RUNNING, STATE_SUCCESS, STATE_FAILED).contains(file.state)) {
if(file.state == STATE_STANDBY ||
file.state == STATE_RUNNING ||
file.state == STATE_SUCCESS ||
file.state == STATE_FAILED) {
in.add(file);
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);
}
}
//in.sort(Comparator.comparing(Transfer::getAddedTime).reversed());
in.sort(Comparator.comparing(e -> e.addedTime));
return in;
}
public static Transfer getFileById(int id) {
for (Object obj: instances) {
Transfer p = (Transfer) obj;
if (p.id == id) return p;
}
return null;
}
public Integer getId() {
return id;
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);
shared_storage = prefs.getString("shared_storage", null);
use_shared_storage = prefs.getBoolean("use_shared_storage", false);
local_storage = prefs.getString("local_storage", null);
if(prefs.getBoolean("protocol", false))
protocol = "https";
else
protocol = "http";
String portValue = prefs.getString("port", null);
if(portValue.equals("")){
if(protocol.equals("http"))
port = 80;
else if(protocol.equals("https"))
port = 443;
}
else
port = valueOf(portValue);
}
public void AddTransfer() {
public Uri uri;
state = STATE_STANDBY;
public static void handleSendFile(Uri uri) {
if (uri != null) {
Transfer tr = new Transfer(Transfer.TYPE_UPLOAD);
tr.uri = uri;
addedTime = new Timestamp(System.currentTimeMillis());
Cursor cursor = activity.getContentResolver().query(uri, null, null, null, null);
cursor.moveToFirst();
Thread.State state = thread.getState();
tr.fileName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
tr.fileSize = cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE));
tr.fileSizeHuman = Transfer.humanReadableByteCountBin(tr.fileSize);
if(state == Thread.State.NEW || state == Thread.State.TERMINATED) {
thread = new Thread(startTransfers);
thread.start();
/*Intent serviceIntent = new Intent(activity, TransferService.class);
ContextCompat.startForegroundService(activity, serviceIntent);*/
tr.AddTransfer();
}
}
public void startTransfer() {
}
public static Runnable startTransfers = () -> {
try {
for (Transfer transfer : getInstances()) {
if (transfer.state == Transfer.STATE_STANDBY) {
transfer.startTransfer();
thread.run(); // restart thread for another potential standby transfer
break; // ignore remainder instance
}
}
} catch (ConcurrentModificationException e) {
e.printStackTrace();
}
/*Intent serviceIntent = new Intent(Transfer.activity, TransferService.class);
Transfer.activity.stopService(serviceIntent);*/
};
public void progressUpdateDelay() {
// Run only every second to reduce CPU usage
if (System.currentTimeMillis() > (savedTimeMillis + 1000)) {
savedTimeMillis = System.currentTimeMillis();
progressUpdate();
}
}
private void progressUpdate(){
loadedSI = Transfer.humanReadableByteCountBin(loaded);
activity.runOnUiThread(() -> {
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();
notifBuilder.setSmallIcon(R.drawable.ic_upload_and_download_from_the_cloud)
.setContentTitle(name)
.setContentText(String.format("%d%% %s/%s", percent, loadedSI, sizeSI))
.setProgress(100, (int) percent, false)
.setContentIntent(pendingIntent);
notifiManager.notify(Transfer.NOTIF_SERVICE, notifBuilder.build());
fileOut.flush();
fileOut.close();
if (app_started && fragment_on)
showProgressFragment();
uri = FileProvider.getUriForFile(activity, activity.getPackageName(), outputFile);
} catch (ConcurrentModificationException e) {
} catch (IOException e) {
e.printStackTrace();
}
});
handleSendFile(uri);
}
}
public void progressEnd() {
progressUpdate();
activity.runOnUiThread(() -> {
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;
notifiManager.cancel(Transfer.NOTIF_SERVICE);
String fileNameUTF8 = new String(fileName.getBytes(), StandardCharsets.ISO_8859_1);
if (!app_started) {
notifBuilder.setSmallIcon(R.drawable.ic_upload_and_download_from_the_cloud)
.setContentTitle(name)
.setContentText(info)
.setProgress(0, 0, false)
.setContentIntent(pendingIntent);
notifiManager.notify(id, notifBuilder.build());
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);
}
System.out.println(message);
/*Snackbar.make(Transfer.activity.findViewById(R.id.view_pager), tr.message, Snackbar.LENGTH_LONG)
.setAction("Action", null).show();*/
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();
request.writeBytes("\r\n");
request.writeBytes("--" + boundary + "--\r\n");
});
}
in.close();
public void showProgressFragment(){
int responseCode = conn.getResponseCode();
LinearLayout groot = fragment_progress.findViewById(R.id.groot);
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);
}
View progress = groot.findViewById(id);
request.flush();
if (progress == null) {
progress = inflater.inflate(R.layout.progress, null);
progress.setId(id);
final View final_progress = progress;
activity.runOnUiThread(() -> {
groot.addView(final_progress, 0);
});
request.close();
conn.disconnect();
}
((ProgressBar) progress.findViewById(R.id.progressBar)).setProgress((int) percent);
((ImageView) progress.findViewById(R.id.transferType)).setImageResource(drawable);
((TextView) progress.findViewById(R.id.progressText)).setText(percent + " %");
((TextView) progress.findViewById(R.id.fileName)).setText(name);
((TextView) progress.findViewById(R.id.fileSize)).setText(String.format("%s/%s", loadedSI, sizeSI));
if (state == Transfer.STATE_SUCCESS)
((TextView) progress.findViewById(R.id.fileName)).setTextColor(Color.GREEN);
else if (state == Transfer.STATE_FAILED)
((TextView) progress.findViewById(R.id.fileName)).setTextColor(Color.RED);
return message;
}
static URL checkRedirection(URL url) throws IOException {
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;
@ -304,47 +488,18 @@ public class Transfer {
return url;
}
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("error code: " + responseCode + " " + s);
}
conn.disconnect();
return fileList;
}
private static int timeout;
public static void errorSnackbar(final String message) {
public static void error(final String message) {
System.out.println(message);
if (activity != null) {
activity.runOnUiThread(() -> Snackbar.make(activity.findViewById(R.id.view_pager), message, Snackbar.LENGTH_LONG)
.setAction("Action", null).show());
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Snackbar.make(activity.findViewById(R.id.view_pager), message, Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
}

View File

@ -1,201 +0,0 @@
package com.localtransfer;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import android.util.Log;
import android.view.View;
import androidx.core.content.FileProvider;
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.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ConcurrentModificationException;
import java.util.Date;
public class UploadFile extends Transfer {
public Uri uri;
public String type;
public UploadFile() {
this.id = View.generateViewId();
instances.add(this);
info = "Upload complete";
state = STATE_NOT_REQUESTED;
drawable = R.drawable.ic_upload_24;
}
public static void handleSendFile(Uri uri) {
if (uri != null) {
UploadFile file = new UploadFile();
file.uri = uri;
Cursor cursor = activity.getContentResolver().query(uri, null, null, null, null);
cursor.moveToFirst();
file.type = activity.getContentResolver().getType(uri);
int col_name = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if (col_name != -1)
file.name = cursor.getString(col_name);
if(file.name == null)
file.name = file.type.split("/")[0] + "_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + "." + file.type.split("/")[1];
int col_size = cursor.getColumnIndex(OpenableColumns.SIZE);
if (col_name != -1) {
file.size = cursor.getLong(col_size);
file.sizeSI = Transfer.humanReadableByteCountBin(file.size);
}
file.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);
}
}
@Override
public void startTransfer() {
super.startTransfer();
startedTime = new Timestamp(System.currentTimeMillis());
state = STATE_RUNNING;
try {
uploadFile();
} catch (IOException e) {
final String ExceptionName = e.getClass().getSimpleName();
final String ExceptionMess = e.getMessage();
state = Transfer.STATE_FAILED;
progressEnd();
if(ExceptionName != null && ExceptionMess != null) {
errorSnackbar(ExceptionMess);
}
}
}
private String uploadFile() throws IOException {
String message = null;
URL url = null;
HttpURLConnection conn = null;
DataOutputStream request = null;
String boundary = "*****";
final int maxBufferSize = 1 * 1024 * 1024;
byte[] buffer = new byte[maxBufferSize];
int bufferLength;
loaded = 0;
Cursor cursor = activity.getContentResolver().query(uri, null, null, null, null);
cursor.moveToFirst();
String fileNameUTF8 = new String(name.getBytes(), StandardCharsets.ISO_8859_1);
url = new URL(protocol, host, port, root + "/upload.php");
url = checkRedirection(url);
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);
size = in.available();
sizeSI = Transfer.humanReadableByteCountBin(size);
while ((bufferLength = in.read(buffer)) > 0) {
request.write(buffer, 0, bufferLength);
loaded+= bufferLength;
percent = ((loaded * 100) / size);
progressUpdateDelay();
}
request.writeBytes("\r\n");
request.writeBytes("--" + boundary + "--\r\n");
in.close();
int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
message = "File " + name + " successful upload";
state = Transfer.STATE_SUCCESS;
progressEnd();
}
else {
String s = conn.getResponseMessage();
throw new HttpErrorException("error code: " + responseCode + " " + s);
}
request.flush();
request.close();
conn.disconnect();
System.out.println(message);
if (activity != null) {
String finalMessage = message;
activity.runOnUiThread(() ->
Snackbar.make(activity.findViewById(R.id.view_pager), finalMessage, Snackbar.LENGTH_LONG)
.setAction("Action", null).show());
}
return message;
}
}

View File

@ -3,10 +3,14 @@ package com.localtransfer.fragment;
import android.animation.ObjectAnimator;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.os.Environment;
import android.view.ContextMenu;
import android.view.Gravity;
@ -20,14 +24,10 @@ import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.fragment.app.Fragment;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.snackbar.Snackbar;
import com.localtransfer.BuildConfig;
import com.localtransfer.R;
import com.localtransfer.DownloadFile;
import com.localtransfer.Transfer;
import org.json.JSONArray;
@ -35,6 +35,8 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@ -50,7 +52,6 @@ public class DownloadFragment extends Fragment {
}
private View root;
private LayoutInflater inflater;
public static DownloadFragment newInstance() {
DownloadFragment fragment = new DownloadFragment();
@ -67,20 +68,17 @@ public class DownloadFragment extends Fragment {
Bundle savedInstanceState) {
root = inflater.inflate(R.layout.fragment_download, container, false);
this.inflater = inflater;
new Timer().schedule(new TimerTask() {
@Override
public void run() {
listFile();
showFileList();
}
}, 100);
SwipeRefreshLayout refresh = root.findViewById(R.id.refresh);
refresh.setOnRefreshListener(() -> {
listFile();
new Thread(() -> showFileList()).start();
refresh.setRefreshing(false);
});
@ -109,21 +107,21 @@ public class DownloadFragment extends Fragment {
public boolean onContextItemSelected(MenuItem item) {
if (item.getTitle() == "Delete") {
LinearLayout fileDesc = (LinearLayout) root.findViewById(item.getItemId());
DownloadFile file = (DownloadFile) DownloadFile.getFileById((Integer) fileDesc.getTag(R.id.ID_DOWNLOAD));
final String file = String.valueOf(fileDesc.getTag(R.id.ID_FILE_NAME));
new Thread(() -> {
try {
String message = file.deleteFile();
String message = Transfer.deleteFile(file);
System.out.println(message);
getActivity().runOnUiThread(() ->
Transfer.activity.runOnUiThread(() ->
Snackbar.make(getActivity().findViewById(R.id.view_pager), message, Snackbar.LENGTH_LONG)
.setAction("Action", null).show());
listFile();
showFileList();
} catch (IOException e) {
String ExceptionName = e.getClass().getSimpleName();
String ExceptionMess = e.getMessage();
if(ExceptionName != null && ExceptionMess != null) {
Transfer.errorSnackbar(ExceptionMess);
Transfer.error(ExceptionMess);
}
}
@ -135,175 +133,204 @@ public class DownloadFragment extends Fragment {
return true;
}
public void listFile() {
new Thread(() -> {
final TextView title = new TextView(Transfer.activity);
final LinearLayout main_layout = root.findViewById(R.id.main_layout);
try {
String data = Transfer.getFileList();
if (data.equals("[]")) {
title.setTextSize(18);
title.setGravity(Gravity.CENTER);
main_layout.removeAllViews();
main_layout.addView(title);
title.setHeight(1000);
title.setText("No file to download");
}
else {
getActivity().runOnUiThread(() -> {
main_layout.removeAllViews();
final View line = inflater.inflate(R.layout.horizontal_line, null);
main_layout.addView(line);
});
try {
JSONArray array = new JSONArray(data);
for (int i = 0; i < array.length(); i++) {
Integer id = View.generateViewId();
DownloadFile trFile = new DownloadFile(id);
JSONObject row = array.getJSONObject(i);
trFile.name = row.getString("name");
trFile.size = row.getLong("size");
trFile.href = row.getString("href");
trFile.mime = row.getString("mime");
trFile.type = row.getString("type");
getActivity().runOnUiThread(() -> showFileList(trFile));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
final String ExceptionName = e.getClass().getSimpleName();
final String ExceptionMess = e.getMessage();
if(ExceptionName != null && ExceptionMess != null) {
Transfer.errorSnackbar(ExceptionMess);
}
}
}).start();
}
private void setThumbnail(DownloadFile file, ImageView image) {
new Thread(() -> {
InputStream thumbnail = file.getThumbnail();
Bitmap bitmap = BitmapFactory.decodeStream(thumbnail);
getActivity().runOnUiThread(() -> {
image.setImageBitmap(bitmap);
});
}).start();
}
private void showFileList(DownloadFile trFile) {
public void showFileList() {
final TextView title = new TextView(Transfer.activity);
title.setTextSize(18);
title.setGravity(Gravity.CENTER);
final LinearLayout main_layout = root.findViewById(R.id.main_layout);
final LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(getContext().LAYOUT_INFLATER_SERVICE);
View file_info = inflater.inflate(R.layout.file_info, null);
main_layout.addView(file_info);
ImageView image = file_info.findViewById(R.id.file_image);
TextView viewName = file_info.findViewById(R.id.file_name);
TextView viewType = file_info.findViewById(R.id.file_type);
TextView viewSize = file_info.findViewById(R.id.file_size);
LinearLayout fileDesc = file_info.findViewById(R.id.file_desc);
LinearLayout file_buttons = file_info.findViewById(R.id.file_buttons);
String data = "";
fileDesc.setId(View.generateViewId());
try {
data = Transfer.getFileList();
} catch (IOException e) {
final String ExceptionName = e.getClass().getSimpleName();
final String ExceptionMess = e.getMessage();
registerForContextMenu(fileDesc);
if(ExceptionName != null && ExceptionMess != null) {
Transfer.error(ExceptionMess);
Transfer.activity.runOnUiThread(() -> {
if(main_layout != null)
main_layout.removeAllViews();
if(title != null) {
main_layout.addView(title);
title.setText(ExceptionMess);
}
});
}
file_buttons.setId(trFile.getId());
file_buttons.setTag(R.id.ID_DOWNLOAD, trFile.getId());
fileDesc.setTag(R.id.ID_DOWNLOAD, trFile.getId());
file_buttons.setTag(R.id.ID_FILE_BUTTONS, "FOR VISIBILITY");
trFile.setButton(file_buttons.findViewById(R.id.file_download));
viewName.setText(trFile.name);
viewType.setText(trFile.mime);
viewSize.setText(Transfer.humanReadableByteCountBin(trFile.size));
switch (trFile.type) {
case "file-image":
image.setImageResource(R.drawable.ic_icon_image);
setThumbnail(trFile, image);
break;
case "file-video":
image.setImageResource(R.drawable.ic_icon_video);
setThumbnail(trFile, image);
break;
case "file-audio":
image.setImageResource(R.drawable.ic_icon_music);
break;
default:
image.setImageResource(R.drawable.ic_icon_file);
}
if (data.equals("[]")) {
Transfer.activity.runOnUiThread(() -> {
main_layout.removeAllViews();
main_layout.addView(title);
title.setHeight(1000);
title.setText("No file to download");
});
return;
}
fileDesc.setOnClickListener(view -> {
try {
Button Bview = file_buttons.findViewById(R.id.file_view);
Button Bshare = file_buttons.findViewById(R.id.file_share);
Button Bdownload = file_buttons.findViewById(R.id.file_download);
JSONArray array;
array = new JSONArray(data);
Bdownload.setOnClickListener(ListenerDL);
Bview.setOnClickListener(ListenerView);
Bshare.setOnClickListener(ListenerShare);
Transfer.activity.runOnUiThread(() -> {
main_layout.removeAllViews();
final View line = inflater.inflate(R.layout.horizontal_line, null);
main_layout.addView(line);
});
for (int i = 0; i < array.length(); i++) {
JSONObject row = array.getJSONObject(i);
final String name = row.getString("name");
final String href = row.getString("href");
final long size = row.getLong("size");
final String mime = row.getString("mime");
final String type = row.getString("type");
if (trFile.exist(getContext())) {
Bview.setVisibility(LinearLayout.VISIBLE);
Bshare.setVisibility(LinearLayout.VISIBLE);
Bdownload.setVisibility(LinearLayout.GONE);
Transfer.activity.runOnUiThread(() -> {
title.setText("Choose file to download");
View file_info = inflater.inflate(R.layout.file_info, null);
main_layout.addView(file_info);
ImageView image = file_info.findViewById(R.id.file_image);
TextView viewName = file_info.findViewById(R.id.file_name);
TextView viewType = file_info.findViewById(R.id.file_type);
TextView viewSize = file_info.findViewById(R.id.file_size);
LinearLayout fileDesc = file_info.findViewById(R.id.file_desc);
LinearLayout file_buttons = file_info.findViewById(R.id.file_buttons);
fileDesc.setId(View.generateViewId());
file_buttons.setId(View.generateViewId());
file_buttons.setTag(R.id.ID_FILE_BUTTONS, "FOR VISIBILITY");
registerForContextMenu(fileDesc);
fileDesc.setTag(R.id.ID_FILE_NAME, name);
viewName.setText(name);
viewType.setText(mime);
viewSize.setText(Transfer.humanReadableByteCountBin(size));
switch (type) {
case "file-image":
image.setImageResource(R.drawable.ic_icon_image);
break;
case "file-audio":
image.setImageResource(R.drawable.ic_icon_music);
break;
case "file-video":
image.setImageResource(R.drawable.ic_icon_video);
break;
default:
image.setImageResource(R.drawable.ic_icon_file);
}
fileDesc.setOnClickListener(view -> {
String save_location;
if (mime.equals("text/plain"))
save_location = getContext().getCacheDir().toString();
else
save_location = Environment.getExternalStorageDirectory() + "/" + Transfer.local_storage;
File file = new File(save_location, name);
file_buttons.setId(View.generateViewId());
Button Bview = file_buttons.findViewById(R.id.file_view);
Button Bshare = file_buttons.findViewById(R.id.file_share);
Button dl = file_buttons.findViewById(R.id.file_download);
dl.setOnClickListener(ListenerDL);
Bview.setOnClickListener(ListenerView);
Bshare.setOnClickListener(ListenerShare);
if (file.exists() && file.length() == size) {
Bview.setVisibility(LinearLayout.VISIBLE);
Bshare.setVisibility(LinearLayout.VISIBLE);
dl.setVisibility(LinearLayout.GONE);
}
else {
dl.setVisibility(LinearLayout.VISIBLE);
Bview.setVisibility(LinearLayout.GONE);
Bshare.setVisibility(LinearLayout.GONE);
}
if (Transfer.setButton(name, dl)) {
dl.setEnabled(false);
final Drawable spinner = Transfer.activity.getDrawable(R.drawable.ic_spinner_rotate);
int h = spinner.getIntrinsicHeight();
int w = spinner.getIntrinsicWidth();
spinner.setBounds(0, 0, w, h);
dl.setCompoundDrawables(dl.getCompoundDrawables()[0], null, spinner, null);
ObjectAnimator anim = ObjectAnimator.ofInt(spinner, "level", 0, 10000);
anim.setDuration(1000);
anim.setRepeatCount(Animation.INFINITE);
anim.setInterpolator(new LinearInterpolator());
anim.start();
}
file_buttons.setTag(R.id.ID_FILE_NAME, name);
file_buttons.setTag(R.id.ID_FILE_SIZE, size);
file_buttons.setTag(R.id.ID_FILE_HREF, href);
file_buttons.setTag(R.id.ID_FILE_MIME, mime);
file_buttons.setTag(R.id.ID_SAVE_LOCATION, save_location);
int state = file_buttons.getVisibility();
if(state == LinearLayout.GONE) {
for(View child : getAllChildren(main_layout)) {
if(child.getTag(R.id.ID_FILE_BUTTONS) != null)
child.setVisibility(LinearLayout.GONE);
}
file_buttons.setVisibility(LinearLayout.VISIBLE);
}
else if(state == LinearLayout.VISIBLE)
file_buttons.setVisibility(LinearLayout.GONE);
});
});
}
else {
Bdownload.setVisibility(LinearLayout.VISIBLE);
Bview.setVisibility(LinearLayout.GONE);
Bshare.setVisibility(LinearLayout.GONE);
}
trFile.setButton(Bdownload);
int state = file_buttons.getVisibility();
if(state == LinearLayout.GONE) {
for(View child : getAllChildren(main_layout)) {
if(child.getTag(R.id.ID_FILE_BUTTONS) != null)
child.setVisibility(LinearLayout.GONE);
}
file_buttons.setVisibility(LinearLayout.VISIBLE);
}
else if(state == LinearLayout.VISIBLE)
file_buttons.setVisibility(LinearLayout.GONE);
});
} catch (JSONException e) {
e.printStackTrace();
}
}
private View.OnClickListener ListenerDL = v -> {
v.setEnabled(false);
final LinearLayout layout = (LinearLayout) v.getParent();
final Button button = (Button) v;
button.setText(String.format("Download pending"));
final Button button = layout.findViewById(R.id.file_download);
final Integer id = (Integer) layout.getTag(R.id.ID_DOWNLOAD);
final String name = (String) layout.getTag(R.id.ID_FILE_NAME);
final String save_location = String.valueOf(layout.getTag(R.id.ID_SAVE_LOCATION));
final long fileSize = (long) layout.getTag(R.id.ID_FILE_SIZE);
final String href = (String) layout.getTag(R.id.ID_FILE_HREF);
DownloadFile file = (DownloadFile) DownloadFile.getFileById(id);
file.sizeSI = Transfer.humanReadableByteCountBin(file.size);
file.AddTransfer();
new File(save_location).mkdirs();
File file = new File(save_location, name);
Transfer tr = new Transfer(Transfer.TYPE_DOWNLOAD);
tr.file = file;
tr.fileName = name;
tr.fileSize = fileSize;
tr.fileSizeHuman = Transfer.humanReadableByteCountBin(fileSize);
tr.href = href;
tr.button = button;
tr.AddTransfer();
};
private View.OnClickListener ListenerShare = v -> {
LinearLayout layout = (LinearLayout) v.getParent();
final Integer id = (Integer) layout.getTag(R.id.ID_DOWNLOAD);
final String name = (String) layout.getTag(R.id.ID_FILE_NAME);
final String type = (String) layout.getTag(R.id.ID_FILE_MIME);
DownloadFile dl = (DownloadFile) DownloadFile.getFileById(id);
final String save_location = String.valueOf(layout.getTag(R.id.ID_SAVE_LOCATION));
File file = new File(save_location, name);
Uri uri = FileProvider.getUriForFile(getContext(), BuildConfig.APPLICATION_ID, file);
if(dl.mime.equals("text/plain")) {
if(type.equals("text/plain")) {
String text = null;
try {
InputStream is = getActivity().getContentResolver().openInputStream(dl.uri);
InputStream is = new FileInputStream(file);
BufferedReader buf = new BufferedReader(new InputStreamReader(is));
String line;
@ -321,43 +348,47 @@ public class DownloadFragment extends Fragment {
try {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType(dl.mime);
intent.setType(type);
intent.putExtra(Intent.EXTRA_TEXT, text);
startActivity(Intent.createChooser(intent, getString(R.string.share_title)));
} catch (ActivityNotFoundException e) {
e.printStackTrace();
Toast.makeText(getActivity(), "Can't share " + dl.mime, Toast.LENGTH_SHORT).show();
}
}
else {
try {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_STREAM, dl.uri);
intent.setType(dl.mime);
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.setType(type);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intent, getString(R.string.share_title)));
} catch (ActivityNotFoundException e) {
e.printStackTrace();
Toast.makeText(getActivity(), "Can't share " + dl.mime, Toast.LENGTH_SHORT).show();
}
}
};
private View.OnClickListener ListenerView = v -> {
LinearLayout layout = (LinearLayout) v.getParent();
final Integer id = (Integer) layout.getTag(R.id.ID_DOWNLOAD);
final String name = (String) layout.getTag(R.id.ID_FILE_NAME);
final String type = (String) layout.getTag(R.id.ID_FILE_MIME);
DownloadFile dl = (DownloadFile) DownloadFile.getFileById(id);
final String save_location = String.valueOf(layout.getTag(R.id.ID_SAVE_LOCATION));
File file = new File(save_location, name);
Uri uri = FileProvider.getUriForFile(getContext(), BuildConfig.APPLICATION_ID, file);
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(dl.uri, dl.mime);
intent.setDataAndType(uri, type);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
Toast.makeText(getActivity(), "No " + dl.mime + " viewer", Toast.LENGTH_SHORT).show();
}
};
private List<View> getAllChildren(View v) {

View File

@ -2,16 +2,26 @@ package com.localtransfer.fragment;
import android.os.Bundle;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.localtransfer.Progress;
import com.localtransfer.R;
import com.localtransfer.Transfer;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
public class ProgressFragment extends Fragment {
@ -33,20 +43,21 @@ public class ProgressFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
Transfer.fragment_progress = inflater.inflate(R.layout.fragment_progress, container, false);
Progress.root = inflater.inflate(R.layout.fragment_progress, container, false);
return Transfer.fragment_progress;
return Progress.root;
}
@Override
public void onResume(){
super.onResume();
Transfer.fragment_on = true;
Progress.fragment_on = true;
List<Transfer> instances = Transfer.getInstances();
for (Transfer file: instances) {
file.showProgressFragment();
List instances = Transfer.getInstances();
for (Object obj: instances) {
Transfer tr = (Transfer) obj;
Progress.showProgressFragment(tr);
}
}
@ -54,6 +65,6 @@ public class ProgressFragment extends Fragment {
public void onPause(){
super.onPause();
Transfer.fragment_on = false;
Progress.fragment_on = false;
}
}

View File

@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="ID_DOWNLOAD" type="id" />
<item name="ID_FILE_NAME" type="id" />
<item name="ID_FILE_SIZE" type="id" />
<item name="ID_FILE_HREF" type="id" />
<item name="ID_FILE_MIME" type="id" />
<item name="ID_SAVE_LOCATION" type="id" />
<item name="ID_FILE_BUTTONS" type="id" />
</resources>

View File

@ -25,14 +25,13 @@
<string name="server_protocol">Use secure https</string>
<string name="server_host_def">www.netdldata.net</string>
<string name="server_port">Port</string>
<string name="server_port_def"></string>
<string name="server_port_def">80</string>
<string name="server_root">Root</string>
<string name="server_root_def">/transfer</string>
<!-- Local Preferences -->
<string name="use_shared_storage">Use shared storage</string>
<string name="shared_storage">Shared Storage</string>
<string name="shared_storage_def">Download/TransferLocal</string>
<string name="local_storage">Local Storage</string>
<string name="local_storage_def">Download/TransferLocal</string>
<!-- File buttons -->
<string name="file_share">Share</string>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">netdldata.net</domain>
</domain-config>
</network-security-config>

View File

@ -31,17 +31,11 @@
<PreferenceCategory app:title="@string/local_header">
<SwitchPreference
android:defaultValue="false"
android:key="use_shared_storage"
android:title="@string/use_shared_storage" />
<Preference
app:key="shared_storage"
app:title="@string/shared_storage"
app:defaultValue="@string/shared_storage_def"
app:useSimpleSummaryProvider="true"
app:isPreferenceVisible="false"/>
app:key="local_storage"
app:title="@string/local_storage"
app:defaultValue="@string/local_storage_def"
app:useSimpleSummaryProvider="true" />
</PreferenceCategory>

View File

@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.1'
classpath 'com.android.tools.build:gradle:4.1.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip