From bb71e859a6ff9b86151f6ab14e742288a67335bc Mon Sep 17 00:00:00 2001 From: Cyb3rKo Date: Wed, 21 Oct 2020 00:06:26 +0200 Subject: [PATCH] rewritten to kotlin --- app/build.gradle | 10 +- app/src/main/AndroidManifest.xml | 2 + .../cyb3rko/logviewerforopenhab/About.java | 132 ------- .../com/cyb3rko/logviewerforopenhab/About.kt | 106 +++++ .../logviewerforopenhab/EndUserConsent.java | 169 -------- .../logviewerforopenhab/EndUserConsent.kt | 153 +++++++ .../logviewerforopenhab/IconCredits.java | 25 -- .../logviewerforopenhab/IconCredits.kt | 21 + .../logviewerforopenhab/MainActivity.java | 109 ----- .../logviewerforopenhab/MainActivity.kt | 107 +++++ .../logviewerforopenhab/MainFragment.java | 373 ------------------ .../logviewerforopenhab/MainFragment.kt | 318 +++++++++++++++ .../logviewerforopenhab/PrivacyPolicy.java | 48 --- .../logviewerforopenhab/PrivacyPolicy.kt | 43 ++ .../logviewerforopenhab/TermsOfUse.java | 48 --- .../cyb3rko/logviewerforopenhab/TermsOfUse.kt | 43 ++ .../logviewerforopenhab/UpdateDialog.java | 108 ----- .../logviewerforopenhab/UpdateDialog.kt | 90 +++++ .../logviewerforopenhab/WebViewFragment.java | 321 --------------- .../logviewerforopenhab/WebViewFragment.kt | 267 +++++++++++++ build.gradle | 2 + 21 files changed, 1158 insertions(+), 1337 deletions(-) delete mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/About.java create mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/About.kt delete mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/EndUserConsent.java create mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/EndUserConsent.kt delete mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/IconCredits.java create mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/IconCredits.kt delete mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/MainActivity.java create mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/MainActivity.kt delete mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/MainFragment.java create mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/MainFragment.kt delete mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/PrivacyPolicy.java create mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/PrivacyPolicy.kt delete mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/TermsOfUse.java create mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/TermsOfUse.kt delete mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/UpdateDialog.java create mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/UpdateDialog.kt delete mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/WebViewFragment.java create mode 100644 app/src/main/java/com/cyb3rko/logviewerforopenhab/WebViewFragment.kt diff --git a/app/build.gradle b/app/build.gradle index 3196485..9d669f1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,8 @@ apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' apply plugin: 'com.mikepenz.aboutlibraries.plugin' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 29 @@ -25,9 +27,8 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.cardview:cardview:1.0.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'androidx.recyclerview:recyclerview:1.1.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.2' + implementation 'androidx.core:core-ktx:1.3.2' implementation 'com.amitshekhar.android:android-networking:1.0.2' implementation 'com.getkeepsafe.taptargetview:taptargetview:1.13.0' implementation 'com.github.GrenderG:Toasty:7be5e09' @@ -39,5 +40,6 @@ dependencies { implementation 'com.mikepenz:aboutlibraries-core:8.3.0' implementation 'com.mikepenz:aboutlibraries:8.3.0' implementation 'org.adw.library:discrete-seekbar:1.0.1' - implementation 'com.github.cyb3rko:about-icons:a9e4ccc' + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'com.github.cyb3rko:about-icons:master-SNAPSHOT' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8dba8b7..078412a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,9 @@ + + diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/About.java b/app/src/main/java/com/cyb3rko/logviewerforopenhab/About.java deleted file mode 100644 index 5e2b1cd..0000000 --- a/app/src/main/java/com/cyb3rko/logviewerforopenhab/About.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.cyb3rko.logviewerforopenhab; - -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.ActivityInfo; -import android.net.Uri; -import android.os.Bundle; -import android.view.View; - -import androidx.appcompat.app.AppCompatActivity; - -import com.mikepenz.aboutlibraries.LibsBuilder; - -import mehdi.sakout.aboutpage.AboutPage; -import mehdi.sakout.aboutpage.Element; - -public class About extends AppCompatActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // load save file - SharedPreferences mySPR = this.getSharedPreferences("Safe", 0); - - // restore set orientation - setRequestedOrientation(mySPR.getInt("orientation", ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)); - - // create and show about page - View aboutPage = new AboutPage(this) - .setImage(R.mipmap.ic_launcher_foreground) - .setDescription(getString(R.string.about_description)) - // first item - .addItem(new Element().setTitle(String.format(getString(R.string.about_element_1), BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)).setIconDrawable(R.drawable.about_icon_github).setOnClickListener(showChangelog())) - // first group - .addGroup(getString(R.string.about_group_1)) - // second item - .addItem(new Element().setTitle(getString(R.string.about_element_2)).setIconDrawable(R.drawable._icon_libraries).setOnClickListener(showLibraries())) - // third item - .addItem(new Element().setTitle(getString(R.string.about_element_3)).setIconDrawable(R.drawable._icon_question).setOnClickListener(showIcons())) - // second group - .addGroup(getString(R.string.about_group_2)) - // feddback item - .addItem(new Element().setTitle(getString(R.string.about_element_feedback_text)).setIconDrawable(R.drawable.about_icon_github).setOnClickListener(openOnGithub())) - // email item - .addEmail(getString(R.string.about_element_email_value), getString(R.string.about_element_email_text)) - // website item - .addWebsite(getString(R.string.about_element_website_value), getString(R.string.about_element_website_text)) - // YouTube item - .addItem(new Element().setTitle(getString(R.string.about_element_youtube_text)).setIconDrawable(R.drawable.about_icon_youtube).setIconTint(R.color.about_youtube_color).setOnClickListener(openYouTubeProfile())) - // GitHub item - .addGitHub(getString(R.string.about_element_github_value), getString(R.string.about_element_github_text)) - // Instagram item - .addItem(new Element().setTitle(getString(R.string.about_element_instagram_text)).setIconDrawable(R.drawable.about_icon_instagram).setIconTint(R.color.about_instagram_color).setOnClickListener(openInstaPage())) - .create(); - - // set view (the about page) - setContentView(aboutPage); - } - - // show youtube profile - private View.OnClickListener openYouTubeProfile() { - return new View.OnClickListener() { - @Override - public void onClick(View view) { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.about_element_youtube_value)))); - } - }; - } - - // show changelog - private View.OnClickListener showChangelog() { - return new View.OnClickListener() { - @Override - public void onClick(View view) { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.about_changelog_link)))); - } - }; - } - - // get and show library credits on click - private View.OnClickListener showLibraries() { - return new View.OnClickListener() { - @Override - public void onClick(View view) { - // open library credits - new LibsBuilder() - .withShowLoadingProgress(true) - .withAboutVersionShownCode(false) - .withAboutVersionShownName(false) - .withAutoDetect(true) - .withAboutIconShown(false) - .withAboutVersionShown(false) - .withVersionShown(true) - .withLicenseDialog(true) - .withLicenseShown(true) - .withCheckCachedDetection(true) - .withSortEnabled(true) - .start(getApplication()); - } - }; - } - - // show icon credits on click - private View.OnClickListener showIcons() { - return new View.OnClickListener() { - @Override - public void onClick(View view) { - startActivity(new Intent(getApplicationContext(), IconCredits.class)); - } - }; - } - - // open project on GitHub on click - private View.OnClickListener openOnGithub() { - return new View.OnClickListener() { - @Override - public void onClick(View view) { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.about_element_feedback_value)))); - } - }; - } - - // show instagram page - private View.OnClickListener openInstaPage() { - return new View.OnClickListener() { - @Override - public void onClick(View view) { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.about_element_instagram_value)))); - } - }; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/About.kt b/app/src/main/java/com/cyb3rko/logviewerforopenhab/About.kt new file mode 100644 index 0000000..c5e72cf --- /dev/null +++ b/app/src/main/java/com/cyb3rko/logviewerforopenhab/About.kt @@ -0,0 +1,106 @@ +package com.cyb3rko.logviewerforopenhab + +import android.content.Intent +import android.content.pm.ActivityInfo +import android.net.Uri +import android.os.Bundle +import android.os.PersistableBundle +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import com.mikepenz.aboutlibraries.LibsBuilder +import mehdi.sakout.aboutpage.AboutPage +import mehdi.sakout.aboutpage.Element + +class About : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // load save file + val mySPR = getSharedPreferences("Safe", 0) + + // restore set orientation + requestedOrientation = mySPR.getInt("orientation", ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) + + // create and show about page + val aboutPage = AboutPage(this) + .setImage(R.mipmap.ic_launcher_foreground) + .setDescription(getString(R.string.about_description)) // first item + .addItem( + Element().setTitle(String.format(getString(R.string.about_element_1), BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)) + .setIconDrawable(R.drawable.about_icon_github).setOnClickListener(showChangelog()) + ) // first group + .addGroup(getString(R.string.about_group_1)) // second item + .addItem( + Element().setTitle(getString(R.string.about_element_2)).setIconDrawable(R.drawable._icon_libraries) + .setOnClickListener(showLibraries()) + ) // third item + .addItem( + Element().setTitle(getString(R.string.about_element_3)).setIconDrawable(R.drawable._icon_question).setOnClickListener(showIcons()) + ) // second group + .addGroup(getString(R.string.about_group_2)) // feddback item + .addItem( + Element().setTitle(getString(R.string.about_element_feedback_text)).setIconDrawable(R.drawable.about_icon_github) + .setOnClickListener(openOnGithub()) + ) // email item + .addEmail(getString(R.string.about_element_email_value), getString(R.string.about_element_email_text)) // website item + .addWebsite(getString(R.string.about_element_website_value), getString(R.string.about_element_website_text)) // YouTube item + .addItem( + Element().setTitle(getString(R.string.about_element_youtube_text)).setIconDrawable(R.drawable.about_icon_youtube) + .setIconTint(R.color.about_youtube_color).setOnClickListener(openYouTubeProfile()) + ) // GitHub item + .addGitHub(getString(R.string.about_element_github_value), getString(R.string.about_element_github_text)) // Instagram item + .addItem( + Element().setTitle(getString(R.string.about_element_instagram_text)).setIconDrawable(R.drawable.about_icon_instagram) + .setIconTint(R.color.about_instagram_color).setOnClickListener(openInstaPage()) + ) + .create() + + // set view (the about page) + setContentView(aboutPage) + } + + // show youtube profile + private fun openYouTubeProfile(): View.OnClickListener? { + return View.OnClickListener { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.about_element_youtube_value)))) } + } + + // show changelog + private fun showChangelog(): View.OnClickListener? { + return View.OnClickListener { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.about_changelog_link)))) } + } + + // get and show library credits on click + private fun showLibraries(): View.OnClickListener? { + return View.OnClickListener { // open library credits + LibsBuilder() + .withShowLoadingProgress(true) + .withAboutVersionShownCode(false) + .withAboutVersionShownName(false) + .withAutoDetect(true) + .withAboutIconShown(false) + .withAboutVersionShown(false) + .withVersionShown(true) + .withLicenseDialog(true) + .withLicenseShown(true) + .withCheckCachedDetection(true) + .withSortEnabled(true) + .start(application) + } + } + + // show icon credits on click + private fun showIcons(): View.OnClickListener? { + return View.OnClickListener { startActivity(Intent(applicationContext, IconCredits::class.java)) } + } + + // open project on GitHub on click + private fun openOnGithub(): View.OnClickListener? { + return View.OnClickListener { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.about_element_feedback_value)))) } + } + + // show instagram page + private fun openInstaPage(): View.OnClickListener? { + return View.OnClickListener { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.about_element_instagram_value)))) } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/EndUserConsent.java b/app/src/main/java/com/cyb3rko/logviewerforopenhab/EndUserConsent.java deleted file mode 100644 index 1939357..0000000 --- a/app/src/main/java/com/cyb3rko/logviewerforopenhab/EndUserConsent.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.cyb3rko.logviewerforopenhab; - -import android.annotation.SuppressLint; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.graphics.Typeface; -import android.os.Bundle; -import android.text.SpannableString; -import android.text.Spanned; -import android.text.method.LinkMovementMethod; -import android.text.style.ClickableSpan; -import android.view.Gravity; -import android.view.View; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatDialogFragment; - -import org.jetbrains.annotations.NotNull; - -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.Objects; - -public class EndUserConsent extends AppCompatDialogFragment { - private ClickableSpan clickableSpan1; - private ClickableSpan clickableSpan2; - private DialogInterface.OnClickListener dialogClickListener; - private SharedPreferences mySPR; - private SharedPreferences.Editor editor; - private String title; - private String button1Text; - private SpannableString spannableString; - - public static boolean dialogType; - - @NotNull - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - // load save file and its editor - mySPR = Objects.requireNonNull(getActivity()).getSharedPreferences("Safe", 0); - editor = mySPR.edit(); - editor.apply(); - - // implement actions if click on link is registered - clickableSpan1 = new ClickableSpan() { - @Override - public void onClick(@NonNull View view) { - view.getContext().startActivity(new Intent(getContext(), PrivacyPolicy.class)); - } - }; - clickableSpan2 = new ClickableSpan() { - @Override - public void onClick(@NonNull View view) { - view.getContext().startActivity(new Intent(getContext(), TermsOfUse.class)); - } - }; - - // check which type of end user consent to show (either to accept or to show the already accepted end user consent) - if (dialogType) { - dialog1(); - } else { - dialog2(); - } - - // create new dialog builder - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - - // create title - TextView titleView = new TextView(getContext()); - titleView.setText(title); - titleView.setTextSize(22); - titleView.setTypeface(Typeface.DEFAULT_BOLD); - titleView.setPadding(32, 32, 32, 32); - titleView.setGravity(Gravity.CENTER_HORIZONTAL); - - // create text - TextView messageView = new TextView(getContext()); - messageView.setMovementMethod(LinkMovementMethod.getInstance()); - messageView.setPadding(32, 32, 32, 32); - messageView.setGravity(Gravity.CENTER_HORIZONTAL); - messageView.setText(spannableString); - messageView.setTextSize(16); - - // create dialog - builder.setCustomTitle(titleView) - .setView(messageView) - // only allow to cancel on second dialog - .setCancelable(!dialogType) - // add right button - .setPositiveButton(button1Text, dialogClickListener); - - // add left button if second dialog shall be shown - if (dialogType) { - builder.setNegativeButton(getString(R.string.end_user_consent_button_2), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - Objects.requireNonNull(getActivity()).finish(); - } - }); - } - - // show dialog - return builder.create(); - } - - // set data of first dialog - private void dialog1() { - // set text - String message = getString(R.string.end_user_consent_message); - spannableString = new SpannableString(message); - - // add clickable links to policy and terms - spannableString.setSpan(clickableSpan1, message.indexOf("Privacy"), message.indexOf("Privacy") + "Privacy Policy".length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - spannableString.setSpan(clickableSpan2, message.indexOf("Terms"), message.indexOf("Terms") + "Terms of Use".length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - - // set title - title = getString(R.string.end_user_consent_title); - // set text of right button - button1Text = getString(R.string.end_user_consent_button_1); - // set what happens when right button is clicked - dialogClickListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - // turn on Firebase Analytics - MainActivity.firebaseAnalytics.setAnalyticsCollectionEnabled(true); - // get date and time - Date date = Calendar.getInstance().getTime(); - @SuppressLint("SimpleDateFormat") SimpleDateFormat sDF = new SimpleDateFormat("dd.MM.yyyy"); - @SuppressLint("SimpleDateFormat") SimpleDateFormat sDF2 = new SimpleDateFormat("HH:mm:ss"); - // store dat and time - editor.putString("date", sDF.format(date)); - editor.putString("time", sDF2.format(date)); - editor.putBoolean("firstStart", false).apply(); - // open menu - editor.putInt("orientation", -1).apply(); - Objects.requireNonNull(getActivity()).getSupportFragmentManager().beginTransaction().replace((R.id.start), new MainFragment()).commit(); - } - }; - } - - // set data of second dialog - private void dialog2() { - // set text - String message = getString(R.string.end_user_consent_2_message_1); - message += mySPR.getString("date", "") + getString(R.string.end_user_consent_2_message_2) + mySPR.getString("time", ""); - spannableString = new SpannableString(message); - - // add clickable links to policy and terms - spannableString.setSpan(clickableSpan1, message.indexOf("Privacy"), message.indexOf("Privacy") + "Privacy Policy".length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - spannableString.setSpan(clickableSpan2, message.indexOf("Terms"), message.indexOf("Terms") + "Terms of Use".length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - - // set title - title = getString(R.string.end_user_consent_2_title); - // set text of right button - button1Text = getString(R.string.end_user_consent_2_button); - // set what happens when right button is clicked - dialogClickListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - // nothing to clean up (for PMD) - } - }; - } -} diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/EndUserConsent.kt b/app/src/main/java/com/cyb3rko/logviewerforopenhab/EndUserConsent.kt new file mode 100644 index 0000000..3e87c30 --- /dev/null +++ b/app/src/main/java/com/cyb3rko/logviewerforopenhab/EndUserConsent.kt @@ -0,0 +1,153 @@ +package com.cyb3rko.logviewerforopenhab + +import android.annotation.SuppressLint +import android.app.AlertDialog +import android.app.Dialog +import android.content.DialogInterface +import android.content.Intent +import android.content.SharedPreferences +import android.graphics.Typeface +import android.os.Bundle +import android.text.SpannableString +import android.text.Spanned +import android.text.method.LinkMovementMethod +import android.text.style.ClickableSpan +import android.view.Gravity +import android.view.View +import android.widget.TextView +import androidx.appcompat.app.AppCompatDialogFragment +import java.text.SimpleDateFormat +import java.util.* + +class EndUserConsent(val dialogType: Boolean) : AppCompatDialogFragment() { + + private lateinit var clickableSpan1: ClickableSpan + private lateinit var clickableSpan2: ClickableSpan + private lateinit var dialogClickListener: DialogInterface.OnClickListener + private lateinit var mySPR: SharedPreferences + private lateinit var editor: SharedPreferences.Editor + private lateinit var title: String + private lateinit var button1Text: String + private lateinit var spannableString: SpannableString + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + // load save file and its editor + mySPR = activity!!.getSharedPreferences("Safe", 0) + editor = mySPR.edit() + editor.apply() + + // implement actions if click on link is registered + clickableSpan1 = object : ClickableSpan() { + override fun onClick(view: View) { + view.context.startActivity(Intent(context, PrivacyPolicy::class.java)) + } + } + clickableSpan2 = object : ClickableSpan() { + override fun onClick(view: View) { + view.context.startActivity(Intent(context, TermsOfUse::class.java)) + } + } + + // check which type of end user consent to show (either to accept or to show the already accepted end user consent) + if (dialogType) { + dialog1() + } else { + dialog2() + } + + // create new dialog builder + val builder = AlertDialog.Builder(activity) + + // create title + val titleView = TextView(context) + titleView.text = title + titleView.textSize = 22f + titleView.setTypeface(Typeface.DEFAULT_BOLD) + titleView.setPadding(32, 32, 32, 32) + titleView.gravity = Gravity.CENTER_HORIZONTAL + + // create text + val messageView = TextView(context) + messageView.movementMethod = LinkMovementMethod.getInstance() + messageView.setPadding(32, 32, 32, 32) + messageView.gravity = Gravity.CENTER_HORIZONTAL + messageView.text = spannableString + messageView.textSize = 16f + + // create dialog + builder.setCustomTitle(titleView) + .setView(messageView) // only allow to cancel on second dialog + .setCancelable(!dialogType) // add right button + .setPositiveButton(button1Text, dialogClickListener) + + // add left button if second dialog shall be shown + if (dialogType) { + builder.setNegativeButton(getString(R.string.end_user_consent_button_2)) { _, _ -> + activity!!.finish() + } + } + + // show dialog + return builder.create() + } + + // set data of first dialog + private fun dialog1() { + // set text + val message = getString(R.string.end_user_consent_message) + spannableString = SpannableString(message) + + // add clickable links to policy and terms + spannableString.setSpan(clickableSpan1, message.indexOf("Privacy"), message.indexOf("Privacy") + "Privacy Policy".length, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + spannableString.setSpan(clickableSpan2, message.indexOf("Terms"), message.indexOf("Terms") + "Terms of Use".length, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + + // set title + title = getString(R.string.end_user_consent_title) + // set text of right button + button1Text = getString(R.string.end_user_consent_button_1) + // set what happens when right button is clicked + dialogClickListener = DialogInterface.OnClickListener { _, _ -> // turn on Firebase Analytics + MainActivity.firebaseAnalytics.setAnalyticsCollectionEnabled(true) + // get date and time + val date = Calendar.getInstance().time + @SuppressLint("SimpleDateFormat") val sDF = SimpleDateFormat("dd.MM.yyyy") + @SuppressLint("SimpleDateFormat") val sDF2 = SimpleDateFormat("HH:mm:ss") + // store dat and time + editor.putString("date", sDF.format(date)) + editor.putString("time", sDF2.format(date)) + editor.putBoolean("firstStart", false).apply() + // open menu + editor.putInt("orientation", -1).apply() + activity?.supportFragmentManager?.beginTransaction()?.replace(R.id.start, MainFragment())?.commit() + } + } + + // set data of second dialog + private fun dialog2() { + // set text + var message = getString(R.string.end_user_consent_2_message_1) + message += mySPR.getString("date", "") + getString(R.string.end_user_consent_2_message_2) + mySPR.getString("time", "") + spannableString = SpannableString(message) + + // add clickable links to policy and terms + spannableString.setSpan(clickableSpan1, message.indexOf("Privacy"), message.indexOf("Privacy") + "Privacy Policy".length, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + spannableString.setSpan(clickableSpan2, message.indexOf("Terms"), message.indexOf("Terms") + "Terms of Use".length, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + + // set title + title = getString(R.string.end_user_consent_2_title) + // set text of right button + button1Text = getString(R.string.end_user_consent_2_button) + // set what happens when right button is clicked + dialogClickListener = DialogInterface.OnClickListener { _, _ -> + // nothing to clean up (for PMD) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/IconCredits.java b/app/src/main/java/com/cyb3rko/logviewerforopenhab/IconCredits.java deleted file mode 100644 index ed1ab5b..0000000 --- a/app/src/main/java/com/cyb3rko/logviewerforopenhab/IconCredits.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.cyb3rko.logviewerforopenhab; - -import android.content.SharedPreferences; -import android.content.pm.ActivityInfo; -import android.os.Bundle; - -import androidx.appcompat.app.AppCompatActivity; - -import com.cyb3rko.abouticons.AboutIcons; - -public class IconCredits extends AppCompatActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // load save file - SharedPreferences mySPR = this.getSharedPreferences("Safe", 0); - - // restore set orientation - setRequestedOrientation(mySPR.getInt("orientation", ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)); - - // set view - setContentView(new AboutIcons(this, R.drawable.class).get()); - } -} diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/IconCredits.kt b/app/src/main/java/com/cyb3rko/logviewerforopenhab/IconCredits.kt new file mode 100644 index 0000000..0b81ee8 --- /dev/null +++ b/app/src/main/java/com/cyb3rko/logviewerforopenhab/IconCredits.kt @@ -0,0 +1,21 @@ +package com.cyb3rko.logviewerforopenhab + +import android.content.pm.ActivityInfo +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.cyb3rko.abouticons.AboutIcons + +class IconCredits : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // load save file + val mySPR = getSharedPreferences("Safe", 0) + + // restore set orientation + requestedOrientation = mySPR.getInt("orientation", ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) + + // set view + setContentView(AboutIcons(this, R.drawable::class.java).get()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/MainActivity.java b/app/src/main/java/com/cyb3rko/logviewerforopenhab/MainActivity.java deleted file mode 100644 index b284e4c..0000000 --- a/app/src/main/java/com/cyb3rko/logviewerforopenhab/MainActivity.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.cyb3rko.logviewerforopenhab; - -import android.Manifest; -import android.app.Activity; -import android.content.SharedPreferences; -import android.os.Bundle; - -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.app.ActivityCompat; - -import com.androidnetworking.AndroidNetworking; -import com.androidnetworking.error.ANError; -import com.androidnetworking.interfaces.StringRequestListener; -import com.google.firebase.analytics.FirebaseAnalytics; - -import es.dmoral.toasty.Toasty; - -public class MainActivity extends AppCompatActivity { - public static FirebaseAnalytics firebaseAnalytics; - private SharedPreferences mySPR; - private SharedPreferences.Editor editor; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - // connect Firebase - firebaseAnalytics = FirebaseAnalytics.getInstance(this); - - // load save file and its editor - mySPR = this.getSharedPreferences("Safe", 0); - editor = mySPR.edit(); - editor.apply(); - - // forbid queueing of toasts - Toasty.Config.getInstance().allowQueue(false).apply(); - - // open end user content dialog if it is not yet accepted - if (mySPR.getBoolean("firstStart", true) || mySPR.getString("date", "").equals("")) { - EndUserConsent.dialogType = true; - EndUserConsent endUserConsent = new EndUserConsent(); - endUserConsent.setCancelable(false); - endUserConsent.show(getSupportFragmentManager(), getClass().getName()); - } else { - // enable Firebase Analytics - firebaseAnalytics.setAnalyticsCollectionEnabled(true); - // open menu - getSupportFragmentManager().beginTransaction().replace(R.id.start, new MainFragment()).commit(); - // check if orientation was recently changed - if (!mySPR.getBoolean("tempDisableStart", false)) { - // check for update - updateCheck(this); - } - } - } - - // method to check for updates and open update dialog if new update is available - private void updateCheck(final Activity activity) { - AndroidNetworking.get(getString(R.string.update_check_link)) - .doNotCacheResponse() - .build() - .getAsString(new StringRequestListener() { - // if request is succesful - @Override - public void onResponse(String response) { - // extract and store newest version code and name - String versionCodeAndFollowing = response.split("versionCode ")[1]; - String versionCode = versionCodeAndFollowing.split("\n")[0]; - int newestVersionCode = Integer.parseInt(versionCode); - String versionNameAndFollowing = versionCodeAndFollowing.split("\"")[1]; - String versionName = versionNameAndFollowing.split("\"")[0]; - editor.putString("newestVersion", versionName).apply(); - - // if newer update available, open update dialog - if (BuildConfig.VERSION_CODE != newestVersionCode) { - UpdateDialog updateDialog = new UpdateDialog(); - updateDialog.show(getSupportFragmentManager(), getClass().getName()); - - // request permissions - ActivityCompat.requestPermissions(activity , new String[]{Manifest.permission.INTERNET, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.REQUEST_INSTALL_PACKAGES}, 1); - } - } - // if request is not succesful - @Override - public void onError(ANError anError) { - // nothing to clean up (for PMD) - } - }); - } - - // on app stop - @Override - protected void onDestroy() { - super.onDestroy(); - // if log was shown - if (!mySPR.getBoolean("connected", false)) { - // set tempDisableStart to true to open log view again after orientation was changed - editor.putBoolean("tempDisableStart", true).apply(); - } - } - - // if back button pressed - @Override - public void onBackPressed() { - finish(); - } -} diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/MainActivity.kt b/app/src/main/java/com/cyb3rko/logviewerforopenhab/MainActivity.kt new file mode 100644 index 0000000..7942b12 --- /dev/null +++ b/app/src/main/java/com/cyb3rko/logviewerforopenhab/MainActivity.kt @@ -0,0 +1,107 @@ +package com.cyb3rko.logviewerforopenhab + +import android.Manifest +import android.app.Activity +import android.content.SharedPreferences +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import com.androidnetworking.AndroidNetworking +import com.androidnetworking.error.ANError +import com.androidnetworking.interfaces.StringRequestListener +import com.google.firebase.analytics.FirebaseAnalytics +import es.dmoral.toasty.Toasty + +class MainActivity : AppCompatActivity() { + + private lateinit var mySPR: SharedPreferences + private lateinit var editor: SharedPreferences.Editor + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + // connect Firebase + firebaseAnalytics = FirebaseAnalytics.getInstance(this) + + // load save file and its editor + mySPR = getSharedPreferences("Safe", 0) + editor = mySPR.edit() + editor.apply() + + // forbid queueing of toasts + Toasty.Config.getInstance().allowQueue(false).apply() + + // open end user content dialog if it is not yet accepted + if (mySPR.getBoolean("firstStart", true) || mySPR.getString("date", "") == "") { + val endUserConsent = EndUserConsent(true) + endUserConsent.isCancelable = false + endUserConsent.show(supportFragmentManager, javaClass.name) + } else { + // enable Firebase Analytics + firebaseAnalytics.setAnalyticsCollectionEnabled(true) + // open menu + supportFragmentManager.beginTransaction().replace(R.id.start, MainFragment()).commit() + // check if orientation was recently changed + if (!mySPR.getBoolean("tempDisableStart", false)) { + // check for update + updateCheck(this) + } + } + } + + // method to check for updates and open update dialog if new update is available + private fun updateCheck(activity: Activity) { + AndroidNetworking.get(getString(R.string.update_check_link)) + .doNotCacheResponse() + .build() + .getAsString(object : StringRequestListener { + // if request is succesful + override fun onResponse(response: String) { + // extract and store newest version code and name + val versionCodeAndFollowing = response.split("versionCode ".toRegex()).toTypedArray()[1] + val versionCode = versionCodeAndFollowing.split("\n".toRegex()).toTypedArray()[0] + val newestVersionCode = versionCode.toInt() + val versionNameAndFollowing = versionCodeAndFollowing.split("\"".toRegex()).toTypedArray()[1] + val versionName = versionNameAndFollowing.split("\"".toRegex()).toTypedArray()[0] + editor.putString("newestVersion", versionName).apply() + + // if newer update available, open update dialog + if (BuildConfig.VERSION_CODE != newestVersionCode) { + val updateDialog = UpdateDialog() + updateDialog.show(supportFragmentManager, javaClass.name) + + // request permissions + ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.INTERNET, Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.REQUEST_INSTALL_PACKAGES + ), 1 + ) + } + } + + // if request is not succesful + override fun onError(anError: ANError) { + // nothing to clean up (for PMD) + } + }) + } + + // on app stop + override fun onDestroy() { + super.onDestroy() + // if log was shown + if (!mySPR.getBoolean("connected", false)) { + // set tempDisableStart to true to open log view again after orientation was changed + editor.putBoolean("tempDisableStart", true).apply() + } + } + + // if back button pressed + override fun onBackPressed() { + finish() + } + + companion object { + lateinit var firebaseAnalytics: FirebaseAnalytics + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/MainFragment.java b/app/src/main/java/com/cyb3rko/logviewerforopenhab/MainFragment.java deleted file mode 100644 index a5638ce..0000000 --- a/app/src/main/java/com/cyb3rko/logviewerforopenhab/MainFragment.java +++ /dev/null @@ -1,373 +0,0 @@ -package com.cyb3rko.logviewerforopenhab; - -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.ActivityInfo; -import android.os.Bundle; -import android.os.Handler; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.InputMethodManager; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; - -import java.util.Objects; - -import es.dmoral.toasty.Toasty; - -public class MainFragment extends Fragment { - private Button connectButton; - private CheckBox connectCheck; - private CheckBox hostnameIPAddressCheck; - private CheckBox portCheck; - private EditText hostnameIPAddress; - private EditText port; - private ImageButton hostnameIPAddressEdit; - private ImageButton portEdit; - private ImageView orientation; - private SharedPreferences mySPR; - private SharedPreferences.Editor editor; - private String hostnameIPAddressString; - private String link; - private TextView about; - private TextView linkView; - private TextView endUserConsent; - - private int portInt; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View v = inflater.inflate(R.layout.fragment_main, container, false); - - connectButton = v.findViewById(R.id.connect_button); - connectCheck = v.findViewById(R.id.connect_check); - hostnameIPAddressCheck = v.findViewById(R.id.hostname_ip_address_check); - portCheck = v.findViewById(R.id.port_check); - hostnameIPAddress = v.findViewById(R.id.hostname_ip_address); - port = v.findViewById(R.id.port); - hostnameIPAddressEdit = v.findViewById(R.id.hostname_ip_address_edit); - portEdit = v.findViewById(R.id.port_edit); - orientation = v.findViewById(R.id.imageView); - linkView = v.findViewById(R.id.link_view); - about = v.findViewById(R.id.about); - endUserConsent = v.findViewById(R.id.end_user_consent); - TextView versionView = v.findViewById(R.id.version_view); - - // load save file and its editor - mySPR = v.getContext().getSharedPreferences("Safe", 0); - editor = mySPR.edit(); - editor.apply(); - - // restore set orientation - Objects.requireNonNull(getActivity()).setRequestedOrientation(mySPR.getInt("orientation", ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)); - - // restore last status - statusRestoring(); - - // show version - versionView.setText(BuildConfig.VERSION_NAME); - - // set onclick listeners - setConnectButtonClickListener(v); - setOrientationIconClickListener(); - setEditButtonClickListener(hostnameIPAddressEdit, hostnameIPAddress, portEdit, hostnameIPAddressCheck); - setEditButtonClickListener(portEdit, port, hostnameIPAddressEdit, portCheck); - setEndUserConsentClickListener(); - setAboutClickListener(); - setConnectCheckClickListener(); - - // show view - return v; - } - - // if view is ready restore set orientation - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - Objects.requireNonNull(getActivity()).setRequestedOrientation(mySPR.getInt("orientation", 0)); - super.onViewCreated(view, savedInstanceState); - } - - // restore last status - private void statusRestoring() { - // restore chechbox status - connectCheck.setChecked(mySPR.getBoolean("connectCheck", false)); - - // check if orientation was recently changed - if (!mySPR.getBoolean("tempDisableStart", false)) { - // check if autoStart is enabled - if (mySPR.getBoolean("autoStart", false) && connectCheck.isChecked()) { - // open logview - assert getFragmentManager() != null; - getFragmentManager().beginTransaction() - .replace(R.id.start, new WebViewFragment()) - .addToBackStack(null) - .commit(); - editor.putBoolean("connected", true).apply(); - - // show toast - Toasty.info(Objects.requireNonNull(getContext()), getString(R.string.connecting), Toasty.LENGTH_SHORT).show(); - } - } else { - // disable temporary lock after orientation was changed - Runnable runnable = new Runnable() { - @Override - public void run() { - editor.putBoolean("tempDisableStart", false).apply(); - } - }; - new Handler().postDelayed(runnable, 10); - } - - // set correct orientation icon - setOrientationIcon(); - - // restore textbox status - if (mySPR.getString("hostnameIPAddressString", "").equals("") || mySPR.getString("hostnameIPAddressString", "").equals("0")) { - hostnameIPAddress.setText(""); - hostnameIPAddress.setEnabled(true); - } else { - hostnameIPAddress.setText(mySPR.getString("hostnameIPAddressString", "0")); - hostnameIPAddressString = mySPR.getString("hostnameIPAddressString", "0"); - hostnameIPAddress.setEnabled(false); - } - - // restore checkbox status - hostnameIPAddressCheck.setChecked(mySPR.getBoolean("hostnameIPAddressCheck", true)); - - // restore textbox status - if (mySPR.getInt("portInt", 0) == 0) { - port.setText(""); - hostnameIPAddress.setEnabled(true); - } else { - port.setText(String.valueOf(mySPR.getInt("portInt", 0))); - port.setEnabled(false); - portInt = mySPR.getInt("portInt", 9001); - } - - // restore checkbox status - portCheck.setChecked(mySPR.getBoolean("portCheck", true)); - - // check if connect was clicked and restore last status - if (!hostnameIPAddress.getText().toString().isEmpty() && !port.getText().toString().isEmpty()) { - linkGeneration(); - hostnameIPAddressEdit.setVisibility(View.VISIBLE); - portEdit.setVisibility(View.VISIBLE); - hostnameIPAddressCheck.setVisibility(View.INVISIBLE); - portCheck.setVisibility(View.INVISIBLE); - connectCheck.setVisibility(View.VISIBLE); - connectButton.setText(getString(R.string.connect_button_2)); - } - } - - // set correct orientation icon - private void setOrientationIcon() { - switch (mySPR.getInt("orientation", ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) { - case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: - orientation.setImageResource(R.drawable._icon_landscape_orientation); - break; - case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: - orientation.setImageResource(R.drawable._icon_portrait_orientation); - break; - case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED: - orientation.setImageResource(R.drawable._icon_auto_orientation); - break; - default: - break; - } - } - - // generate and show new link according to user inputs - private void linkGeneration() { - hostnameIPAddressString = hostnameIPAddress.getText().toString(); - - if (!port.getText().toString().isEmpty()) { - portInt = Integer.parseInt(String.valueOf(port.getText())); - } else { - portInt = 9001; - } - - link = "http://" + hostnameIPAddressString + ":" + portInt; - linkView.setText(link); - } - - // onClickListener for connect button - private void setConnectButtonClickListener(final View v) { - connectButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - // check if user entered hostname - if (!hostnameIPAddress.getText().toString().isEmpty()) { - // if connect button was not clicked before - if (linkView.getText().toString().isEmpty()) { - // generate new link - linkGeneration(); - - // switch all elements - hostnameIPAddress.setEnabled(false); - hostnameIPAddressEdit.setVisibility(View.VISIBLE); - port.setEnabled(false); - portEdit.setVisibility(View.VISIBLE); - hostnameIPAddressCheck.setVisibility(View.INVISIBLE); - portCheck.setVisibility(View.INVISIBLE); - connectCheck.setVisibility(View.VISIBLE); - - // store values if user wants to - if (hostnameIPAddressCheck.isChecked()) { - editor.putString("hostnameIPAddressString", hostnameIPAddressString); - editor.putBoolean("hostnameIPAddressCheck", true); - } else { - editor.putString("hostnameIPAddressString", ""); - editor.putBoolean("hostnameIPAddressCheck", false); - } - - // store values if user wants to - if (portCheck.isChecked()) { - editor.putBoolean("portCheck", true); - - // check if user entered port - if (!port.getText().toString().isEmpty()) { - editor.putInt("portInt", portInt); - } else { - port.setText(String.valueOf(9001)); - editor.putInt("portInt", 9001); - } - } else { - editor.putInt("portInt", 0); - editor.putBoolean("portCheck", false); - } - - // store link - editor.putString("link", link).apply(); - - // change button text - connectButton.setText(getString(R.string.connect_button_2)); - } else { - // open logview - assert getFragmentManager() != null; - getFragmentManager().beginTransaction() - .replace(R.id.start, new WebViewFragment()) - .addToBackStack(null) - .commit(); - - editor.putBoolean("connected", true).apply(); - // close keyboard - if (view != null) { - InputMethodManager imm = (InputMethodManager) view.getContext().getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE); - assert imm != null; - imm.hideSoftInputFromWindow(view.getWindowToken(), 0); - } - - // show toast - Toasty.info(v.getContext(), getString(R.string.connecting), Toasty.LENGTH_SHORT).show(); - } - } else { - // show error if one field or both fields are empty - Toasty.error(v.getContext(), getString(R.string.error_fill_out), Toasty.LENGTH_LONG).show(); - } - } - }); - } - - // onClickListener for orientation icon - private void setOrientationIconClickListener() { - orientation.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - int newOrientation = 0; - String newOrientationName = ""; - - // check current orientation and define new orientation - switch (mySPR.getInt("orientation", ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)) { - case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: - newOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; - newOrientationName = "portrait"; - break; - case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: - newOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - newOrientationName = "auto"; - break; - case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED: - newOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; - newOrientationName = "landscape"; - break; - default: - break; - } - - // store orientation, change icon and change orientation - editor.putInt("orientation", newOrientation); - editor.putBoolean("tempDisableStart", true).apply(); - setOrientationIcon(); - Objects.requireNonNull(getActivity()).setRequestedOrientation(newOrientation); - - // show toast - Toasty.info(Objects.requireNonNull(getContext()), String.format(getString(R.string.orientation_changed), newOrientationName), Toasty.LENGTH_SHORT).show(); - } - }); - } - - // onClickListener for both edit buttons - private void setEditButtonClickListener(final ImageButton imageButton, final TextView textView, final ImageButton imageButton2, final CheckBox checkBox) { - imageButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - // switch all elements - textView.setEnabled(true); - imageButton.setVisibility(View.INVISIBLE); - imageButton2.setVisibility(View.INVISIBLE); - checkBox.setVisibility(View.VISIBLE); - connectCheck.setVisibility(View.INVISIBLE); - linkView.setText(""); - connectButton.setText(getString(R.string.connect_button_1)); - } - }); - } - - // onClickListener for end user consent textview - private void setEndUserConsentClickListener() { - endUserConsent.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - // start end user consent class (and show second dialog type ("false")) - EndUserConsent.dialogType = false; - EndUserConsent endUserConsent = new EndUserConsent(); - endUserConsent.setCancelable(true); - endUserConsent.show(getChildFragmentManager(), getClass().getName()); - } - }); - } - - // onClickListener for about textview - private void setAboutClickListener() { - about.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - // start about class - view.getContext().startActivity(new Intent(getContext(), About.class)); - } - }); - } - - // onClickListener for connect checkbox - private void setConnectCheckClickListener() { - connectCheck.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean b) { - // store values - editor.putBoolean("connectCheck", connectCheck.isChecked()); - editor.putBoolean("autoStart", connectCheck.isChecked()).apply(); - } - }); - } -} diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/MainFragment.kt b/app/src/main/java/com/cyb3rko/logviewerforopenhab/MainFragment.kt new file mode 100644 index 0000000..d8135ca --- /dev/null +++ b/app/src/main/java/com/cyb3rko/logviewerforopenhab/MainFragment.kt @@ -0,0 +1,318 @@ +package com.cyb3rko.logviewerforopenhab + +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.content.pm.ActivityInfo +import android.os.Bundle +import android.os.Handler +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.inputmethod.InputMethodManager +import android.widget.* +import androidx.fragment.app.Fragment +import es.dmoral.toasty.Toasty + +class MainFragment : Fragment() { + private var connectButton: Button? = null + private var connectCheck: CheckBox? = null + private var hostnameIPAddressCheck: CheckBox? = null + private var portCheck: CheckBox? = null + private var hostnameIPAddress: EditText? = null + private var port: EditText? = null + private var hostnameIPAddressEdit: ImageButton? = null + private var portEdit: ImageButton? = null + private var orientation: ImageView? = null + private lateinit var mySPR: SharedPreferences + private lateinit var editor: SharedPreferences.Editor + private var hostnameIPAddressString: String? = null + private var link: String? = null + private var about: TextView? = null + private var linkView: TextView? = null + private var endUserConsent: TextView? = null + private var portInt = 0 + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val v = inflater.inflate(R.layout.fragment_main, container, false) + connectButton = v.findViewById(R.id.connect_button) + connectCheck = v.findViewById(R.id.connect_check) + hostnameIPAddressCheck = v.findViewById(R.id.hostname_ip_address_check) + portCheck = v.findViewById(R.id.port_check) + hostnameIPAddress = v.findViewById(R.id.hostname_ip_address) + port = v.findViewById(R.id.port) + hostnameIPAddressEdit = v.findViewById(R.id.hostname_ip_address_edit) + portEdit = v.findViewById(R.id.port_edit) + orientation = v.findViewById(R.id.imageView) + linkView = v.findViewById(R.id.link_view) + about = v.findViewById(R.id.about) + endUserConsent = v.findViewById(R.id.end_user_consent) + val versionView = v.findViewById(R.id.version_view) + + // load save file and its editor + mySPR = v.context.getSharedPreferences("Safe", 0) + editor = mySPR.edit() + editor.apply() + + // restore set orientation + activity?.requestedOrientation = mySPR.getInt("orientation", ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) + + // restore last status + statusRestoring() + + // show version + versionView.text = BuildConfig.VERSION_NAME + + // set onclick listeners + setConnectButtonClickListener(v) + setOrientationIconClickListener() + setEditButtonClickListener(hostnameIPAddressEdit, hostnameIPAddress, portEdit, hostnameIPAddressCheck) + setEditButtonClickListener(portEdit, port, hostnameIPAddressEdit, portCheck) + setEndUserConsentClickListener() + setAboutClickListener() + setConnectCheckClickListener() + + // show view + return v + } + + // if view is ready restore set orientation + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + activity?.requestedOrientation = mySPR.getInt("orientation", 0) + super.onViewCreated(view, savedInstanceState) + } + + // restore last status + private fun statusRestoring() { + // restore chechbox status + connectCheck!!.isChecked = mySPR.getBoolean("connectCheck", false) + + // check if orientation was recently changed + if (!mySPR.getBoolean("tempDisableStart", false)) { + // check if autoStart is enabled + if (mySPR.getBoolean("autoStart", false) && connectCheck!!.isChecked) { + // open logview + fragmentManager!!.beginTransaction() + .replace(R.id.start, WebViewFragment()) + .addToBackStack(null) + .commit() + editor.putBoolean("connected", true).apply() + + // show toast + context?.let { Toasty.info(it, getString(R.string.connecting), Toasty.LENGTH_SHORT).show() } + } + } else { + // disable temporary lock after orientation was changed + val runnable = Runnable { editor.putBoolean("tempDisableStart", false).apply() } + Handler().postDelayed(runnable, 10) + } + + // set correct orientation icon + setOrientationIcon() + + // restore textbox status + if (mySPR.getString("hostnameIPAddressString", "") == "" || mySPR.getString("hostnameIPAddressString", "") == "0") { + hostnameIPAddress!!.setText("") + hostnameIPAddress!!.isEnabled = true + } else { + hostnameIPAddress!!.setText(mySPR.getString("hostnameIPAddressString", "0")) + hostnameIPAddressString = mySPR.getString("hostnameIPAddressString", "0") + hostnameIPAddress!!.isEnabled = false + } + + // restore checkbox status + hostnameIPAddressCheck!!.isChecked = mySPR.getBoolean("hostnameIPAddressCheck", true) + + // restore textbox status + if (mySPR.getInt("portInt", 0) == 0) { + port!!.setText("") + hostnameIPAddress!!.isEnabled = true + } else { + port!!.setText(mySPR.getInt("portInt", 0).toString()) + port!!.isEnabled = false + portInt = mySPR.getInt("portInt", 9001) + } + + // restore checkbox status + portCheck!!.isChecked = mySPR.getBoolean("portCheck", true) + + // check if connect was clicked and restore last status + if (hostnameIPAddress!!.text.toString().isNotEmpty() && port!!.text.toString().isNotEmpty()) { + linkGeneration() + hostnameIPAddressEdit!!.visibility = View.VISIBLE + portEdit!!.visibility = View.VISIBLE + hostnameIPAddressCheck!!.visibility = View.INVISIBLE + portCheck!!.visibility = View.INVISIBLE + connectCheck!!.visibility = View.VISIBLE + connectButton!!.text = getString(R.string.connect_button_2) + } + } + + // set correct orientation icon + private fun setOrientationIcon() { + when (mySPR.getInt("orientation", ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) { + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE -> orientation!!.setImageResource(R.drawable._icon_landscape_orientation) + ActivityInfo.SCREEN_ORIENTATION_PORTRAIT -> orientation!!.setImageResource(R.drawable._icon_portrait_orientation) + ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED -> orientation!!.setImageResource(R.drawable._icon_auto_orientation) + else -> { + } + } + } + + // generate and show new link according to user inputs + private fun linkGeneration() { + hostnameIPAddressString = hostnameIPAddress!!.text.toString() + portInt = if (port!!.text.toString().isNotEmpty()) { + port!!.text.toString().toInt() + } else { + 9001 + } + link = "http://$hostnameIPAddressString:$portInt" + linkView!!.text = link + } + + // onClickListener for connect button + private fun setConnectButtonClickListener(v: View) { + connectButton!!.setOnClickListener { view -> + // check if user entered hostname + if (hostnameIPAddress!!.text.toString().isNotEmpty()) { + // if connect button was not clicked before + if (linkView!!.text.toString().isEmpty()) { + // generate new link + linkGeneration() + + // switch all elements + hostnameIPAddress!!.isEnabled = false + hostnameIPAddressEdit!!.visibility = View.VISIBLE + port!!.isEnabled = false + portEdit!!.visibility = View.VISIBLE + hostnameIPAddressCheck!!.visibility = View.INVISIBLE + portCheck!!.visibility = View.INVISIBLE + connectCheck!!.visibility = View.VISIBLE + + // store values if user wants to + if (hostnameIPAddressCheck!!.isChecked) { + editor.putString("hostnameIPAddressString", hostnameIPAddressString) + editor.putBoolean("hostnameIPAddressCheck", true) + } else { + editor.putString("hostnameIPAddressString", "") + editor.putBoolean("hostnameIPAddressCheck", false) + } + + // store values if user wants to + if (portCheck!!.isChecked) { + editor.putBoolean("portCheck", true) + + // check if user entered port + if (port!!.text.toString().isNotEmpty()) { + editor.putInt("portInt", portInt) + } else { + port!!.setText(9001.toString()) + editor.putInt("portInt", 9001) + } + } else { + editor.putInt("portInt", 0) + editor.putBoolean("portCheck", false) + } + + // store link + editor.putString("link", link).apply() + + // change button text + connectButton!!.text = getString(R.string.connect_button_2) + } else { + // open logview + fragmentManager!!.beginTransaction() + .replace(R.id.start, WebViewFragment()) + .addToBackStack(null) + .commit() + editor.putBoolean("connected", true).apply() + // close keyboard + if (view != null) { + val imm = + (view.context.applicationContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager) + imm.hideSoftInputFromWindow(view.windowToken, 0) + } + + // show toast + Toasty.info(v.context, getString(R.string.connecting), Toasty.LENGTH_SHORT).show() + } + } else { + // show error if one field or both fields are empty + Toasty.error(v.context, getString(R.string.error_fill_out), Toasty.LENGTH_LONG).show() + } + } + } + + // onClickListener for orientation icon + private fun setOrientationIconClickListener() { + orientation!!.setOnClickListener { + var newOrientation = 0 + var newOrientationName = "" + when (mySPR.getInt("orientation", ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)) { + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE -> { + newOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + newOrientationName = "portrait" + } + ActivityInfo.SCREEN_ORIENTATION_PORTRAIT -> { + newOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + newOrientationName = "auto" + } + ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED -> { + newOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + newOrientationName = "landscape" + } + else -> { + } + } + + // store orientation, change icon and change orientation + editor.putInt("orientation", newOrientation) + editor.putBoolean("tempDisableStart", true).apply() + setOrientationIcon() + activity?.requestedOrientation = newOrientation + + // show toast + context?.let { it1 -> Toasty.info(it1, String.format(getString(R.string.orientation_changed), newOrientationName), Toasty.LENGTH_SHORT).show() } + } + } + + // onClickListener for both edit buttons + private fun setEditButtonClickListener(imageButton: ImageButton?, textView: TextView?, imageButton2: ImageButton?, checkBox: CheckBox?) { + imageButton!!.setOnClickListener { // switch all elements + textView!!.isEnabled = true + imageButton.visibility = View.INVISIBLE + imageButton2!!.visibility = View.INVISIBLE + checkBox!!.visibility = View.VISIBLE + connectCheck!!.visibility = View.INVISIBLE + linkView!!.text = "" + connectButton!!.text = getString(R.string.connect_button_1) + } + } + + // onClickListener for end user consent textview + private fun setEndUserConsentClickListener() { + endUserConsent!!.setOnClickListener(object : View.OnClickListener { + override fun onClick(view: View) { + // start end user consent class (and show second dialog type ("false")) + val endUserConsent = EndUserConsent(false) + endUserConsent.isCancelable = true + endUserConsent.show(childFragmentManager, javaClass.name) + } + }) + } + + // onClickListener for about textview + private fun setAboutClickListener() { + about!!.setOnClickListener { view -> // start about class + view.context.startActivity(Intent(context, About::class.java)) + } + } + + // onClickListener for connect checkbox + private fun setConnectCheckClickListener() { + connectCheck!!.setOnCheckedChangeListener { _, _ -> // store values + editor.putBoolean("connectCheck", connectCheck!!.isChecked) + editor.putBoolean("autoStart", connectCheck!!.isChecked).apply() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/PrivacyPolicy.java b/app/src/main/java/com/cyb3rko/logviewerforopenhab/PrivacyPolicy.java deleted file mode 100644 index 41cb3af..0000000 --- a/app/src/main/java/com/cyb3rko/logviewerforopenhab/PrivacyPolicy.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.cyb3rko.logviewerforopenhab; - -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.ActivityInfo; -import android.net.Uri; -import android.os.Bundle; -import android.view.View; -import android.widget.TextView; - -import androidx.appcompat.app.AppCompatActivity; - -import com.google.android.material.floatingactionbutton.FloatingActionButton; - -public class PrivacyPolicy extends AppCompatActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // load save file - SharedPreferences mySPR = this.getSharedPreferences("Safe", 0); - // restore set orientation - setRequestedOrientation(mySPR.getInt("orientation", ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)); - // set view - setContentView(R.layout.activity_privacy_policy); - - // set text of textviews - TextView[] textViews = new TextView[21]; - - for (int i = 1; i <= 21; i++) { - textViews[i-1] = findViewById(getResources().getIdentifier("textView" + i, "id", getPackageName())); - textViews[i-1].setText(getResources().getStringArray(R.array.privacy_policy)[i-1]); - } - - // floating action button clicklistener - FloatingActionButton fab = findViewById(R.id.fab); - fab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - // start intent for writing an e mail - Intent mailIntent = new Intent(Intent.ACTION_VIEW); - Uri data = Uri.parse(String.format(getString(R.string.mail_info), getString(R.string.app_name), getResources().getStringArray(R.array.privacy_policy)[0])); - mailIntent.setData(data); - startActivity(Intent.createChooser(mailIntent, getString(R.string.send_mail))); - } - }); - } -} diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/PrivacyPolicy.kt b/app/src/main/java/com/cyb3rko/logviewerforopenhab/PrivacyPolicy.kt new file mode 100644 index 0000000..410da72 --- /dev/null +++ b/app/src/main/java/com/cyb3rko/logviewerforopenhab/PrivacyPolicy.kt @@ -0,0 +1,43 @@ +package com.cyb3rko.logviewerforopenhab + +import android.content.Intent +import android.content.pm.ActivityInfo +import android.net.Uri +import android.os.Bundle +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import com.google.android.material.floatingactionbutton.FloatingActionButton + +class PrivacyPolicy : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // load save file + val mySPR = getSharedPreferences("Safe", 0) + // restore set orientation + requestedOrientation = mySPR.getInt("orientation", ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) + // set view + setContentView(R.layout.activity_privacy_policy) + + // set text of textviews + val textViews = arrayOfNulls(21) + for (i in 1..21) { + textViews[i - 1] = findViewById(resources.getIdentifier("textView$i", "id", packageName)) + textViews[i - 1]?.text = (resources.getStringArray(R.array.privacy_policy)[i - 1]) + } + + // floating action button clicklistener + val fab = findViewById(R.id.fab) + fab.setOnClickListener { // start intent for writing an e mail + val mailIntent = Intent(Intent.ACTION_VIEW) + val data = Uri.parse( + String.format( + getString(R.string.mail_info), getString(R.string.app_name), + resources.getStringArray(R.array.privacy_policy)[0] + ) + ) + mailIntent.data = data + startActivity(Intent.createChooser(mailIntent, getString(R.string.send_mail))) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/TermsOfUse.java b/app/src/main/java/com/cyb3rko/logviewerforopenhab/TermsOfUse.java deleted file mode 100644 index 00e548c..0000000 --- a/app/src/main/java/com/cyb3rko/logviewerforopenhab/TermsOfUse.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.cyb3rko.logviewerforopenhab; - -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.ActivityInfo; -import android.net.Uri; -import android.os.Bundle; -import android.view.View; -import android.widget.TextView; - -import androidx.appcompat.app.AppCompatActivity; - -import com.google.android.material.floatingactionbutton.FloatingActionButton; - -public class TermsOfUse extends AppCompatActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // load save file - SharedPreferences mySPR = this.getSharedPreferences("Safe", 0); - // restore set orientation - setRequestedOrientation(mySPR.getInt("orientation", ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)); - // set view - setContentView(R.layout.activity_terms_of_use); - - // set text of textviews - TextView[] textViews = new TextView[7]; - - for (int i = 1; i <= 7; i++) { - textViews[i-1] = findViewById(getResources().getIdentifier("textView" + i, "id", getPackageName())); - textViews[i-1].setText(getResources().getStringArray(R.array.terms_of_use)[i-1]); - } - - // floating action button clicklistener - FloatingActionButton fab = findViewById(R.id.fab); - fab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - // start intent for writing an e mail - Intent mailIntent = new Intent(Intent.ACTION_VIEW); - Uri data = Uri.parse(String.format(getString(R.string.mail_info), getString(R.string.app_name), getResources().getStringArray(R.array.terms_of_use)[0])); - mailIntent.setData(data); - startActivity(Intent.createChooser(mailIntent, getString(R.string.send_mail))); - } - }); - } -} diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/TermsOfUse.kt b/app/src/main/java/com/cyb3rko/logviewerforopenhab/TermsOfUse.kt new file mode 100644 index 0000000..af4d6d6 --- /dev/null +++ b/app/src/main/java/com/cyb3rko/logviewerforopenhab/TermsOfUse.kt @@ -0,0 +1,43 @@ +package com.cyb3rko.logviewerforopenhab + +import android.content.Intent +import android.content.pm.ActivityInfo +import android.net.Uri +import android.os.Bundle +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import com.google.android.material.floatingactionbutton.FloatingActionButton + +class TermsOfUse : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // load save file + val mySPR = getSharedPreferences("Safe", 0) + // restore set orientation + requestedOrientation = mySPR.getInt("orientation", ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) + // set view + setContentView(R.layout.activity_terms_of_use) + + // set text of textviews + val textViews = arrayOfNulls(7) + for (i in 1..7) { + textViews[i - 1] = findViewById(resources.getIdentifier("textView$i", "id", packageName)) + textViews[i - 1]?.text = (resources.getStringArray(R.array.terms_of_use)[i - 1]) + } + + // floating action button clicklistener + val fab = findViewById(R.id.fab) + fab.setOnClickListener { // start intent for writing an e mail + val mailIntent = Intent(Intent.ACTION_VIEW) + val data = Uri.parse( + String.format( + getString(R.string.mail_info), getString(R.string.app_name), + resources.getStringArray(R.array.terms_of_use)[0] + ) + ) + mailIntent.data = data + startActivity(Intent.createChooser(mailIntent, getString(R.string.send_mail))) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/UpdateDialog.java b/app/src/main/java/com/cyb3rko/logviewerforopenhab/UpdateDialog.java deleted file mode 100644 index 07dc11f..0000000 --- a/app/src/main/java/com/cyb3rko/logviewerforopenhab/UpdateDialog.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.cyb3rko.logviewerforopenhab; - -import android.Manifest; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DownloadManager; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.graphics.Typeface; -import android.net.Uri; -import android.os.Bundle; -import android.os.Environment; -import android.text.SpannableString; -import android.text.Spanned; -import android.text.method.LinkMovementMethod; -import android.text.style.ClickableSpan; -import android.view.Gravity; -import android.view.View; -import android.webkit.URLUtil; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatDialogFragment; -import androidx.core.content.ContextCompat; - -import java.util.Objects; - -import es.dmoral.toasty.Toasty; - -import static android.content.Context.DOWNLOAD_SERVICE; - -public class UpdateDialog extends AppCompatDialogFragment { - @NonNull - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - // load save file and its editor - final SharedPreferences mySPR = Objects.requireNonNull(getActivity()).getSharedPreferences("Safe", 0); - final SharedPreferences.Editor editor = mySPR.edit(); - editor.apply(); - - // create new dialog builder - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - - // create title - TextView titleView = new TextView(getContext()); - titleView.setPadding(32, 32, 32, 32); - titleView.setGravity(Gravity.CENTER_HORIZONTAL); - titleView.setTypeface(Typeface.DEFAULT_BOLD); - titleView.setTextSize(22); - titleView.setText(getString(R.string.update_dialog_title)); - - // create text - TextView messageView = new TextView(getContext()); - messageView.setPadding(32, 32, 32, 32); - messageView.setGravity(Gravity.CENTER_HORIZONTAL); - messageView.setTextSize(16); - ClickableSpan clickableSpan = new ClickableSpan() { - @Override - public void onClick(@NonNull View view) { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.update_changelog_link)))); - } - }; - String message = String.format(getString(R.string.update_dialog_message), mySPR.getString("newestVersion", ""), BuildConfig.VERSION_NAME); - String index = getString(R.string.update_dialog_changelog); - SpannableString spannableString = new SpannableString(message); - spannableString.setSpan(clickableSpan, message.indexOf(index), message.indexOf(index) + index.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - messageView.setText(spannableString); - messageView.setMovementMethod(LinkMovementMethod.getInstance()); - - // create dialog - builder.setCustomTitle(titleView) - .setView(messageView) - .setCancelable(false) - // add right button - .setPositiveButton(getString(R.string.update_dialog_button_1), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - // if permission are given - if (ContextCompat.checkSelfPermission(Objects.requireNonNull(getContext()), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { - // create link - String link = String.format(getString(R.string.update_download_link), mySPR.getString("newestVersion", "")); - // download apk file - DownloadManager.Request request = new DownloadManager.Request(Uri.parse(link)) - .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, URLUtil.guessFileName(link, null, null)) - .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); - DownloadManager downloadManager = (DownloadManager) Objects.requireNonNull(getActivity()).getSystemService(DOWNLOAD_SERVICE); - assert downloadManager != null; - downloadManager.enqueue(request); - } else { - // give error that permissions are not given - Toasty.error(getContext(), getString(R.string.update_dialog_error), Toasty.LENGTH_LONG).show(); - } - } - }) - // add left button - .setNegativeButton(getString(R.string.update_dialog_button_2), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - // nothing to clean up (for PMD) - } - }); - - // show dialog - return builder.create(); - } -} diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/UpdateDialog.kt b/app/src/main/java/com/cyb3rko/logviewerforopenhab/UpdateDialog.kt new file mode 100644 index 0000000..d95c1d7 --- /dev/null +++ b/app/src/main/java/com/cyb3rko/logviewerforopenhab/UpdateDialog.kt @@ -0,0 +1,90 @@ +package com.cyb3rko.logviewerforopenhab + +import android.Manifest +import android.app.AlertDialog +import android.app.Dialog +import android.app.DownloadManager +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.graphics.Typeface +import android.net.Uri +import android.os.Bundle +import android.os.Environment +import android.text.SpannableString +import android.text.Spanned +import android.text.method.LinkMovementMethod +import android.text.style.ClickableSpan +import android.view.Gravity +import android.view.View +import android.webkit.URLUtil +import android.widget.TextView +import androidx.appcompat.app.AppCompatDialogFragment +import androidx.core.content.ContextCompat +import es.dmoral.toasty.Toasty + +class UpdateDialog : AppCompatDialogFragment() { + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + // load save file and its editor + val mySPR = activity?.getSharedPreferences("Safe", 0) + val editor = mySPR?.edit() + editor?.apply() + + // create new dialog builder + val builder = AlertDialog.Builder(activity) + + // create title + val titleView = TextView(context) + titleView.setPadding(32, 32, 32, 32) + titleView.gravity = Gravity.CENTER_HORIZONTAL + titleView.typeface = Typeface.DEFAULT_BOLD + titleView.textSize = 22f + titleView.text = getString(R.string.update_dialog_title) + + // create text + val messageView = TextView(context) + messageView.setPadding(32, 32, 32, 32) + messageView.gravity = Gravity.CENTER_HORIZONTAL + messageView.textSize = 16f + val clickableSpan: ClickableSpan = object : ClickableSpan() { + override fun onClick(view: View) { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.update_changelog_link)))) + } + } + val message = String.format(getString(R.string.update_dialog_message), mySPR?.getString("newestVersion", ""), BuildConfig.VERSION_NAME) + val index = getString(R.string.update_dialog_changelog) + val spannableString = SpannableString(message) + spannableString.setSpan(clickableSpan, message.indexOf(index), message.indexOf(index) + index.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + messageView.text = spannableString + messageView.movementMethod = LinkMovementMethod.getInstance() + + // create dialog + builder.setCustomTitle(titleView) + .setView(messageView) + .setCancelable(false) // add right button + .setPositiveButton(getString(R.string.update_dialog_button_1)) { _, _ -> + // if permission are given + if (context?.let { + ContextCompat.checkSelfPermission(it, Manifest.permission.WRITE_EXTERNAL_STORAGE) } == PackageManager.PERMISSION_GRANTED) { + // create link + val link = String.format(getString(R.string.update_download_link), mySPR?.getString("newestVersion", "")) + // download apk file + val request = DownloadManager.Request(Uri.parse(link)).setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, + URLUtil.guessFileName(link, null, null) + ) + .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) + val downloadManager = activity?.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager + downloadManager.enqueue(request) + } else { + // give error that permissions are not given + Toasty.error(context!!, getString(R.string.update_dialog_error), Toasty.LENGTH_LONG).show() + } + } // add left button + .setNegativeButton(getString(R.string.update_dialog_button_2)) { _, _ -> + // nothing to clean up (for PMD) + } + + // show dialog + return builder.create() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/WebViewFragment.java b/app/src/main/java/com/cyb3rko/logviewerforopenhab/WebViewFragment.java deleted file mode 100644 index 5f37a1d..0000000 --- a/app/src/main/java/com/cyb3rko/logviewerforopenhab/WebViewFragment.java +++ /dev/null @@ -1,321 +0,0 @@ -package com.cyb3rko.logviewerforopenhab; - -import android.annotation.SuppressLint; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.graphics.Typeface; -import android.os.Bundle; -import android.os.Handler; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.webkit.WebSettings; -import android.webkit.WebView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import androidx.appcompat.app.AlertDialog; -import androidx.fragment.app.Fragment; - -import com.getkeepsafe.taptargetview.TapTarget; -import com.getkeepsafe.taptargetview.TapTargetSequence; -import com.google.android.material.floatingactionbutton.FloatingActionButton; - -import org.adw.library.widgets.discreteseekbar.DiscreteSeekBar; - -import java.util.Objects; - -import es.dmoral.toasty.Toasty; - -public class WebViewFragment extends Fragment { - private FloatingActionButton backButton; - private FloatingActionButton textButton; - private FloatingActionButton viewButton; - private SharedPreferences mySPR; - private SharedPreferences.Editor editor; - private String textSizeType; - private WebSettings webSettings; - private WebView webView; - - private boolean viewLocked = true; - - @SuppressLint("SetJavaScriptEnabled") - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View v = inflater.inflate(R.layout.fragment_web_view, container, false); - - webView = v.findViewById(R.id.webview); - viewButton = v.findViewById(R.id.view_button); - textButton = v.findViewById(R.id.text_button); - backButton = v.findViewById(R.id.back_button); - - // load save file and its editor - mySPR = v.getContext().getSharedPreferences("Safe", 0); - editor = mySPR.edit(); - editor.apply(); - - // adapt settings for webview - webSettings = webView.getSettings(); - webSettings.setJavaScriptEnabled(true); - v.setVerticalScrollBarEnabled(false); - setTouchable(false); - - // restore text size (according to orientation) - if (getResources().getConfiguration().orientation == 1) { - textSizeType = "textSizePortrait"; - } else { - textSizeType = "textSizeOther"; - } - - webSettings.setTextZoom(mySPR.getInt(textSizeType, 60)); - - // open link - webView.loadUrl(mySPR.getString("link", "")); - - // permanent scroll - final Handler handler = new Handler(); - Runnable runnable = new Runnable() { - @Override - public void run() { - if (viewLocked) { - webView.scrollBy(0, 10000); - } - - handler.postDelayed(this, 1000); - } - }; - handler.postDelayed(runnable, 500); - - setViewButtonClickListener(); - setTextButtonClickListener(v); - setBackButtonClickListener(); - - showTapTargetSequence(v); - - return v; - } - - // onClickListener for viwe button - private void setViewButtonClickListener() { - viewButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - // if scrolling locked - if (viewLocked) { - viewButton.setImageResource(R.drawable._icon_lock_2); - viewLocked = false; - setTouchable(true); - - // show toast - Toasty.info(view.getContext(), getString(R.string.lock_button_1), Toasty.LENGTH_SHORT).show(); - } else { - viewButton.setImageResource(R.drawable._icon_lock); - viewLocked = true; - setTouchable(false); - - //scroll to bottom - webView.scrollBy(0, 10000); - - // show toast - Toasty.info(view.getContext(), getString(R.string.lock_button_2), Toasty.LENGTH_SHORT).show(); - } - } - }); - } - - // dialog to change text size - private void setTextButtonClickListener(final View v) { - textButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(final View view) { - // create dialog builder - final AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext()); - - // current text size - final int currentTextSize = mySPR.getInt(textSizeType, 60); - // add textview for showing text size - final TextView sizeView = new TextView(v.getContext()); - // add seekbar for choosing text size - final DiscreteSeekBar discreteSeekBar = new DiscreteSeekBar(v.getContext()); - - // create title - TextView titleView = new TextView(v.getContext()); - titleView.setText(R.string.text_size_dialog_title); - titleView.setTextSize(22); - titleView.setTypeface(Typeface.DEFAULT_BOLD); - titleView.setPadding(32, 32, 32, 32); - titleView.setGravity(Gravity.CENTER_HORIZONTAL); - - LinearLayout content = new LinearLayout(v.getContext()); - content.setOrientation(LinearLayout.VERTICAL); - - // set size view - sizeView.setText(String.format(getString(R.string.text_size_dialog_text), currentTextSize, currentTextSize)); - sizeView.setTextSize(18); - sizeView.setPadding(24, 24, 24, 50); - sizeView.setGravity(Gravity.CENTER_HORIZONTAL); - content.addView(sizeView); - - // set seekbar - discreteSeekBar.setMax(100); - discreteSeekBar.setMin(1); - discreteSeekBar.setProgress(webSettings.getTextZoom()); - discreteSeekBar.setPadding(50,0,50,50); - discreteSeekBar.setOnProgressChangeListener(new DiscreteSeekBar.OnProgressChangeListener() { - @Override - public void onProgressChanged(DiscreteSeekBar seekBar, int value, boolean fromUser) { - // nothing to clean up (for PMD) - } - - @Override - public void onStartTrackingTouch(DiscreteSeekBar seekBar) { - // nothing to clean up (for PMD) - } - - // if user stops touching seekbar - @Override - public void onStopTrackingTouch(DiscreteSeekBar seekBar) { - sizeView.setText(String.format(getString(R.string.text_size_dialog_text), currentTextSize, discreteSeekBar.getProgress())); - } - }); - content.addView(discreteSeekBar); - - // create dialog - builder.setCustomTitle(titleView) - .setView(content) - // add right button - .setPositiveButton(getString(R.string.text_size_dialog_button_1), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - int textSize = discreteSeekBar.getProgress(); - - // change text size - webSettings.setTextZoom(textSize); - // store new size - editor.putInt(textSizeType, textSize).apply(); - // show toast - Toasty.success(view.getContext(), getString(R.string.text_size_changed) + textSize, Toasty.LENGTH_SHORT).show(); - } - }) - // add left button - .setNegativeButton(getString(R.string.text_size_dialog_button_2), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - // nothing to clean up (for PMD) - } - }) - // show dialog - .create().show(); - } - }); - } - - // onClickListener for back button - private void setBackButtonClickListener() { - backButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - // reset autoStart temporarily to be able to switch to menu without directly reconnecting - final boolean autoStartTemp = mySPR.getBoolean("autoStart", false); - editor.putBoolean("autoStart", false).apply(); - assert getFragmentManager() != null; - getFragmentManager().beginTransaction().replace(R.id.start, new MainFragment()).commit(); - - final Handler handler2 = new Handler(); - Runnable runnable2 = new Runnable() { - @Override - public void run() { - editor.putBoolean("autoStart", autoStartTemp).apply(); - } - }; - - handler2.postDelayed(runnable2, 1); - editor.putBoolean("connected", false).apply(); - } - }); - } - - // taptargetsequence to explain buttons - private void showTapTargetSequence(View v) { - // if first log visit - if (mySPR.getBoolean("firstStartWeb", true)) { - new TapTargetSequence(Objects.requireNonNull(getActivity())) - .targets( - // first target (view button) - TapTarget.forView(v.findViewById(R.id.view_button), getString(R.string.tap_target_title_1), getString(R.string.tap_target_message_1)) - // many settings - .transparentTarget(true) - .tintTarget(false) - .outerCircleColor(R.color.colorAccent) - .tintTarget(false) - .titleTextSize(18) - .descriptionTextSize(16) - .drawShadow(true) - .cancelable(false), - // first target (text button) - TapTarget.forView(v.findViewById(R.id.text_button), getString(R.string.tap_target_title_2), getString(R.string.tap_target_message_2)) - // many settings - .transparentTarget(true) - .tintTarget(false) - .outerCircleColor(R.color.colorAccent) - .tintTarget(false) - .titleTextSize(18) - .descriptionTextSize(16) - .drawShadow(true) - .cancelable(false), - // first target (back button) - TapTarget.forView(v.findViewById(R.id.back_button), getString(R.string.tap_target_title_3), getString(R.string.tap_target_message_3)) - // many settings - .transparentTarget(true) - .tintTarget(false) - .outerCircleColor(R.color.colorAccent) - .tintTarget(false) - .titleTextSize(18) - .descriptionTextSize(16) - .drawShadow(true) - .cancelable(false) - ).listener(new TapTargetSequence.Listener() { - @Override - public void onSequenceFinish() { - editor.putBoolean("firstStartWeb", false).apply(); - } - - // on every single new target - @Override - public void onSequenceStep(TapTarget lastTarget, boolean targetClicked) { - // nothing to clean up (for PMD) - } - - // if sequence has been canceled - @Override - public void onSequenceCanceled(TapTarget lastTarget) { - // nothing to clean up (for PMD) - } - }) - // start sequence - .start(); - } - } - - // switch touchability of log - @SuppressLint("ClickableViewAccessibility") - private void setTouchable(boolean b) { - if (b) { - webView.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View view, MotionEvent motionEvent) { - return false; - } - }); - } else { - webView.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View view, MotionEvent motionEvent) { - return true; - } - }); - } - } -} diff --git a/app/src/main/java/com/cyb3rko/logviewerforopenhab/WebViewFragment.kt b/app/src/main/java/com/cyb3rko/logviewerforopenhab/WebViewFragment.kt new file mode 100644 index 0000000..3aa7a9f --- /dev/null +++ b/app/src/main/java/com/cyb3rko/logviewerforopenhab/WebViewFragment.kt @@ -0,0 +1,267 @@ +package com.cyb3rko.logviewerforopenhab + +import android.annotation.SuppressLint +import android.content.SharedPreferences +import android.graphics.Typeface +import android.os.Bundle +import android.os.Handler +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.webkit.WebSettings +import android.webkit.WebView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.Fragment +import com.getkeepsafe.taptargetview.TapTarget +import com.getkeepsafe.taptargetview.TapTargetSequence +import com.google.android.material.floatingactionbutton.FloatingActionButton +import es.dmoral.toasty.Toasty +import org.adw.library.widgets.discreteseekbar.DiscreteSeekBar +import org.adw.library.widgets.discreteseekbar.DiscreteSeekBar.OnProgressChangeListener +import java.util.* + +class WebViewFragment : Fragment() { + private lateinit var backButton: FloatingActionButton + private lateinit var textButton: FloatingActionButton + private lateinit var viewButton: FloatingActionButton + private lateinit var mySPR: SharedPreferences + private lateinit var editor: SharedPreferences.Editor + private lateinit var textSizeType: String + private lateinit var webSettings: WebSettings + private lateinit var webView: WebView + private var viewLocked = true + + @SuppressLint("SetJavaScriptEnabled") + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val v = inflater.inflate(R.layout.fragment_web_view, container, false) + webView = v.findViewById(R.id.webview) + viewButton = v.findViewById(R.id.view_button) + textButton = v.findViewById(R.id.text_button) + backButton = v.findViewById(R.id.back_button) + + // load save file and its editor + mySPR = v.context.getSharedPreferences("Safe", 0) + editor = mySPR.edit() + editor.apply() + + // adapt settings for webview + webSettings = webView.settings + webSettings.javaScriptEnabled = true + v.isVerticalScrollBarEnabled = false + setTouchable(false) + + // restore text size (according to orientation) + textSizeType = if (resources.configuration.orientation == 1) { + "textSizePortrait" + } else { + "textSizeOther" + } + webSettings.textZoom = mySPR.getInt(textSizeType, 60) + + // open link + webView.loadUrl(mySPR.getString("link", "")) + + // permanent scroll + val handler = Handler() + val runnable: Runnable = object : Runnable { + override fun run() { + if (viewLocked) { + webView.scrollBy(0, 10000) + } + handler.postDelayed(this, 1000) + } + } + handler.postDelayed(runnable, 500) + setViewButtonClickListener() + setTextButtonClickListener(v) + setBackButtonClickListener() + showTapTargetSequence(v) + return v + } + + // onClickListener for viwe button + private fun setViewButtonClickListener() { + viewButton.setOnClickListener { view -> + // if scrolling locked + if (viewLocked) { + viewButton.setImageResource(R.drawable._icon_lock_2) + viewLocked = false + setTouchable(true) + + // show toast + Toasty.info(view.context, getString(R.string.lock_button_1), Toasty.LENGTH_SHORT).show() + } else { + viewButton.setImageResource(R.drawable._icon_lock) + viewLocked = true + setTouchable(false) + + //scroll to bottom + webView.scrollBy(0, 10000) + + // show toast + Toasty.info(view.context, getString(R.string.lock_button_2), Toasty.LENGTH_SHORT).show() + } + } + } + + // dialog to change text size + private fun setTextButtonClickListener(v: View) { + textButton.setOnClickListener { view -> + // create dialog builder + val builder = AlertDialog.Builder(v.context) + + // current text size + val currentTextSize = mySPR.getInt(textSizeType, 60) + // add textview for showing text size + val sizeView = TextView(v.context) + // add seekbar for choosing text size + val discreteSeekBar = DiscreteSeekBar(v.context) + + // create title + val titleView = TextView(v.context) + titleView.setText(R.string.text_size_dialog_title) + titleView.textSize = 22f + titleView.typeface = Typeface.DEFAULT_BOLD + titleView.setPadding(32, 32, 32, 32) + titleView.gravity = Gravity.CENTER_HORIZONTAL + val content = LinearLayout(v.context) + content.orientation = LinearLayout.VERTICAL + + // set size view + sizeView.text = String.format(getString(R.string.text_size_dialog_text), currentTextSize, currentTextSize) + sizeView.textSize = 18f + sizeView.setPadding(24, 24, 24, 50) + sizeView.gravity = Gravity.CENTER_HORIZONTAL + content.addView(sizeView) + + // set seekbar + discreteSeekBar.max = 100 + discreteSeekBar.min = 1 + discreteSeekBar.progress = webSettings.textZoom + discreteSeekBar.setPadding(50, 0, 50, 50) + discreteSeekBar.setOnProgressChangeListener(object : OnProgressChangeListener { + override fun onProgressChanged(seekBar: DiscreteSeekBar, value: Int, fromUser: Boolean) { + // nothing to clean up (for PMD) + } + + override fun onStartTrackingTouch(seekBar: DiscreteSeekBar) { + // nothing to clean up (for PMD) + } + + // if user stops touching seekbar + override fun onStopTrackingTouch(seekBar: DiscreteSeekBar) { + sizeView.text = String.format(getString(R.string.text_size_dialog_text), currentTextSize, discreteSeekBar.progress) + } + }) + content.addView(discreteSeekBar) + + // create dialog + builder.setCustomTitle(titleView) + .setView(content) // add right button + .setPositiveButton(getString(R.string.text_size_dialog_button_1)) { _, _ -> + val textSize = discreteSeekBar.progress + + // change text size + webSettings.textZoom = textSize + // store new size + editor.putInt(textSizeType, textSize).apply() + // show toast + Toasty.success(view.context, getString(R.string.text_size_changed) + textSize, Toasty.LENGTH_SHORT).show() + } // add left button + .setNegativeButton(getString(R.string.text_size_dialog_button_2)) { _, _ -> + // nothing to clean up (for PMD) + } // show dialog + .create().show() + } + } + + // onClickListener for back button + private fun setBackButtonClickListener() { + backButton.setOnClickListener { // reset autoStart temporarily to be able to switch to menu without directly reconnecting + val autoStartTemp = mySPR.getBoolean("autoStart", false) + editor.putBoolean("autoStart", false).apply() + fragmentManager!!.beginTransaction().replace(R.id.start, MainFragment()).commit() + val handler2 = Handler() + val runnable2 = Runnable { editor.putBoolean("autoStart", autoStartTemp).apply() } + handler2.postDelayed(runnable2, 1) + editor.putBoolean("connected", false).apply() + } + } + + // taptargetsequence to explain buttons + private fun showTapTargetSequence(v: View) { + // if first log visit + if (mySPR.getBoolean("firstStartWeb", true)) { + TapTargetSequence(Objects.requireNonNull(activity)) + .targets( // first target (view button) + TapTarget.forView( + v.findViewById(R.id.view_button), + getString(R.string.tap_target_title_1), + getString(R.string.tap_target_message_1) + ) // many settings + .transparentTarget(true) + .tintTarget(false) + .outerCircleColor(R.color.colorAccent) + .tintTarget(false) + .titleTextSize(18) + .descriptionTextSize(16) + .drawShadow(true) + .cancelable(false), // first target (text button) + TapTarget.forView( + v.findViewById(R.id.text_button), + getString(R.string.tap_target_title_2), + getString(R.string.tap_target_message_2) + ) // many settings + .transparentTarget(true) + .tintTarget(false) + .outerCircleColor(R.color.colorAccent) + .tintTarget(false) + .titleTextSize(18) + .descriptionTextSize(16) + .drawShadow(true) + .cancelable(false), // first target (back button) + TapTarget.forView( + v.findViewById(R.id.back_button), + getString(R.string.tap_target_title_3), + getString(R.string.tap_target_message_3) + ) // many settings + .transparentTarget(true) + .tintTarget(false) + .outerCircleColor(R.color.colorAccent) + .tintTarget(false) + .titleTextSize(18) + .descriptionTextSize(16) + .drawShadow(true) + .cancelable(false) + ).listener(object : TapTargetSequence.Listener { + override fun onSequenceFinish() { + editor.putBoolean("firstStartWeb", false).apply() + } + + // on every single new target + override fun onSequenceStep(lastTarget: TapTarget, targetClicked: Boolean) { + // nothing to clean up (for PMD) + } + + // if sequence has been canceled + override fun onSequenceCanceled(lastTarget: TapTarget) { + // nothing to clean up (for PMD) + } + }) // start sequence + .start() + } + } + + // switch touchability of log + @SuppressLint("ClickableViewAccessibility") + private fun setTouchable(b: Boolean) { + if (b) { + webView.setOnTouchListener { _, _ -> false } + } else { + webView.setOnTouchListener { _, _ -> true } + } + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 84b99ac..de63d34 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + ext.kotlin_version = "1.4.10" repositories { google() jcenter() @@ -9,6 +10,7 @@ buildscript { classpath 'com.android.tools.build:gradle:4.0.0' classpath 'com.google.gms:google-services:4.3.3' classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:8.3.0" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files