Commit 5b0c10b8 authored by akwizgran's avatar akwizgran

Upgrade dependencies and target API, fix lint warnings.

parent 5aef4df0
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
compileSdkVersion 28
buildToolsVersion "28.0.3"
defaultConfig {
applicationId "org.briarproject.snooze"
minSdkVersion 14
targetSdkVersion 23
targetSdkVersion 28
versionCode 10000
versionName "1.0.0"
}
}
dependencies {
compile 'com.android.support:appcompat-v7:23.2.1'
compile 'com.android.support:design:23.2.1'
compile 'org.jetbrains:annotations-java5:15.0'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'org.jetbrains:annotations-java5:15.0'
}
......@@ -31,4 +31,6 @@ public interface Constants {
int CONNECT_TIMEOUT_MS = 30 * 1000;
int RECONNECT_DELAY_MS = 10 * 1000;
int DEFAULT_PORT = 9999;
}
......@@ -38,6 +38,7 @@ class FileLogger extends Thread {
PrintStream out = null;
try {
// Load any existing messages and pass them to the callback
//noinspection ResultOfMethodCallIgnored
logFile.createNewFile();
in = new FileInputStream(logFile);
Scanner scanner = new Scanner(in);
......
......@@ -2,7 +2,9 @@ package org.briarproject.snooze;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
......@@ -26,12 +28,14 @@ public class LogFragment extends Fragment implements LogListener {
private volatile Logger log = null;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
log = ((SnoozeApplication) getContext().getApplicationContext()).getLogger();
Context ctx = getContext();
if (ctx == null) return null;
log = ((SnoozeApplication) ctx.getApplicationContext()).getLogger();
View layout = inflater.inflate(R.layout.fragment_log, container, false);
outputScrollView = (ScrollView) layout.findViewById(R.id.outputScrollView);
outputTextView = (TextView) layout.findViewById(R.id.outputTextView);
outputScrollView = layout.findViewById(R.id.outputScrollView);
outputTextView = layout.findViewById(R.id.outputTextView);
layout.findViewById(R.id.clearLogButton).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
......@@ -77,7 +81,10 @@ public class LogFragment extends Fragment implements LogListener {
}
private void onClickCopyLogButton() {
ClipboardManager cp = (ClipboardManager) getContext().getSystemService(CLIPBOARD_SERVICE);
Context ctx = getContext();
if (ctx == null) return;
ClipboardManager cp = (ClipboardManager) ctx.getSystemService(CLIPBOARD_SERVICE);
if (cp == null) return;
String text = outputTextView.getText().toString();
cp.setPrimaryClip(ClipData.newPlainText(getString(R.string.app_name), text));
Toast.makeText(getContext(), R.string.copied_to_clipboard, LENGTH_SHORT).show();
......
......@@ -47,6 +47,7 @@ class Logger implements FileLogger.LogLoadedCallback {
log("API version: " + Build.VERSION.SDK_INT);
if (Build.VERSION.SDK_INT >= 23) {
PowerManager pm = (PowerManager) appContext.getSystemService(POWER_SERVICE);
if (pm == null) throw new AssertionError();
boolean ignoring = pm.isIgnoringBatteryOptimizations(appContext.getPackageName());
log("Ignoring battery optimizations: " + ignoring);
}
......
package org.briarproject.snooze;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.PowerManager;
......@@ -40,6 +41,7 @@ class NetworkClient extends Thread {
this.keepaliveBytes = keepaliveBytes;
log = ((SnoozeApplication) appContext).getLogger();
PowerManager pm = (PowerManager) appContext.getSystemService(POWER_SERVICE);
if (pm == null) throw new AssertionError();
wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, LOCK_TAG);
wakeLock.setReferenceCounted(false);
lastActive = System.currentTimeMillis();
......@@ -70,6 +72,7 @@ class NetworkClient extends Thread {
}
}
@SuppressLint("WakelockTimeout")
@Override
public void run() {
wakeLock.acquire();
......
package org.briarproject.snooze;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
......@@ -22,8 +23,10 @@ import android.widget.TextView;
import org.briarproject.snooze.alarm.AlarmType;
import static android.content.Context.INPUT_METHOD_SERVICE;
import static org.briarproject.snooze.Constants.ACTION_START;
import static org.briarproject.snooze.Constants.ACTION_STOP;
import static org.briarproject.snooze.Constants.DEFAULT_PORT;
import static org.briarproject.snooze.Constants.EXTRA_ALARM_INTERVAL_MS;
import static org.briarproject.snooze.Constants.EXTRA_ALARM_TYPE;
import static org.briarproject.snooze.Constants.EXTRA_FOREGROUND_SERVICE;
......@@ -41,13 +44,15 @@ public class SettingsFragment extends Fragment {
private TextView alarmIntervalTextView, keepaliveIntervalTextView, keepaliveBytesTextView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Context ctx = getContext();
if (ctx == null) return null;
View layout = inflater.inflate(R.layout.fragment_settings, container, false);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getContext(),
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(ctx,
R.array.alarm_types, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Spinner alarmTypeSpinner = (Spinner) layout.findViewById(R.id.alarmTypeSpinner);
Spinner alarmTypeSpinner = layout.findViewById(R.id.alarmTypeSpinner);
alarmTypeSpinner.setAdapter(adapter);
alarmTypeSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
......@@ -59,7 +64,7 @@ public class SettingsFragment extends Fragment {
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
alarmIntervalTextView = (TextView) layout.findViewById(R.id.alarmIntervalTextView);
alarmIntervalTextView = layout.findViewById(R.id.alarmIntervalTextView);
alarmIntervalTextView.setText(getString(R.string.interval, 1));
((SeekBar) layout.findViewById(R.id.alarmIntervalSeekBar)).setOnSeekBarChangeListener(
new OnSeekBarChangeListener() {
......@@ -76,7 +81,7 @@ public class SettingsFragment extends Fragment {
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
keepaliveIntervalTextView = (TextView) layout.findViewById(R.id.keepaliveIntervalTextView);
keepaliveIntervalTextView = layout.findViewById(R.id.keepaliveIntervalTextView);
keepaliveIntervalTextView.setText(getString(R.string.interval, 1));
((SeekBar) layout.findViewById(R.id.keepaliveIntervalSeekBar)).setOnSeekBarChangeListener(
new OnSeekBarChangeListener() {
......@@ -94,7 +99,7 @@ public class SettingsFragment extends Fragment {
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
keepaliveBytesTextView = (TextView) layout.findViewById(R.id.keepaliveBytesTextView);
keepaliveBytesTextView = layout.findViewById(R.id.keepaliveBytesTextView);
keepaliveBytesTextView.setText("1");
((SeekBar) layout.findViewById(R.id.keepaliveBytesSeekBar)).setOnSeekBarChangeListener(
new OnSeekBarChangeListener() {
......@@ -143,10 +148,18 @@ public class SettingsFragment extends Fragment {
}
private void onClickApplySettingsButton() {
Context ctx = getContext();
if (ctx == null) return;
View layout = getView();
if (layout == null) return;
String ipAddress = ((EditText) layout.findViewById(R.id.ipEntry)).getText().toString();
String portString = ((EditText) layout.findViewById(R.id.portEntry)).getText().toString();
int portNumber = Integer.parseInt(portString);
int portNumber;
try {
portNumber = Integer.parseInt(portString);
} catch (NumberFormatException e) {
portNumber = DEFAULT_PORT;
}
boolean foreground =
((Switch) layout.findViewById(R.id.foregroundServiceSwitch)).isChecked();
boolean wakeLock = ((Switch) layout.findViewById(R.id.wakeLockSwitch)).isChecked();
......@@ -157,9 +170,9 @@ public class SettingsFragment extends Fragment {
((SeekBar) layout.findViewById(R.id.keepaliveIntervalSeekBar)).getProgress() + 1;
int keepaliveBytes =
((SeekBar) layout.findViewById(R.id.keepaliveBytesSeekBar)).getProgress() + 1;
Intent i = new Intent(getContext(), SnoozeService.class);
Intent i = new Intent(ctx, SnoozeService.class);
i.setAction(ACTION_START);
i.putExtra(EXTRA_IP_ADDRESS, ipAddress);
if (!ipAddress.isEmpty()) i.putExtra(EXTRA_IP_ADDRESS, ipAddress);
i.putExtra(EXTRA_PORT_NUMBER, portNumber);
i.putExtra(EXTRA_FOREGROUND_SERVICE, foreground);
i.putExtra(EXTRA_WAKE_LOCK, wakeLock);
......@@ -168,18 +181,22 @@ public class SettingsFragment extends Fragment {
i.putExtra(EXTRA_KEEPALIVE_INTERVAL_MS, keepaliveIntervalMins * 60 * 1000);
i.putExtra(EXTRA_KEEPALIVE_BYTES, keepaliveBytes);
i.putExtra(EXTRA_ALARM_TYPE, alarmType.name());
getContext().startService(i);
ctx.startService(i);
}
private void onClickStopServiceButton() {
Intent i = new Intent(getContext(), SnoozeService.class);
Context ctx = getContext();
if (ctx == null) return;
Intent i = new Intent(ctx, SnoozeService.class);
i.setAction(ACTION_STOP);
getContext().startService(i);
ctx.startService(i);
}
public void hideKeyboard(View view) {
InputMethodManager inputMethodManager = (InputMethodManager)
getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
Context ctx = getContext();
if (ctx == null) return;
InputMethodManager imm = (InputMethodManager) ctx.getSystemService(INPUT_METHOD_SERVICE);
if (imm == null) return;
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
......@@ -20,7 +20,7 @@ public class SnoozeActivity extends AppCompatActivity {
setContentView(R.layout.activity_snooze);
String[] pageTitles = {getString(R.string.settings), getString(R.string.log)};
TabAdapter adapter = new TabAdapter(getSupportFragmentManager(), pageTitles);
ViewPager viewPager = ((ViewPager) findViewById(R.id.viewPager));
ViewPager viewPager = findViewById(R.id.viewPager);
viewPager.setAdapter(adapter);
((TabLayout) findViewById(R.id.tabLayout)).setupWithViewPager(viewPager);
}
......
package org.briarproject.snooze;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.PowerManager;
import android.support.v4.content.WakefulBroadcastReceiver;
import android.os.PowerManager.WakeLock;
import static android.content.Context.CONNECTIVITY_SERVICE;
import static android.content.Context.POWER_SERVICE;
......@@ -22,8 +23,14 @@ import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.os.BatteryManager.EXTRA_LEVEL;
import static android.os.BatteryManager.EXTRA_PLUGGED;
import static android.os.BatteryManager.EXTRA_SCALE;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
import static android.os.PowerManager.ACTION_POWER_SAVE_MODE_CHANGED;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
class SnoozeBroadcastReceiver extends WakefulBroadcastReceiver {
class SnoozeBroadcastReceiver extends BroadcastReceiver {
private static final String LOCK_TAG = SnoozeBroadcastReceiver.class.getName();
IntentFilter getIntentFilter() {
IntentFilter filter = new IntentFilter();
......@@ -32,17 +39,29 @@ class SnoozeBroadcastReceiver extends WakefulBroadcastReceiver {
filter.addAction(ACTION_BATTERY_CHANGED);
filter.addAction(ACTION_POWER_CONNECTED);
filter.addAction(ACTION_POWER_DISCONNECTED);
if (Build.VERSION.SDK_INT >= 21)
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
if (Build.VERSION.SDK_INT >= 23)
filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
if (SDK_INT >= 21) filter.addAction(ACTION_POWER_SAVE_MODE_CHANGED);
if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED);
filter.addAction(ACTION_AIRPLANE_MODE_CHANGED);
filter.addAction(CONNECTIVITY_ACTION);
return filter;
}
@SuppressLint("WakelockTimeout")
@Override
public void onReceive(Context ctx, Intent i) {
PowerManager pm = (PowerManager) ctx.getSystemService(POWER_SERVICE);
if (pm == null) throw new AssertionError();
WakeLock wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, LOCK_TAG);
wakeLock.setReferenceCounted(false);
wakeLock.acquire();
try {
onReceiveLocked(ctx, i);
} finally {
wakeLock.release();
}
}
private void onReceiveLocked(Context ctx, Intent i) {
Logger log = ((SnoozeApplication) ctx.getApplicationContext()).getLogger();
String action = i.getAction();
if (ACTION_SCREEN_ON.equals(action)) {
......@@ -58,19 +77,20 @@ class SnoozeBroadcastReceiver extends WakefulBroadcastReceiver {
log.log("Power connected");
} else if (ACTION_POWER_DISCONNECTED.equals(action)) {
log.log("Power disconnected");
} else if (Build.VERSION.SDK_INT >= 21
&& PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
} else if (SDK_INT >= 21 && ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
PowerManager pm = (PowerManager) ctx.getSystemService(POWER_SERVICE);
if (pm == null) throw new AssertionError();
log.log("Power save mode: " + pm.isPowerSaveMode());
} else if (Build.VERSION.SDK_INT >= 23
&& PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
} else if (SDK_INT >= 23 && ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
PowerManager pm = (PowerManager) ctx.getSystemService(POWER_SERVICE);
if (pm == null) throw new AssertionError();
log.log("Idle mode: " + pm.isDeviceIdleMode());
} else if (ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
log.log("Airplane mode: " + i.getBooleanExtra("state", false));
} else if (CONNECTIVITY_ACTION.equals(action)) {
ConnectivityManager cm =
(ConnectivityManager) ctx.getSystemService(CONNECTIVITY_SERVICE);
if (cm == null) throw new AssertionError();
NetworkInfo net = cm.getActiveNetworkInfo();
boolean online = net != null && net.isConnected();
boolean wifi = net != null && net.getType() == TYPE_WIFI;
......
package org.briarproject.snooze;
import android.annotation.SuppressLint;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.WifiLock;
import android.os.Binder;
import android.os.IBinder;
import android.os.PowerManager;
import android.support.v7.app.NotificationCompat;
import android.os.PowerManager.WakeLock;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;
import org.briarproject.snooze.alarm.Alarm;
import org.briarproject.snooze.alarm.AlarmFactory;
import org.briarproject.snooze.alarm.AlarmType;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.net.wifi.WifiManager.WIFI_MODE_FULL;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static android.support.v4.app.NotificationCompat.CATEGORY_SERVICE;
import static android.widget.Toast.LENGTH_SHORT;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.snooze.Constants.ACTION_ALARM;
import static org.briarproject.snooze.Constants.ACTION_START;
import static org.briarproject.snooze.Constants.ACTION_STOP;
import static org.briarproject.snooze.Constants.DEFAULT_PORT;
import static org.briarproject.snooze.Constants.EXTRA_ALARM_INTERVAL_MS;
import static org.briarproject.snooze.Constants.EXTRA_ALARM_TYPE;
import static org.briarproject.snooze.Constants.EXTRA_FOREGROUND_SERVICE;
......@@ -37,13 +46,14 @@ import static org.briarproject.snooze.alarm.AlarmType.STOP;
public class SnoozeService extends Service {
private static final String LOCK_TAG = SnoozeService.class.getName();
private static final String NOTIFICATION_CHANNEL_ID = "Snooze";
private final Binder binder = new Binder();
private final SnoozeBroadcastReceiver receiver = new SnoozeBroadcastReceiver();
private boolean foreground = false;
private PowerManager.WakeLock wakeLock = null;
private WifiManager.WifiLock wifiLock = null;
private WakeLock wakeLock = null;
private WifiLock wifiLock = null;
private NetworkClient networkClient = null;
private Alarm alarm = null;
private int alarmIntervalMs, keepaliveIntervalMs, keepaliveBytes, port;
......@@ -62,13 +72,21 @@ public class SnoozeService extends Service {
log = ((SnoozeApplication) getApplication()).getLogger();
log.log("Service created");
registerReceiver(receiver, receiver.getIntentFilter());
if (SDK_INT >= 26) {
NotificationChannel nc = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
getString(R.string.ongoing_notification_title), IMPORTANCE_DEFAULT);
NotificationManager nm = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
if (nm == null) throw new AssertionError();
nm.createNotificationChannel(nc);
}
}
@Override
public int onStartCommand(Intent i, int flags, int startId) {
if (ACTION_START.equals(i.getAction())) {
host = i.getStringExtra(EXTRA_IP_ADDRESS);
port = i.getIntExtra(EXTRA_PORT_NUMBER, 9999);
port = i.getIntExtra(EXTRA_PORT_NUMBER, DEFAULT_PORT);
boolean newForeground = i.getBooleanExtra(EXTRA_FOREGROUND_SERVICE, false);
boolean newWakeLock = i.getBooleanExtra(EXTRA_WAKE_LOCK, false);
boolean newWifiLock = i.getBooleanExtra(EXTRA_WIFI_LOCK, false);
......@@ -83,7 +101,7 @@ public class SnoozeService extends Service {
else if (!newWakeLock && wakeLock != null) releaseWakeLock();
if (newWifiLock && wifiLock == null) acquireWifiLock();
else if (!newWifiLock && wifiLock != null) releaseWifiLock();
startNetworkThread();
if (host != null) startNetworkThread();
setAlarm(newAlarmType);
Toast.makeText(this, R.string.service_started, LENGTH_SHORT).show();
} else if (ACTION_STOP.equals(i.getAction())) {
......@@ -122,11 +140,13 @@ public class SnoozeService extends Service {
private void startForegroundService() {
if (foreground) throw new IllegalStateException();
log.log("Starting foreground service");
NotificationCompat.Builder b = new NotificationCompat.Builder(this);
NotificationCompat.Builder b =
new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
b.setSmallIcon(R.drawable.ongoing_notification_icon);
b.setContentTitle(getText(R.string.ongoing_notification_title));
b.setContentText(getText(R.string.ongoing_notification_text));
b.setWhen(0); // Don't show the time
b.setCategory(CATEGORY_SERVICE);
b.setOngoing(true);
Intent i = new Intent(this, SnoozeActivity.class);
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
......@@ -142,10 +162,12 @@ public class SnoozeService extends Service {
foreground = false;
}
@SuppressLint("WakelockTimeout")
private void acquireWakeLock() {
if (wakeLock != null) throw new IllegalStateException();
log.log("Acquiring wake lock");
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
if (pm == null) throw new AssertionError();
wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, LOCK_TAG);
wakeLock.setReferenceCounted(false);
wakeLock.acquire();
......@@ -162,6 +184,7 @@ public class SnoozeService extends Service {
if (wifiLock != null) throw new IllegalStateException();
log.log("Acquiring wifi lock");
WifiManager wm = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
if (wm == null) return;
wifiLock = wm.createWifiLock(WIFI_MODE_FULL, LOCK_TAG);
wifiLock.setReferenceCounted(false);
wifiLock.acquire();
......
<resources>
<string-array name="alarm_types">
<resources xmlns:tools="http://schemas.android.com/tools">
<string-array name="alarm_types" tools:ignore="InconsistentArrays">
<item>No alarm</item>
<item>Java Timer</item>
<item>Java ScheduledExecutorService</item>
......
<resources>
<string-array name="alarm_types">
<resources xmlns:tools="http://schemas.android.com/tools">
<string-array name="alarm_types" tools:ignore="InconsistentArrays">
<item>No alarm</item>
<item>Java Timer</item>
<item>Java ScheduledExecutorService</item>
......
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name">Snooze</string>
<string name="ongoing_notification_title">Snooze is running</string>
<string name="ongoing_notification_text">Touch to open</string>
......@@ -16,7 +16,7 @@
<string name="keepalive_bytes">Keepalive bytes</string>
<string name="interval">%d min</string>
<string name="alarm_type">Alarm type</string>
<string-array name="alarm_types">
<string-array name="alarm_types" tools:ignore="InconsistentArrays">
<item>No alarm</item>
<item>Java Timer</item>
<item>Java ScheduledExecutorService</item>
......
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
classpath 'com.android.tools.build:gradle:3.2.1'
}
}
allprojects {
repositories {
jcenter()
google()
}
}
......
......@@ -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-3.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment