From 0eecdff99ffe612112b82a008c3f46c332109dbe Mon Sep 17 00:00:00 2001 From: Finn M Glas Date: Sun, 24 May 2020 03:59:25 +0200 Subject: [PATCH] Clean and Refractor code. Split up the `SettingsActivity` into the smaller Fragments, Rename many ids... overall cleaner code. It is 03:56 am and I did not sleep yet... I won't today ^^ --- .../com/finnmglas/launcher/ChooseActivity.kt | 18 +- .../launcher/FirstStartupActivity.kt | 25 +- .../com/finnmglas/launcher/MainActivity.kt | 22 +- .../finnmglas/launcher/SettingsActivity.kt | 217 +----------------- .../launcher/SettingsFragmentApps.kt | 20 -- .../launcher/SettingsFragmentLauncher.kt | 20 -- .../launcher/SettingsFragmentTheme.kt | 20 -- .../launcher/{ => extern}/FontAwesome.kt | 7 +- .../launcher/{ => extern}/Functions.kt | 73 +++++- .../{ui/main => settings}/PageViewModel.kt | 3 +- .../main => settings}/SectionsPagerAdapter.kt | 10 +- .../launcher/settings/SettingsFragmentApps.kt | 98 ++++++++ .../launcher/settings/SettingsFragmentMeta.kt | 127 ++++++++++ .../settings/SettingsFragmentTheme.kt | 101 ++++++++ app/src/main/res/layout/activity_choose.xml | 17 +- .../main/res/layout/activity_firststartup.xml | 23 +- app/src/main/res/layout/activity_main.xml | 22 +- app/src/main/res/layout/activity_settings.xml | 20 +- .../res/layout/fragment_settings_apps.xml | 44 ++-- ...auncher.xml => fragment_settings_meta.xml} | 21 +- .../res/layout/fragment_settings_theme.xml | 28 +-- 21 files changed, 530 insertions(+), 406 deletions(-) delete mode 100644 app/src/main/java/com/finnmglas/launcher/SettingsFragmentApps.kt delete mode 100644 app/src/main/java/com/finnmglas/launcher/SettingsFragmentLauncher.kt delete mode 100644 app/src/main/java/com/finnmglas/launcher/SettingsFragmentTheme.kt rename app/src/main/java/com/finnmglas/launcher/{ => extern}/FontAwesome.kt (89%) rename app/src/main/java/com/finnmglas/launcher/{ => extern}/Functions.kt (83%) rename app/src/main/java/com/finnmglas/launcher/{ui/main => settings}/PageViewModel.kt (82%) rename app/src/main/java/com/finnmglas/launcher/{ui/main => settings}/SectionsPagerAdapter.kt (80%) create mode 100644 app/src/main/java/com/finnmglas/launcher/settings/SettingsFragmentApps.kt create mode 100644 app/src/main/java/com/finnmglas/launcher/settings/SettingsFragmentMeta.kt create mode 100644 app/src/main/java/com/finnmglas/launcher/settings/SettingsFragmentTheme.kt rename app/src/main/res/layout/{fragment_settings_launcher.xml => fragment_settings_meta.xml} (76%) diff --git a/app/src/main/java/com/finnmglas/launcher/ChooseActivity.kt b/app/src/main/java/com/finnmglas/launcher/ChooseActivity.kt index a626469..24aa627 100644 --- a/app/src/main/java/com/finnmglas/launcher/ChooseActivity.kt +++ b/app/src/main/java/com/finnmglas/launcher/ChooseActivity.kt @@ -10,6 +10,7 @@ import android.view.WindowManager import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import com.finnmglas.launcher.extern.* import kotlinx.android.synthetic.main.activity_choose.* @@ -33,19 +34,18 @@ class ChooseActivity : AppCompatActivity() { setContentView(R.layout.activity_choose) // As older APIs somehow do not recognize the xml defined onClick - close_chooser.setOnClickListener() { finish() } + activity_choose_close.setOnClickListener() { finish() } val bundle = intent.extras val action = bundle!!.getString("action") // why choose an app val forApp = bundle.getString("forApp") // which app we choose if (action == "launch") - heading.text = getString(R.string.choose_title_launch) - else if (action == "pick") { - heading.text = getString(R.string.choose_title) - } + activity_choose_heading.text = getString(R.string.choose_title_launch) + else if (action == "pick") + activity_choose_heading.text = getString(R.string.choose_title) else if (action == "uninstall") - heading.text = getString(R.string.choose_title_remove) + activity_choose_heading.text = getString(R.string.choose_title_remove) /* Build Layout */ @@ -78,10 +78,12 @@ class ChooseActivity : AppCompatActivity() { val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE) intent.data = Uri.parse("package:" + app.packageName) intent.putExtra(Intent.EXTRA_RETURN_RESULT, true) - startActivityForResult(intent, REQUEST_UNINSTALL) + startActivityForResult(intent, + REQUEST_UNINSTALL + ) } } - apps_list.addView(tvdynamic) + activity_choose_apps_list.addView(tvdynamic) } } diff --git a/app/src/main/java/com/finnmglas/launcher/FirstStartupActivity.kt b/app/src/main/java/com/finnmglas/launcher/FirstStartupActivity.kt index 58e9399..bfc1a4c 100644 --- a/app/src/main/java/com/finnmglas/launcher/FirstStartupActivity.kt +++ b/app/src/main/java/com/finnmglas/launcher/FirstStartupActivity.kt @@ -5,9 +5,10 @@ import android.content.SharedPreferences import android.os.Bundle import android.util.TypedValue import android.view.* -import android.view.animation.AlphaAnimation -import android.view.animation.Animation import androidx.appcompat.app.AppCompatActivity +import com.finnmglas.launcher.extern.blink +import com.finnmglas.launcher.extern.getSavedTheme +import com.finnmglas.launcher.extern.resetSettings import kotlinx.android.synthetic.main.activity_firststartup.* @@ -40,7 +41,7 @@ class FirstStartupActivity : AppCompatActivity(){ ) setContentView(R.layout.activity_firststartup) - hintText.blink() // animate + activity_firststartup_hint_text.blink() // animate loadMenu(this) val sharedPref = this.getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE) @@ -50,10 +51,10 @@ class FirstStartupActivity : AppCompatActivity(){ if (isFirstTime) defaultApps = resetSettings(sharedPref, this) // UP, DOWN, RIGHT, LEFT, VOLUME_UP, VOLUME_DOWN else - app_bar.visibility = View.VISIBLE + activity_firststartup_app_bar.visibility = View.VISIBLE // As older APIs somehow do not recognize the xml defined onClick - close_tutorial.setOnClickListener() { finish() } + activity_firststartup_close.setOnClickListener() { finish() } } /** Touch- and Key-related functions to navigate */ @@ -92,14 +93,16 @@ class FirstStartupActivity : AppCompatActivity(){ if (menuNumber < intro.size){ val entry = intro[menuNumber].split("|").toTypedArray() //heading|infoText|hintText|size - heading.text = entry[0] - if (entry[4] == "1" && isFirstTime)infoText.text = String.format(entry[1], + activity_firststartup_section_heading.text = entry[0] + if (entry[4] == "1" && isFirstTime) + activity_firststartup_descriptive_text.text = String.format(entry[1], defaultApps[0], defaultApps[1], defaultApps[2], defaultApps[3], defaultApps[4], defaultApps[5]) - else if (entry[4] == "1" && !isFirstTime)infoText.text = String.format(entry[1], + else if (entry[4] == "1" && !isFirstTime) + activity_firststartup_descriptive_text.text = String.format(entry[1], "-", "-", "-", "-", "-", "-") - else infoText.text = entry[1] - hintText.text = entry[2] - infoText.setTextSize(TypedValue.COMPLEX_UNIT_SP, entry[3].toFloat()) + else activity_firststartup_descriptive_text.text = entry[1] + activity_firststartup_hint_text.text = entry[2] + activity_firststartup_descriptive_text.setTextSize(TypedValue.COMPLEX_UNIT_SP, entry[3].toFloat()) } else { // End intro if (isFirstTime){ diff --git a/app/src/main/java/com/finnmglas/launcher/MainActivity.kt b/app/src/main/java/com/finnmglas/launcher/MainActivity.kt index cc09fbf..75cd445 100644 --- a/app/src/main/java/com/finnmglas/launcher/MainActivity.kt +++ b/app/src/main/java/com/finnmglas/launcher/MainActivity.kt @@ -8,6 +8,7 @@ import android.util.DisplayMetrics import android.view.* import androidx.appcompat.app.AppCompatActivity import androidx.core.view.GestureDetectorCompat +import com.finnmglas.launcher.extern.* import kotlinx.android.synthetic.main.activity_main.* import java.text.SimpleDateFormat import java.util.* @@ -66,7 +67,7 @@ class MainActivity : AppCompatActivity(), showSettingsIcon() // As older APIs somehow do not recognize the xml defined onClick - settingstooltip.setOnClickListener() { openSettings() } + activity_main_settings_icon.setOnClickListener() { openSettings() } // First Startup if (!sharedPref.getBoolean("startedBefore", false)){ @@ -95,8 +96,8 @@ class MainActivity : AppCompatActivity(), // TODO: do this immediately after changing preferences if (currentTheme != getSavedTheme(this)) recreate() - if (background_image != null && getSavedTheme(this) == "custom") - background_image.setImageBitmap(background) + if (activity_main_background_image != null && getSavedTheme(this) == "custom") + activity_main_background_image.setImageBitmap(background) val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) val timeFormat = SimpleDateFormat("HH:mm:ss", Locale.getDefault()) @@ -104,9 +105,12 @@ class MainActivity : AppCompatActivity(), clockTimer = fixedRateTimer("clockTimer", true, 0L, 100) { this@MainActivity.runOnUiThread { val t = timeFormat.format(Date()) - if (timeView.text != t) timeView.text = t + if (activity_main_time_view.text != t) + activity_main_time_view.text = t + val d = dateFormat.format(Date()) - if (dateView.text != d) dateView.text = d + if (activity_main_date_view.text != d) + activity_main_date_view.text = d } } @@ -173,8 +177,8 @@ class MainActivity : AppCompatActivity(), } private fun showSettingsIcon(){ - settingstooltip.fadeRotateIn() - settingstooltip.visibility = View.VISIBLE + activity_main_settings_icon.fadeRotateIn() + activity_main_settings_icon.visibility = View.VISIBLE settingsIconShown = true tooltipTimer = fixedRateTimer("tooltipTimer", true, 10000, 1000) { @@ -184,8 +188,8 @@ class MainActivity : AppCompatActivity(), private fun hideSettingsIcon(){ tooltipTimer.cancel() - settingstooltip.fadeRotateOut() - settingstooltip.visibility = View.INVISIBLE + activity_main_settings_icon.fadeRotateOut() + activity_main_settings_icon.visibility = View.INVISIBLE settingsIconShown = false } diff --git a/app/src/main/java/com/finnmglas/launcher/SettingsActivity.kt b/app/src/main/java/com/finnmglas/launcher/SettingsActivity.kt index 6b44bd8..8cdd691 100644 --- a/app/src/main/java/com/finnmglas/launcher/SettingsActivity.kt +++ b/app/src/main/java/com/finnmglas/launcher/SettingsActivity.kt @@ -15,10 +15,11 @@ import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.viewpager.widget.ViewPager -import com.finnmglas.launcher.ui.main.SectionsPagerAdapter +import com.finnmglas.launcher.extern.* +import com.finnmglas.launcher.settings.SectionsPagerAdapter import com.google.android.material.tabs.TabLayout import kotlinx.android.synthetic.main.activity_settings.* -import kotlinx.android.synthetic.main.activity_settings.container +import kotlinx.android.synthetic.main.fragment_settings_apps.* import kotlinx.android.synthetic.main.fragment_settings_theme.* @@ -43,224 +44,18 @@ class SettingsActivity : AppCompatActivity() { window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) val sectionsPagerAdapter = SectionsPagerAdapter(this, supportFragmentManager) - val viewPager: ViewPager = findViewById(R.id.view_pager) + val viewPager: ViewPager = findViewById(R.id.activity_settings_view_pager) viewPager.adapter = sectionsPagerAdapter - val tabs: TabLayout = findViewById(R.id.tabs) + val tabs: TabLayout = findViewById(R.id.activity_settings_tabs) tabs.setupWithViewPager(viewPager) - // Hide 'select' button for the selected theme - tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { - override fun onTabSelected(tab: TabLayout.Tab) { - if (tab == tabs.getTabAt(1 )) { - when (getSavedTheme(container!!.context)) { - "dark" -> select_theme_dark.visibility = View.INVISIBLE - "finn" -> select_theme_finn.visibility = View.INVISIBLE - "custom" -> { - select_theme_custom.text = getString(R.string.settings_select_image) - } - } - } - } - override fun onTabUnselected(tab: TabLayout.Tab) {} - override fun onTabReselected(tab: TabLayout.Tab) {} - }) - // As older APIs somehow do not recognize the xml defined onClick - close_settings.setOnClickListener() { finish() } - } - - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - - when (requestCode) { - REQUEST_CHOOSE_APP -> { - val value = data?.getStringExtra("value") - val forApp = data?.getStringExtra("forApp") ?: return - - // Save the new App to Preferences - val sharedPref = this.getSharedPreferences( - getString(R.string.preference_file_key), Context.MODE_PRIVATE) - - val editor :SharedPreferences.Editor = sharedPref.edit() - editor.putString("action_$forApp", value.toString()) - editor.apply() - - loadSettings(sharedPref) - } - - REQUEST_PERMISSION_STORAGE -> letUserPickImage() - REQUEST_PICK_IMAGE -> handlePickedImage(resultCode, data) - else -> super.onActivityResult(requestCode, resultCode, data) - } - } - - /** onClick functions for Settings */ - fun chooseDownApp(view: View) {chooseApp("downApp")} - fun chooseUpApp(view: View) {chooseApp("upApp")} - fun chooseLeftApp(view: View) {chooseApp("leftApp")} - fun chooseRightApp(view: View) {chooseApp("rightApp")} - fun chooseVolumeDownApp(view: View) {chooseApp("volumeDownApp")} - fun chooseVolumeUpApp(view: View) {chooseApp("volumeUpApp")} - - fun chooseApp(forAction :String) { - val intent = Intent(this, ChooseActivity::class.java) - intent.putExtra("action", "pick") - intent.putExtra("forApp", forAction) // for which action we choose the app - startActivityForResult(intent, REQUEST_CHOOSE_APP) - } - - fun chooseUninstallApp(view: View) { - val intent = Intent(this, ChooseActivity::class.java) - intent.putExtra("action", "uninstall") - startActivity(intent) - } - - fun chooseLaunchApp(view: View) { - val intent = Intent(this, ChooseActivity::class.java) - intent.putExtra("action", "launch") - startActivity(intent) - } - - fun chooseInstallApp(view : View) { - try { - val rateIntent = Intent( - Intent.ACTION_VIEW, - Uri.parse("https://play.google.com/store/apps/")) - startActivity(rateIntent) - } catch (e: ActivityNotFoundException) { - Toast.makeText(this,getString(R.string.settings_toast_store_not_found), Toast.LENGTH_SHORT) - .show() - } - } - - fun openFinnWebsite(view: View) { openNewTabWindow(getString(R.string.settings_footer_web), this) } - fun openGithubRepo(view: View) { openNewTabWindow(getString(R.string.settings_footer_repo), this) } - - // Rate App - // Just copied code from https://stackoverflow.com/q/10816757/12787264 - // that is how we write good software ^ - fun rateApp(view: View) { - try { - val rateIntent = rateIntentForUrl("market://details") - startActivity(rateIntent) - } catch (e: ActivityNotFoundException) { - val rateIntent = rateIntentForUrl("https://play.google.com/store/apps/details") - startActivity(rateIntent) - } - } - - private fun rateIntentForUrl(url: String): Intent { - val intent = Intent( - Intent.ACTION_VIEW, - Uri.parse(String.format("%s?id=%s", url, packageName)) - ) - var flags = Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_MULTIPLE_TASK - flags = if (Build.VERSION.SDK_INT >= 21) { - flags or Intent.FLAG_ACTIVITY_NEW_DOCUMENT - } else { - flags or Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET - } - intent.addFlags(flags) - return intent + activity_settings_close.setOnClickListener() { finish() } } fun backHome(view: View) { finish() } - fun setLauncher(view: View) { - // on newer sdk: choose launcher - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - val callHomeSettingIntent = Intent(Settings.ACTION_HOME_SETTINGS) - startActivity(callHomeSettingIntent) - } - // on older sdk: manage app details - else { - AlertDialog.Builder(this, R.style.AlertDialogCustom) - .setTitle(getString(R.string.alert_cant_choose_launcher)) - .setMessage(getString(R.string.alert_cant_choose_launcher_message)) - .setPositiveButton(android.R.string.yes, - DialogInterface.OnClickListener { dialog, which -> - try { - openAppSettings(packageName, this) - } catch ( e : ActivityNotFoundException) { - val intent = Intent(android.provider.Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS) - startActivity(intent) - } - }) - .setNegativeButton(android.R.string.no, null) - .setIcon(android.R.drawable.ic_dialog_info) - .show() - } - } - - fun viewTutorial (view: View){ - startActivity(Intent(this, FirstStartupActivity::class.java)) - } - - // Show a dialog prompting for confirmation - fun resetSettingsClick(view: View) { - AlertDialog.Builder(this, R.style.AlertDialogCustom) - .setTitle(getString(R.string.settings_reset)) - .setMessage(getString(R.string.settings_reset_message)) - .setPositiveButton(android.R.string.yes, - DialogInterface.OnClickListener { dialog, which -> - resetSettings(this.getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE), this) - finish() - }) - .setNegativeButton(android.R.string.no, null) - .setIcon(android.R.drawable.ic_dialog_alert) - .show() - } - /** Theme - related */ - fun chooseDarkTheme(view: View) { - saveTheme(this, "dark") - recreate() - } - - fun chooseFinnTheme(view: View) { - saveTheme(this, "finn") - recreate() - } - - fun chooseCustomTheme(view: View) { - - // Request permission (on newer APIs) - if (Build.VERSION.SDK_INT >= 23) { - when { - ContextCompat.checkSelfPermission(this, - Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED - -> letUserPickImage() - shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) - -> {} - else - -> requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_PERMISSION_STORAGE) - } - } - else letUserPickImage() - } - - private fun letUserPickImage() { - val intent = Intent() - intent.type = "image/*" - intent.action = Intent.ACTION_PICK // other option: Intent.ACTION_GET_CONTENT - //intent.putExtra("crop", "true") - //intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()) - startActivityForResult(intent, REQUEST_PICK_IMAGE) - } - - private fun handlePickedImage(resultCode: Int, data: Intent?) { - - if (resultCode == RESULT_OK) { - if (data == null) return - - //BitmapFactory.(data.data) - val imageUri = data.data - background = MediaStore.Images.Media.getBitmap(this.contentResolver, imageUri) - - saveTheme(this, "custom") - recreate() - } - } } diff --git a/app/src/main/java/com/finnmglas/launcher/SettingsFragmentApps.kt b/app/src/main/java/com/finnmglas/launcher/SettingsFragmentApps.kt deleted file mode 100644 index ad193fd..0000000 --- a/app/src/main/java/com/finnmglas/launcher/SettingsFragmentApps.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.finnmglas.launcher - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup - -/** The 'Apps' Tab associated Fragment in Settings */ - -class SettingsFragmentApps : Fragment() { - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - - return inflater.inflate(R.layout.fragment_settings_apps, container, false) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/SettingsFragmentLauncher.kt b/app/src/main/java/com/finnmglas/launcher/SettingsFragmentLauncher.kt deleted file mode 100644 index 89cb44f..0000000 --- a/app/src/main/java/com/finnmglas/launcher/SettingsFragmentLauncher.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.finnmglas.launcher - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup - -/** The 'Launcher' Tab associated Fragment in Settings */ - -class SettingsFragmentLauncher : Fragment() { - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - - return inflater.inflate(R.layout.fragment_settings_launcher, container, false) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/SettingsFragmentTheme.kt b/app/src/main/java/com/finnmglas/launcher/SettingsFragmentTheme.kt deleted file mode 100644 index 71d8d2f..0000000 --- a/app/src/main/java/com/finnmglas/launcher/SettingsFragmentTheme.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.finnmglas.launcher - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup - -/** The 'Theme' Tab associated Fragment in Settings */ - -class SettingsFragmentTheme : Fragment() { - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - - return inflater.inflate(R.layout.fragment_settings_theme, container, false) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/FontAwesome.kt b/app/src/main/java/com/finnmglas/launcher/extern/FontAwesome.kt similarity index 89% rename from app/src/main/java/com/finnmglas/launcher/FontAwesome.kt rename to app/src/main/java/com/finnmglas/launcher/extern/FontAwesome.kt index 7a4627a..4b8f2fb 100644 --- a/app/src/main/java/com/finnmglas/launcher/FontAwesome.kt +++ b/app/src/main/java/com/finnmglas/launcher/extern/FontAwesome.kt @@ -1,4 +1,4 @@ -package com.finnmglas.launcher // replace with your package +package com.finnmglas.launcher.extern // replace with your package // On GitHub: https://github.com/finnmglas/fontawesome-android @@ -6,6 +6,7 @@ import android.content.Context import android.graphics.Typeface import android.util.AttributeSet import androidx.appcompat.widget.AppCompatTextView +import com.finnmglas.launcher.R /** [FontAwesome] is just a type of TextView with special functions: * @@ -28,7 +29,9 @@ class FontAwesome : AppCompatTextView { private fun init(attrs: AttributeSet?) { if (attrs != null) { - val a = context!!.obtainStyledAttributes(attrs, R.styleable.FontAwesome) + val a = context!!.obtainStyledAttributes(attrs, + R.styleable.FontAwesome + ) if (a.hasValue(R.styleable.FontAwesome_type)) type = a.getString(R.styleable.FontAwesome_type)!! a.recycle() diff --git a/app/src/main/java/com/finnmglas/launcher/Functions.kt b/app/src/main/java/com/finnmglas/launcher/extern/Functions.kt similarity index 83% rename from app/src/main/java/com/finnmglas/launcher/Functions.kt rename to app/src/main/java/com/finnmglas/launcher/extern/Functions.kt index ee006c7..b345a7a 100644 --- a/app/src/main/java/com/finnmglas/launcher/Functions.kt +++ b/app/src/main/java/com/finnmglas/launcher/extern/Functions.kt @@ -1,4 +1,4 @@ -package com.finnmglas.launcher +package com.finnmglas.launcher.extern import android.app.Activity import android.app.AlertDialog @@ -9,12 +9,18 @@ import android.content.SharedPreferences import android.content.pm.PackageManager import android.content.pm.ResolveInfo import android.graphics.Bitmap +import android.graphics.BlendMode +import android.graphics.BlendModeColorFilter +import android.graphics.PorterDuff import android.net.Uri +import android.os.Build import android.os.Bundle import android.provider.Settings import android.view.View import android.view.animation.* +import android.widget.Button import android.widget.Toast +import com.finnmglas.launcher.R /** Variables for all of the app */ var upApp = "" @@ -134,7 +140,8 @@ private fun getIntent(packageName: String, context: Context): Intent? { } fun launchApp(packageName: String, context: Context) { - val intent = getIntent(packageName, context) + val intent = + getIntent(packageName, context) if (intent != null) { context.startActivity(intent) @@ -145,12 +152,17 @@ fun launchApp(packageName: String, context: Context) { } else { if (isInstalled(packageName, context)){ - AlertDialog.Builder(context, R.style.AlertDialogCustom) + AlertDialog.Builder(context, + R.style.AlertDialogCustom + ) .setTitle(context.getString(R.string.alert_cant_open_title)) .setMessage(context.getString(R.string.alert_cant_open_message)) .setPositiveButton(android.R.string.yes, DialogInterface.OnClickListener { dialog, which -> - openAppSettings(packageName, context) + openAppSettings( + packageName, + context + ) }) .setNegativeButton(android.R.string.no, null) .setIcon(android.R.drawable.ic_dialog_info) @@ -217,32 +229,53 @@ fun resetSettings(sharedPref : SharedPreferences, context: Context) : MutableLis val editor: SharedPreferences.Editor = sharedPref.edit() - val (chosenUpName, chosenUpPackage) = pickDefaultApp("action_upApp", context) + val (chosenUpName, chosenUpPackage) = pickDefaultApp( + "action_upApp", + context + ) editor.putString("action_upApp", chosenUpPackage) defaultList.add(chosenUpName) - val (chosenDownName, chosenDownPackage) = pickDefaultApp("action_downApp", context) + val (chosenDownName, chosenDownPackage) = pickDefaultApp( + "action_downApp", + context + ) editor.putString("action_downApp", chosenDownPackage) defaultList.add(chosenDownName) - val (chosenRightName, chosenRightPackage) = pickDefaultApp("action_rightApp", context) + val (chosenRightName, chosenRightPackage) = pickDefaultApp( + "action_rightApp", + context + ) editor.putString("action_rightApp", chosenRightPackage) defaultList.add(chosenRightName) - val (chosenLeftName, chosenLeftPackage) = pickDefaultApp("action_leftApp", context) + val (chosenLeftName, chosenLeftPackage) = pickDefaultApp( + "action_leftApp", + context + ) editor.putString("action_leftApp", chosenLeftPackage) editor.putString("action_calendarApp", chosenLeftPackage) defaultList.add(chosenLeftName) - val (chosenVolumeUpName, chosenVolumeUpPackage) = pickDefaultApp("action_volumeUpApp", context) + val (chosenVolumeUpName, chosenVolumeUpPackage) = pickDefaultApp( + "action_volumeUpApp", + context + ) editor.putString("action_volumeUpApp", chosenVolumeUpPackage) defaultList.add(chosenVolumeUpName) - val (chosenVolumeDownName, chosenVolumeDownPackage) = pickDefaultApp("action_volumeDownApp", context) + val (chosenVolumeDownName, chosenVolumeDownPackage) = pickDefaultApp( + "action_volumeDownApp", + context + ) editor.putString("action_volumeDownApp", chosenVolumeDownPackage) defaultList.add(chosenVolumeDownName) - val (_, chosenClockPackage) = pickDefaultApp("action_clockApp", context) + val (_, chosenClockPackage) = pickDefaultApp( + "action_clockApp", + context + ) editor.putString("action_clockApp", chosenClockPackage) editor.apply() @@ -264,7 +297,7 @@ fun pickDefaultApp(action: String, context: Context) : Pair{ // Related question: https://stackoverflow.com/q/3013655/12787264 (Adjusted) val list = context.resources.getStringArray(arrayResource) - for (entry in list!!){ + for (entry in list){ val splitResult = entry.split("|").toTypedArray() val pkgname = splitResult[0] val name = splitResult[1] @@ -273,3 +306,19 @@ fun pickDefaultApp(action: String, context: Context) : Pair{ } return Pair(context.getString(R.string.none_found), "") } + +/** Bitmaps */ + +fun getDominantColor(bitmap: Bitmap?): Int { + val newBitmap = Bitmap.createScaledBitmap(bitmap!!, 1, 1, true) + val color = newBitmap.getPixel(0, 0) + newBitmap.recycle() + return color +} + +fun setButtonColor(btn: Button, color: Int) { + if (Build.VERSION.SDK_INT >= 29) + btn.background.colorFilter = BlendModeColorFilter(color, BlendMode.DST_OVER) + else + btn.background.setColorFilter(color, PorterDuff.Mode.MULTIPLY) +} \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/ui/main/PageViewModel.kt b/app/src/main/java/com/finnmglas/launcher/settings/PageViewModel.kt similarity index 82% rename from app/src/main/java/com/finnmglas/launcher/ui/main/PageViewModel.kt rename to app/src/main/java/com/finnmglas/launcher/settings/PageViewModel.kt index 584ab43..7157d60 100644 --- a/app/src/main/java/com/finnmglas/launcher/ui/main/PageViewModel.kt +++ b/app/src/main/java/com/finnmglas/launcher/settings/PageViewModel.kt @@ -1,10 +1,9 @@ -package com.finnmglas.launcher.ui.main +package com.finnmglas.launcher.settings import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider class PageViewModel : ViewModel() { diff --git a/app/src/main/java/com/finnmglas/launcher/ui/main/SectionsPagerAdapter.kt b/app/src/main/java/com/finnmglas/launcher/settings/SectionsPagerAdapter.kt similarity index 80% rename from app/src/main/java/com/finnmglas/launcher/ui/main/SectionsPagerAdapter.kt rename to app/src/main/java/com/finnmglas/launcher/settings/SectionsPagerAdapter.kt index f412ddd..2b304ef 100644 --- a/app/src/main/java/com/finnmglas/launcher/ui/main/SectionsPagerAdapter.kt +++ b/app/src/main/java/com/finnmglas/launcher/settings/SectionsPagerAdapter.kt @@ -1,4 +1,4 @@ -package com.finnmglas.launcher.ui.main +package com.finnmglas.launcher.settings import android.content.Context import androidx.fragment.app.Fragment @@ -14,13 +14,13 @@ private val TAB_TITLES = arrayOf( /** Returns the fragment corresponding to the selected tab.*/ class SectionsPagerAdapter(private val context: Context, fm: FragmentManager) - : FragmentPagerAdapter(fm) { + : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { override fun getItem(position: Int): Fragment { return when (position){ 0 -> SettingsFragmentApps() 1 -> SettingsFragmentTheme() - 2 -> SettingsFragmentLauncher() + 2 -> SettingsFragmentMeta() else -> Fragment() } } @@ -29,7 +29,5 @@ class SectionsPagerAdapter(private val context: Context, fm: FragmentManager) return context.resources.getString(TAB_TITLES[position]) } - override fun getCount(): Int { - return 3 - } + override fun getCount(): Int { return 3 } } \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/settings/SettingsFragmentApps.kt b/app/src/main/java/com/finnmglas/launcher/settings/SettingsFragmentApps.kt new file mode 100644 index 0000000..b7cdfb0 --- /dev/null +++ b/app/src/main/java/com/finnmglas/launcher/settings/SettingsFragmentApps.kt @@ -0,0 +1,98 @@ +package com.finnmglas.launcher.settings + +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.net.Uri +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import com.finnmglas.launcher.ChooseActivity +import com.finnmglas.launcher.R +import com.finnmglas.launcher.extern.REQUEST_CHOOSE_APP +import com.finnmglas.launcher.extern.loadSettings +import kotlinx.android.synthetic.main.fragment_settings_apps.* + +/** The 'Apps' Tab associated Fragment in Settings */ + +class SettingsFragmentApps : Fragment() { + + /** Lifecycle functions */ + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + + return inflater.inflate(R.layout.fragment_settings_apps, container, false) + } + + override fun onStart() { + // Action - selecting buttons + fragment_settings_apps_choose_up_btn.setOnClickListener{ chooseApp("upApp") } + fragment_settings_apps_choose_down_btn.setOnClickListener{ chooseApp("downApp") } + fragment_settings_apps_choose_left_btn.setOnClickListener{ chooseApp("leftApp") } + fragment_settings_apps_choose_right_btn.setOnClickListener{ chooseApp("rightApp") } + fragment_settings_apps_choose_vol_up_btn.setOnClickListener{ chooseApp("volumeUpApp")} + fragment_settings_apps_choose_vol_down_btn.setOnClickListener{ chooseApp("volumeDownApp")} + + // App management buttons + fragment_settings_apps_launch_btn.setOnClickListener{ + val intent = Intent(this.context, ChooseActivity::class.java) + intent.putExtra("action", "launch") + startActivity(intent) + } + fragment_settings_apps_install_btn.setOnClickListener{ + try { + val rateIntent = Intent( + Intent.ACTION_VIEW, + Uri.parse("https://play.google.com/store/apps/")) + startActivity(rateIntent) + } catch (e: ActivityNotFoundException) { + Toast.makeText(this.context, getString(R.string.settings_toast_store_not_found), Toast.LENGTH_SHORT) + .show() + } + } + fragment_settings_apps_remove_btn.setOnClickListener{ + val intent = Intent(this.context, ChooseActivity::class.java) + intent.putExtra("action", "uninstall") + startActivity(intent) + } + + super.onStart() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + when (requestCode) { + REQUEST_CHOOSE_APP -> { + val value = data?.getStringExtra("value") + val forApp = data?.getStringExtra("forApp") ?: return + + // Save the new App to Preferences + val sharedPref = this.context!!.getSharedPreferences( + getString(R.string.preference_file_key), Context.MODE_PRIVATE) + + val editor : SharedPreferences.Editor = sharedPref.edit() + editor.putString("action_$forApp", value.toString()) + editor.apply() + + loadSettings(sharedPref) + } + else -> super.onActivityResult(requestCode, resultCode, data) + } + } + + /** Extra functions */ + + private fun chooseApp(forAction: String) { + val intent = Intent(this.context, ChooseActivity::class.java) + intent.putExtra("action", "pick") + intent.putExtra("forApp", forAction) // for which action we choose the app + startActivityForResult(intent, REQUEST_CHOOSE_APP) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/settings/SettingsFragmentMeta.kt b/app/src/main/java/com/finnmglas/launcher/settings/SettingsFragmentMeta.kt new file mode 100644 index 0000000..1d3f498 --- /dev/null +++ b/app/src/main/java/com/finnmglas/launcher/settings/SettingsFragmentMeta.kt @@ -0,0 +1,127 @@ +package com.finnmglas.launcher.settings + +import android.app.AlertDialog +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.provider.Settings +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.finnmglas.launcher.FirstStartupActivity +import com.finnmglas.launcher.R +import com.finnmglas.launcher.extern.openAppSettings +import com.finnmglas.launcher.extern.openNewTabWindow +import com.finnmglas.launcher.extern.resetSettings +import kotlinx.android.synthetic.main.fragment_settings_meta.* + +/** The 'Meta' Tab associated Fragment in Settings */ + +class SettingsFragmentMeta : Fragment() { + + /** Lifecycle functions */ + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_settings_meta, container, false) + } + + override fun onStart() { + // Button onClicks + + fragment_settings_meta_select_launcher_btn.setOnClickListener { + // on newer sdk: choose launcher + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + val callHomeSettingIntent = Intent(Settings.ACTION_HOME_SETTINGS) + startActivity(callHomeSettingIntent) + } + // on older sdk: manage app details + else { + AlertDialog.Builder(this.context!!, R.style.AlertDialogCustom) + .setTitle(getString(R.string.alert_cant_choose_launcher)) + .setMessage(getString(R.string.alert_cant_choose_launcher_message)) + .setPositiveButton(android.R.string.yes, + DialogInterface.OnClickListener { _, _ -> + try { + openAppSettings(this.context!!.packageName, this.context!!) + } catch ( e : ActivityNotFoundException) { + val intent = Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS) + startActivity(intent) + } + }) + .setNegativeButton(android.R.string.no, null) + .setIcon(android.R.drawable.ic_dialog_info) + .show() + } + } + + fragment_settings_meta_view_tutorial_btn.setOnClickListener { + startActivity(Intent(this.context, FirstStartupActivity::class.java)) + } + + // prompting for settings-reset confirmation + fragment_settings_meta_reset_settings_btn.setOnClickListener { + AlertDialog.Builder(this.context!!, R.style.AlertDialogCustom) + .setTitle(getString(R.string.settings_reset)) + .setMessage(getString(R.string.settings_reset_message)) + .setPositiveButton(android.R.string.yes, + DialogInterface.OnClickListener { _, _ -> + resetSettings(this.context!!.getSharedPreferences(getString(R.string.preference_file_key), + Context.MODE_PRIVATE), this.context!!) + activity!!.finish() + }) + .setNegativeButton(android.R.string.no, null) + .setIcon(android.R.drawable.ic_dialog_alert) + .show() + } + + // rate app + fragment_settings_meta_rate_app_btn.setOnClickListener { + try { + val rateIntent = rateIntentForUrl("market://details") + startActivity(rateIntent) + } catch (e: ActivityNotFoundException) { + val rateIntent = rateIntentForUrl("https://play.google.com/store/apps/details") + startActivity(rateIntent) + } + } + + // Footer onClicks + fragment_settings_meta_footer_github_link.setOnClickListener { + openNewTabWindow(getString(R.string.settings_footer_repo), this.context!!) + } + fragment_settings_meta_footer_website_link.setOnClickListener { + openNewTabWindow(getString(R.string.settings_footer_web), this.context!!) + } + + super.onStart() + } + + /** Extra functions */ + + // Rate App + // Just copied code from https://stackoverflow.com/q/10816757/12787264 + // that is how we write good software ^^ + + private fun rateIntentForUrl(url: String): Intent { + val intent = Intent( + Intent.ACTION_VIEW, + Uri.parse(String.format("%s?id=%s", url, this.context!!.packageName)) + ) + var flags = Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_MULTIPLE_TASK + flags = if (Build.VERSION.SDK_INT >= 21) { + flags or Intent.FLAG_ACTIVITY_NEW_DOCUMENT + } else { + flags or Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET + } + intent.addFlags(flags) + return intent + } +} \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/settings/SettingsFragmentTheme.kt b/app/src/main/java/com/finnmglas/launcher/settings/SettingsFragmentTheme.kt new file mode 100644 index 0000000..0609fd9 --- /dev/null +++ b/app/src/main/java/com/finnmglas/launcher/settings/SettingsFragmentTheme.kt @@ -0,0 +1,101 @@ +package com.finnmglas.launcher.settings + +import android.Manifest +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import android.os.Bundle +import android.provider.MediaStore +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import com.finnmglas.launcher.R +import com.finnmglas.launcher.extern.* +import kotlinx.android.synthetic.main.fragment_settings_theme.* + +/** The 'Theme' Tab associated Fragment in Settings */ + +class SettingsFragmentTheme : Fragment() { + + /** Lifecycle functions */ + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_settings_theme, container, false) + } + + override fun onStart(){ + // Hide 'select' button for the selected theme or allow customisation + when (getSavedTheme(this.context!!)) { + "dark" -> fragment_settings_theme_select_dark_btn.visibility = View.INVISIBLE + "finn" -> fragment_settings_theme_select_finn_btn.visibility = View.INVISIBLE + "custom" -> { + fragment_settings_theme_select_custom_btn.text = getString(R.string.settings_select_image) + } + } + + // Theme changing buttons + fragment_settings_theme_select_dark_btn.setOnClickListener { + saveTheme(this.context!!, "dark") + activity!!.recreate() + } + fragment_settings_theme_select_finn_btn.setOnClickListener { + saveTheme(this.context!!, "finn") + activity!!.recreate() + } + fragment_settings_theme_select_custom_btn.setOnClickListener { + // Request permission (on newer APIs) + if (Build.VERSION.SDK_INT >= 23) { + when { + ContextCompat.checkSelfPermission(this.context!!, + Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED + -> letUserPickImage() + shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) + -> {} + else + -> requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_PERMISSION_STORAGE) + } + } + else letUserPickImage() + } + + super.onStart() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + + when (requestCode) { + REQUEST_PERMISSION_STORAGE -> letUserPickImage() + REQUEST_PICK_IMAGE -> handlePickedImage(resultCode, data) + else -> super.onActivityResult(requestCode, resultCode, data) + } + } + + /** Extra functions */ + + private fun letUserPickImage(crop: Boolean = false) { + val intent = Intent() + intent.type = "image/*" + intent.action = Intent.ACTION_PICK // other option: Intent.ACTION_GET_CONTENT + if (crop) intent.putExtra("crop", "true") + startActivityForResult(intent, REQUEST_PICK_IMAGE) + } + + private fun handlePickedImage(resultCode: Int, data: Intent?) { + + if (resultCode == AppCompatActivity.RESULT_OK) { + if (data == null) return + + val imageUri = data.data + background = MediaStore.Images.Media.getBitmap(this.context!!.contentResolver, imageUri) + + saveTheme(this.context!!, "custom") + activity!!.recreate() + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_choose.xml b/app/src/main/res/layout/activity_choose.xml index bdf723e..e15028a 100644 --- a/app/src/main/res/layout/activity_choose.xml +++ b/app/src/main/res/layout/activity_choose.xml @@ -1,15 +1,16 @@ - - + app:layout_constraintTop_toBottomOf="@id/activity_choose_app_bar"> diff --git a/app/src/main/res/layout/activity_firststartup.xml b/app/src/main/res/layout/activity_firststartup.xml index e695b1c..74ae392 100644 --- a/app/src/main/res/layout/activity_firststartup.xml +++ b/app/src/main/res/layout/activity_firststartup.xml @@ -1,17 +1,17 @@ - - + app:layout_constraintTop_toBottomOf="@id/activity_firststartup_section_heading" /> + app:layout_constraintTop_toBottomOf="@id/activity_firststartup_descriptive_text" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index e21648a..f1ba21e 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,8 +1,9 @@ - + android:scaleType="centerCrop" + tools:ignore="ContentDescription" /> - - @@ -17,7 +19,7 @@ android:layout_height="match_parent"> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - @@ -58,7 +62,7 @@ diff --git a/app/src/main/res/layout/fragment_settings_apps.xml b/app/src/main/res/layout/fragment_settings_apps.xml index 1a0d25a..2211434 100644 --- a/app/src/main/res/layout/fragment_settings_apps.xml +++ b/app/src/main/res/layout/fragment_settings_apps.xml @@ -1,8 +1,9 @@ - + tools:context=".settings.SettingsFragmentApps">