diff --git a/app/src/main/java/com/denizd/substitutionplan/CardAdapter.kt b/app/src/main/java/com/denizd/substitutionplan/CardAdapter.kt index 391405b..a02276e 100644 --- a/app/src/main/java/com/denizd/substitutionplan/CardAdapter.kt +++ b/app/src/main/java/com/denizd/substitutionplan/CardAdapter.kt @@ -9,7 +9,7 @@ import android.widget.TextView import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView -class CardAdapter (var mSubst: List) : RecyclerView.Adapter() { +class CardAdapter(private var mSubst: List) : RecyclerView.Adapter() { var colour = 0 var colorCheck = "" @@ -33,7 +33,7 @@ class CardAdapter (var mSubst: List) : RecyclerView.Adapter) : RecyclerView.Adapter) { + fun setSubst(subst: List) { mSubst = subst notifyDataSetChanged() } diff --git a/app/src/main/java/com/denizd/substitutionplan/DataFetcher.kt b/app/src/main/java/com/denizd/substitutionplan/DataFetcher.kt new file mode 100644 index 0000000..15953e3 --- /dev/null +++ b/app/src/main/java/com/denizd/substitutionplan/DataFetcher.kt @@ -0,0 +1,222 @@ +package com.denizd.substitutionplan + +import android.app.* +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.graphics.Color +import android.media.RingtoneManager +import android.os.AsyncTask +import android.os.Build +import android.preference.PreferenceManager +import android.view.View +import android.widget.RemoteViews +import androidx.core.app.NotificationCompat +import com.madapps.prefrences.EasyPrefrences +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import org.jsoup.select.Elements +import java.lang.IndexOutOfBoundsException +import java.lang.NullPointerException +import java.net.URL +import java.net.URLConnection + +class DataFetcher(isjobservice: Boolean, context: Context, application: Application, view: View) : AsyncTask() { + + var jobservice = isjobservice + var mContext = context + var mApplication = application + var mView = view + var priority = 200 + var notifText = "" + var informational = "" + var foodList = ArrayList() + var successfulAttempt = false + lateinit var connection: URLConnection + lateinit var rows: Elements + lateinit var paragraphs: Elements + lateinit var foodElements: Elements + lateinit var doc: Document + lateinit var docFood: Document + var modified = "" + var manager = mContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val channelId = "general" + var channelName = mContext.getString(R.string.general) + val importance = NotificationManager.IMPORTANCE_DEFAULT + lateinit var channel: NotificationChannel + lateinit var notification: Notification + val prefs = PreferenceManager.getDefaultSharedPreferences(mContext) as SharedPreferences + val edit = prefs.edit() as SharedPreferences.Editor + val easyPrefs = EasyPrefrences(mContext) + val substViewModel = SubstViewModel(mApplication) + + override fun doInBackground(vararg params: Void?): Void? { + + try { + doc = Jsoup.connect("https://djd4rkn355.github.io/subst").get() + connection = URL("https://djd4rkn355.github.io/subst").openConnection() + modified = connection.getHeaderField("Last-Modified") + rows = doc.select("tr") + paragraphs = doc.select("p") + + var groupS = arrayOfNulls(rows.size) + var dateS = arrayOfNulls(rows.size) + var timeS = arrayOfNulls(rows.size) + var courseS = arrayOfNulls(rows.size) + var roomS = arrayOfNulls(rows.size) + var additionalS = arrayOfNulls(rows.size) + + docFood = Jsoup.connect("https://djd4rkn355.github.io/food.html").get() + foodElements = docFood.select("th") + + successfulAttempt = true + + for (i in 0 until paragraphs.size) { + if (i == 0) { + informational = paragraphs[i].text() + } else { + informational += "\n\n" + paragraphs[i].text() + } + } + edit.putString("informational", informational).apply() + + if (successfulAttempt) { + var foodInt = 0 + while (foodInt in 0 until foodElements.size) { + try { // what a mess this is! + if (foodElements[foodInt].text().contains("Montag") || + foodElements[foodInt].text().contains("Dienstag") || + foodElements[foodInt].text().contains("Mittwoch") || + foodElements[foodInt].text().contains("Donnerstag") || + foodElements[foodInt].text().contains("Freitag")) { + + if (foodElements[foodInt + 3].text().contains("Montag") || + foodElements[foodInt + 3].text().contains("Dienstag") || + foodElements[foodInt + 3].text().contains("Mittwoch") || + foodElements[foodInt + 3].text().contains("Donnerstag") || + foodElements[foodInt + 3].text().contains("Freitag") || + foodElements[foodInt + 3].text().contains("von")) { + foodList.add(foodElements[foodInt].text() + "\n" + + foodElements[foodInt + 1].text() + "\n" + + foodElements[foodInt + 2].text()) + foodInt += 2 + } else if (foodElements[foodInt + 2].text().contains("Montag") || + foodElements[foodInt + 2].text().contains("Dienstag") || + foodElements[foodInt + 2].text().contains("Mittwoch") || + foodElements[foodInt + 2].text().contains("Donnerstag") || + foodElements[foodInt + 2].text().contains("Freitag") || + foodElements[foodInt + 2].text().contains("von")) { + foodList.add(foodElements[foodInt].text() + "\n" + + foodElements[foodInt + 1].text()) + foodInt += 1 + } + + } else { + foodList.add(foodElements[foodInt].text()) + } + } catch (e: IndexOutOfBoundsException) { + try { + foodList.add(foodElements[foodInt].text() + "\n" + + foodElements[foodInt + 1].text() + "\n" + + foodElements[foodInt + 2].text()) + break + } catch (e1: IndexOutOfBoundsException) { + foodList.add(foodElements[foodInt].text() + "\n" + + foodElements[foodInt + 1].text()) + break + } + } + foodInt++ + } + + easyPrefs.putListString("foodListPrefs", foodList) + + substViewModel.deleteAllSubst() + + for (i in 0 until rows.size) { + val row = rows[i] + val cols = row.select("th") as Elements + + groupS[i] = cols[0].text() + dateS[i] = cols[1].text() + timeS[i] = cols[2].text() + courseS[i] = cols[3].text() + roomS[i] = cols[4].text() + additionalS[i] = cols[5].text() + + val md = MiscData() + val drawable = md.getIcon(courseS[i].toString()) + val subst = Subst(drawable, groupS[i].toString(), dateS[i].toString(), timeS[i].toString(), courseS[i].toString(), + roomS[i].toString(), additionalS[i].toString(), priority) + priority-- + substViewModel.insert(subst) + + if (jobservice) { + if (prefs.getString("courses", "").isEmpty() && prefs.getString("classes", "").isNotEmpty()) { + if (groupS[i].toString().isNotEmpty() && !groupS[i].equals("")) { + if (prefs.getString("classes", "").contains(groupS[i].toString()) || groupS[i].toString().contains(prefs.getString("classes", "").toString())) { + if (notifText.isNotEmpty()) { + notifText += ", " + } + notifText += courseS[i] + ": " + additionalS[i] + } + } + } else if (prefs.getString("classes", "").isNotEmpty() && prefs.getString("courses", "").isNotEmpty()) { + if (!groupS[i].equals("") && !courseS[i].equals("")) { + if (prefs.getString("courses", "").contains(courseS[i].toString())) { + if (prefs.getString("classes", "").contains(groupS[i].toString()) || groupS[i].toString().contains(prefs.getString("classes", "").toString())) { + if (notifText.isNotEmpty()) { + notifText += ", " + } + notifText += courseS[i] + ": " + additionalS[i] + } + } + } + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + channel = NotificationChannel(channelId, channelName, importance) + channel.enableLights(true) + channel.lightColor = Color.BLUE + manager.createNotificationChannel(channel) + } + + val openApp = Intent(mContext, Main::class.java) + openApp.flags = Intent.FLAG_ACTIVITY_NEW_TASK + openApp.flags += Intent.FLAG_ACTIVITY_CLEAR_TASK + val openAppPending = PendingIntent.getActivity(mContext, 0, openApp, 0) // PendingIntent.FLAG_ONE_SHOT + + val notificationLayout = RemoteViews(mContext.packageName, R.layout.notification) + notificationLayout.setTextViewText(R.id.notification_title, mContext.getString(R.string.subst)) + notificationLayout.setTextViewText(R.id.notification_textview, notifText) + + if (notifText.isNotEmpty()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + notification = NotificationCompat.Builder(mContext) // TODO switch out the deprecated notification delivery method + .setStyle(NotificationCompat.DecoratedCustomViewStyle()) + .setCustomContentView(notificationLayout) + .setSmallIcon(R.drawable.ic_avh) + .setChannelId(channelId) + .setContentIntent(openAppPending) + .setAutoCancel(true) + .build() + } else { + notification = NotificationCompat.Builder(mContext) + .setStyle(NotificationCompat.DecoratedCustomViewStyle()) + .setCustomContentView(notificationLayout) + .setSmallIcon(R.drawable.ic_avh) + .setContentIntent(openAppPending) + .setAutoCancel(true) + .build() + } + notification.sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) + manager.notify(1, notification) // TODO notification plays multiple times + } + + } + } + } + } catch (e: NullPointerException) {} + return null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/denizd/substitutionplan/FragmentPersonal.java b/app/src/main/java/com/denizd/substitutionplan/FragmentPersonal.java index 308dbaf..fdff9cc 100644 --- a/app/src/main/java/com/denizd/substitutionplan/FragmentPersonal.java +++ b/app/src/main/java/com/denizd/substitutionplan/FragmentPersonal.java @@ -263,7 +263,7 @@ public class FragmentPersonal extends Fragment { additionalS[i] = cols.get(5).text(); progressBar.incrementProgressBy(1); - DataGetter dg = new DataGetter(); + MiscData dg = new MiscData(); int drawable = dg.getIcon(courseS[i]); Subst subst = new Subst(drawable, groupS[i], dateS[i], timeS[i], courseS[i], roomS[i], additionalS[i], priority); substViewModel.insert(subst); @@ -290,49 +290,49 @@ public class FragmentPersonal extends Fragment { @Override protected void onPostExecute(Void result) { - if (attempt) { - final Animation fadeOut = AnimationUtils.loadAnimation(getContext(), R.anim.fade_out); + try { + if (attempt) { + final Animation fadeOut = AnimationUtils.loadAnimation(getContext(), R.anim.fade_out); - final Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() { - progressBar.startAnimation(fadeOut); - } - }, 200); + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + progressBar.startAnimation(fadeOut); + } + }, 200); - fadeOut.setAnimationListener(new Animation.AnimationListener() { - @Override - public void onAnimationStart(Animation arg0) { - } + fadeOut.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation arg0) { + } - @Override - public void onAnimationRepeat(Animation arg0) { - } + @Override + public void onAnimationRepeat(Animation arg0) { + } - @Override - public void onAnimationEnd(Animation arg0) { - progressBar.setProgress(0); - } - }); + @Override + public void onAnimationEnd(Animation arg0) { + progressBar.setProgress(0); + } + }); - sdf.applyPattern(NEW_FORMAT); - newDateString = sdf.format(d); - View contextView = getView().getRootView().findViewById(R.id.coordination); - Snackbar snackbar = make(contextView, getText(R.string.lastupdated) + ": " + newDateString, Snackbar.LENGTH_LONG) - .setAction("Action", null); - snackbar.show(); + sdf.applyPattern(NEW_FORMAT); + newDateString = sdf.format(d); + View contextView = getView().getRootView().findViewById(R.id.coordination); + Snackbar snackbar = make(contextView, getText(R.string.lastupdated) + ": " + newDateString, Snackbar.LENGTH_LONG) + .setAction("Action", null); + snackbar.show(); - pullToRefresh.setRefreshing(false); - } else if (npe) { - pullToRefresh.setRefreshing(false); - View contextView = getView().getRootView().findViewById(R.id.coordination); - Snackbar snackbar = make(contextView, getText(R.string.nointernet), Snackbar.LENGTH_LONG) - .setAction("Action", null); - snackbar.show(); - } + pullToRefresh.setRefreshing(false); + } else if (npe) { + pullToRefresh.setRefreshing(false); + View contextView = getView().getRootView().findViewById(R.id.coordination); + Snackbar snackbar = make(contextView, getText(R.string.nointernet), Snackbar.LENGTH_LONG) + .setAction("Action", null); + snackbar.show(); + } + } catch (NullPointerException ignored) {} } - - } } diff --git a/app/src/main/java/com/denizd/substitutionplan/FragmentPlan.java b/app/src/main/java/com/denizd/substitutionplan/FragmentPlan.java index d83e1d5..f8f148b 100644 --- a/app/src/main/java/com/denizd/substitutionplan/FragmentPlan.java +++ b/app/src/main/java/com/denizd/substitutionplan/FragmentPlan.java @@ -101,7 +101,7 @@ public class FragmentPlan extends Fragment { if (prefs.getInt("firstTimeOpening", 0) == 2) { if (!prefs.getBoolean("notif", false)) { pullToRefresh.setRefreshing(true); - new fetcher().execute(); + new DataFetcher(false, getContext(), getActivity().getApplication(), getView().getRootView()).execute(); edit.putInt("firstTimeOpening", 3); edit.apply(); } @@ -109,7 +109,7 @@ public class FragmentPlan extends Fragment { if (prefs.getBoolean("autoRefresh", false)) { pullToRefresh.setRefreshing(true); - new fetcher().execute(); + new DataFetcher(false, getContext(), getActivity().getApplication(), getView().getRootView()).execute(); bottomSheetText.setText(prefs.getString("informational", getString(R.string.noinfo))); } substViewModel = ViewModelProviders.of(getActivity()).get(SubstViewModel.class); @@ -127,7 +127,7 @@ public class FragmentPlan extends Fragment { @Override public void onRefresh() { pullToRefresh.setRefreshing(true); - new fetcher().execute(); + new DataFetcher(true, getContext(), getActivity().getApplication(), getView().getRootView()).execute(); // TODO test for notification bottomSheetText.setText(prefs.getString("informational", getString(R.string.noinfo))); } }); @@ -206,7 +206,7 @@ public class FragmentPlan extends Fragment { additionalS[i] = cols.get(5).text(); progressBar.incrementProgressBy(1); - DataGetter dg = new DataGetter(); + MiscData dg = new MiscData(); int drawable = dg.getIcon(courseS[i]); Subst subst = new Subst(drawable, groupS[i], dateS[i], timeS[i], courseS[i], roomS[i], additionalS[i], priority); substViewModel.insert(subst); @@ -267,9 +267,7 @@ public class FragmentPlan extends Fragment { .setAction("Action", null); snackbar.show(); } - } catch (NullPointerException e) { - - } + } catch (NullPointerException ignored) {} } diff --git a/app/src/main/java/com/denizd/substitutionplan/FragmentSettings.java b/app/src/main/java/com/denizd/substitutionplan/FragmentSettings.java index cd85c29..ee05f58 100644 --- a/app/src/main/java/com/denizd/substitutionplan/FragmentSettings.java +++ b/app/src/main/java/com/denizd/substitutionplan/FragmentSettings.java @@ -716,10 +716,10 @@ public class FragmentSettings extends Fragment implements View.OnClickListener { // try { // double boyP = 0, girlP = 0, perc = 0; // if (boyT.getText().toString().isEmpty() || boy.isChecked()) { -// boyT.setText(DataGetter.boy[gen.nextInt(DataGetter.boy.length)]); +// boyT.setText(MiscData.boy[gen.nextInt(MiscData.boy.length)]); // } // if (girlT.getText().toString().isEmpty() || girl.isChecked()) { -// girlT.setText(DataGetter.girl[gen.nextInt(DataGetter.girl.length)]); +// girlT.setText(MiscData.girl[gen.nextInt(MiscData.girl.length)]); // } // for (int i = 0; i < boyT.getText().length(); i++) { // boyP++; diff --git a/app/src/main/java/com/denizd/substitutionplan/Main.kt b/app/src/main/java/com/denizd/substitutionplan/Main.kt index c78251e..857f4fd 100644 --- a/app/src/main/java/com/denizd/substitutionplan/Main.kt +++ b/app/src/main/java/com/denizd/substitutionplan/Main.kt @@ -14,6 +14,7 @@ import android.os.Build import android.os.Bundle import android.os.Handler import android.preference.PreferenceManager +import android.util.AttributeSet import android.view.* import android.view.animation.Animation import android.view.animation.AnimationUtils @@ -62,7 +63,6 @@ class Main : AppCompatActivity(R.layout.activity_main) { val handler = Handler() - val toolbar = findViewById(R.id.toolbar) val appbarlayout = findViewById(R.id.appbarlayout) val toolbarTxt = findViewById(R.id.toolbarTxt) val bottomSheetRoot = findViewById(R.id.bottom_sheet) @@ -148,7 +148,7 @@ class Main : AppCompatActivity(R.layout.activity_main) { AlertDialog.Builder(context, R.style.AlertDialogCustomDark) } else { AlertDialog.Builder(context, R.style.AlertDialogCustomLight) - } + } // TODO dialogue theming val dialogView = LayoutInflater.from(context).inflate(R.layout.secret_dialog, null) val title = dialogView.findViewById(R.id.textviewtitle) title.text = getString(R.string.chinaTitle) @@ -173,8 +173,7 @@ class Main : AppCompatActivity(R.layout.activity_main) { val animationIn = AnimationUtils.loadAnimation(context, R.anim.chip_slide_in) val animationOut = AnimationUtils.loadAnimation(context, R.anim.chip_slide_out) val generator = Random() - val res = resources - val greetings = res.getStringArray(R.array.greeting8_array) + val greetings = resources.getStringArray(R.array.greeting8_array) val rightNow = Calendar.getInstance() val currentHour = rightNow.get(Calendar.HOUR_OF_DAY) diff --git a/app/src/main/java/com/denizd/substitutionplan/DataGetter.kt b/app/src/main/java/com/denizd/substitutionplan/MiscData.kt similarity index 99% rename from app/src/main/java/com/denizd/substitutionplan/DataGetter.kt rename to app/src/main/java/com/denizd/substitutionplan/MiscData.kt index ac86c73..d083622 100644 --- a/app/src/main/java/com/denizd/substitutionplan/DataGetter.kt +++ b/app/src/main/java/com/denizd/substitutionplan/MiscData.kt @@ -1,6 +1,6 @@ package com.denizd.substitutionplan -class DataGetter { +class MiscData { var boy = arrayOf("Cedric Grislawski", "Dawid Kniola", "Jonas Köstergarten", "Jakob Moerschner", "Siamak Nemati", "Hendrik Plönnings", "Jan Rohmann", "Anton Rosenberger", "Christoph Senft", "Jan Mika Sieckendieck", "Alexander Sinaj", "Nico Tischer", "Nhat Vinh Tran", "Leon Becker", "Youssef Beltagi", "Erich Kerkesner", "Alex Lick", "Oskar Piskorz", "Theo Recktor", "Eray Vatansever", "Dennis Wojciechowski", "Übeydullah Alkan", "Yahya Almarashli", "Sinan Aydin", "Rick Büteröwe", "Levin Dropp", "Silvester Jermolajew", "Tolgay Kekilli", "Dennis Lange", "Aras Mahmoud", "Batuhan Özcan", "Michael Rek", "Khabat Sharif", "Anbinh Tran", "Bastian Westerboer", "Abdulwahab Alshebli", "Kelechi Gaius", "Armin Großmann", "Anes Halep", "Marco Jankowski", "Emirhan Kaplan", "Alexander Kofmann", "Tobi Derek Köhler", "Vasilije Nedeljkovic", "Batu Öcal", "Paul Rehbohm") var girl = arrayOf("Mayra Dronia", "Shalin Ramadan", "Nigina Amin", "Mariya Kheder", "Niegen Khosrawi", "Dayana Osipova", "Julia Petri", "Laura Pister", "Dana Alyoussef", "Ilkem Kahramanoglu", "Rozita Amiri", "Mirja Arneke", "Nastasja Banik", "Anni Kienke", "Benedite Mateta", "Leonie Pohl", "Cecillia Sawires", "Patrycja Smółka", "Elif Zencirkiran", "Nusyba Al Semadi", "Vivien Bruns", "Yasemin Dal", "Svenja Demuth", "Nina Groß", "Kim Lahmeyer", "Luca Minschke", "Kim Reschke", "Amélie Schnellecke", "Delal Secer", "Xenia Segijanski", "Melisa Tas", "Kim Völker", "Banu Yari", "Zeinab Ali", "Mariam Bannout", "Stefanie Fahl", "Yamaty Gaye", "Anna-Sophia Haeberle", "Finja Hastedt", "Viktoria Hammermeister", "Isabel Horn", "Laura Kern", "Melanie Kleinermann", "Målin Kollhoff", "Darja Maier", "Lisa Maier", "Naura Nadzifah", "Janina Roelfs", "Vanessa Scheifler", "Mara Thies", "Lieli Umar", "Leonie Welk", "Betraben Yacoub") diff --git a/app/src/main/java/com/denizd/substitutionplan/ScheduledJobService.java b/app/src/main/java/com/denizd/substitutionplan/ScheduledJobService.java index 0095186..c287711 100644 --- a/app/src/main/java/com/denizd/substitutionplan/ScheduledJobService.java +++ b/app/src/main/java/com/denizd/substitutionplan/ScheduledJobService.java @@ -217,7 +217,7 @@ public class ScheduledJobService extends JobService { roomS[i] = cols.get(4).text(); additionalS[i] = cols.get(5).text(); - DataGetter dg = new DataGetter(); + MiscData dg = new MiscData(); int drawable = dg.getIcon(courseS[i]); Subst subst = new Subst(drawable, groupS[i], dateS[i], timeS[i], courseS[i], roomS[i], additionalS[i], priority); priority--; diff --git a/app/src/main/java/com/denizd/substitutionplan/SettingsFragment.kt b/app/src/main/java/com/denizd/substitutionplan/SettingsFragment.kt index 43a11bf..602b1f6 100644 --- a/app/src/main/java/com/denizd/substitutionplan/SettingsFragment.kt +++ b/app/src/main/java/com/denizd/substitutionplan/SettingsFragment.kt @@ -435,7 +435,7 @@ class SettingsFragment : Fragment(R.layout.content_settings), View.OnClickListen datingBtn.setOnClickListener { try { - val dg = DataGetter() + val dg = MiscData() if (boyTxt.text.toString().isEmpty() || boyCb.isChecked) { boyTxt.setText(dg.boy[gen.nextInt(dg.boy.size)]) } @@ -473,7 +473,7 @@ class SettingsFragment : Fragment(R.layout.content_settings), View.OnClickListen "@NOTIFICATION" -> { edit.putString("time", "").apply() Toast.makeText(mContext, "Notification time cleared", Toast.LENGTH_LONG).show() - } + } // TODO add option to clear database else -> Toast.makeText(mContext, getString(R.string.nothinghappened), Toast.LENGTH_LONG).show() } } diff --git a/gradle.properties b/gradle.properties index d546dea..c66cd15 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,17 +1,16 @@ -# Project-wide Gradle settings. -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. -# For more details on how to configure your build environment visit +## For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html +# # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -android.enableJetifier=true -android.useAndroidX=true -org.gradle.jvmargs=-Xmx1536m +# Default value: -Xmx1024m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +# # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true - - +#Wed May 22 18:15:41 CEST 2019 +android.enableJetifier=true +org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M" +android.useAndroidX=true