diff --git a/app/build.gradle b/app/build.gradle index 50a82e2..854a15a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -37,4 +37,5 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation 'com.google.android.material:material:1.1.0' implementation 'androidx.gridlayout:gridlayout:1.0.0' + implementation 'androidx.palette:palette:1.0.0' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 587516b..a6ab6d5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ package="com.finnmglas.launcher"> + + android:theme="@style/finnmglasTheme"> R.style.darkTheme + "finn" -> R.style.finnmglasTheme + else -> R.style.finnmglasTheme + } + ) setContentView(R.layout.activity_choose) + if (getSavedTheme(this) == "custom") { + activity_choose_container.setBackgroundColor(dominantColor) + activity_choose_app_bar.setBackgroundColor(dominantColor) + activity_choose_close.setTextColor(vibrantColor) + } + + // As older APIs somehow do not recognize the xml defined onClick + 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 */ @@ -59,7 +73,7 @@ class ChooseActivity : AppCompatActivity() { returnIntent.putExtra("value", app.packageName) returnIntent.putExtra("forApp", forApp) setResult( - 5000, + REQUEST_CHOOSE_APP, returnIntent ) finish() @@ -70,16 +84,18 @@ 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, UNINSTALL_REQUEST_CODE) + startActivityForResult(intent, + REQUEST_UNINSTALL + ) } } - apps_list.addView(tvdynamic) + activity_choose_apps_list.addView(tvdynamic) } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) - if (requestCode == UNINSTALL_REQUEST_CODE) { + if (requestCode == REQUEST_UNINSTALL) { if (resultCode == Activity.RESULT_OK) { Toast.makeText(this, getString(R.string.choose_removed_toast), Toast.LENGTH_LONG).show() updateAppList(packageManager) diff --git a/app/src/main/java/com/finnmglas/launcher/FirstStartupActivity.kt b/app/src/main/java/com/finnmglas/launcher/FirstStartupActivity.kt index 5e8d7b4..be43ead 100644 --- a/app/src/main/java/com/finnmglas/launcher/FirstStartupActivity.kt +++ b/app/src/main/java/com/finnmglas/launcher/FirstStartupActivity.kt @@ -5,9 +5,8 @@ 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.* import kotlinx.android.synthetic.main.activity_firststartup.* @@ -31,9 +30,22 @@ class FirstStartupActivity : AppCompatActivity(){ ) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + setTheme( + when (getSavedTheme(this)) { + "dark" -> R.style.darkTheme + "finn" -> R.style.finnmglasTheme + else -> R.style.finnmglasTheme + } + ) setContentView(R.layout.activity_firststartup) - hintText.blink() // animate + if (getSavedTheme(this) == "custom") { + activity_firststartup_app_bar.setBackgroundColor(dominantColor) + activity_firststartup_container.setBackgroundColor(dominantColor) + activity_firststartup_close.setTextColor(vibrantColor) + } + + activity_firststartup_hint_text.blink() // animate loadMenu(this) val sharedPref = this.getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE) @@ -42,6 +54,11 @@ class FirstStartupActivity : AppCompatActivity(){ if (isFirstTime) defaultApps = resetSettings(sharedPref, this) // UP, DOWN, RIGHT, LEFT, VOLUME_UP, VOLUME_DOWN + else + activity_firststartup_app_bar.visibility = View.VISIBLE + + // As older APIs somehow do not recognize the xml defined onClick + activity_firststartup_close.setOnClickListener() { finish() } } /** Touch- and Key-related functions to navigate */ @@ -67,6 +84,10 @@ class FirstStartupActivity : AppCompatActivity(){ loadMenu(this) } + fun backToSettings(view: View){ + finish() + } + /** Touch- and Key-related functions to navigate */ private fun loadMenu(context :Context) { // Context needed for packageManager @@ -76,14 +97,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){ @@ -94,7 +117,6 @@ class FirstStartupActivity : AppCompatActivity(){ editor.putLong("firstStartup", System.currentTimeMillis() / 1000L) // record first startup timestamp editor.apply() } - finish() } } diff --git a/app/src/main/java/com/finnmglas/launcher/MainActivity.kt b/app/src/main/java/com/finnmglas/launcher/MainActivity.kt index ff61c8e..466abcb 100644 --- a/app/src/main/java/com/finnmglas/launcher/MainActivity.kt +++ b/app/src/main/java/com/finnmglas/launcher/MainActivity.kt @@ -2,21 +2,27 @@ package com.finnmglas.launcher import android.content.Context import android.content.Intent +import android.net.Uri import android.os.AsyncTask import android.os.Bundle +import android.provider.MediaStore 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.* import kotlin.concurrent.fixedRateTimer import kotlin.math.abs + class MainActivity : AppCompatActivity(), GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener { + private var currentTheme = "" // keep track of theme changes + /** Variables for this activity */ private lateinit var mDetector: GestureDetectorCompat @@ -38,10 +44,6 @@ class MainActivity : AppCompatActivity(), val sharedPref = this.getSharedPreferences( getString(R.string.preference_file_key), Context.MODE_PRIVATE) - // First Startup - if (!sharedPref.getBoolean("startedBefore", false)) - startActivity(Intent(this, FirstStartupActivity::class.java)) - // Flags window.setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN, @@ -49,17 +51,37 @@ class MainActivity : AppCompatActivity(), ) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + currentTheme = getSavedTheme(this) + + if (currentTheme == "custom") { + try { + background = MediaStore.Images.Media.getBitmap(this.contentResolver, Uri.parse(sharedPref.getString("background_uri", ""))) + } catch (e: Exception) { } + + if (background == null) + currentTheme = saveTheme(this, "finn") + } + + setTheme( + when (currentTheme) { + "dark" -> R.style.darkTheme + "finn" -> R.style.finnmglasTheme + else -> R.style.finnmglasTheme + } + ) setContentView(R.layout.activity_main) // Start by showing the settings icon showSettingsIcon() // As older APIs somehow do not recognize the xml defined onClick - findViewById(R.id.settingstooltip).setOnClickListener() { - openSettings() - true - } + activity_main_settings_icon.setOnClickListener() { openSettings() } + // First Startup + if (!sharedPref.getBoolean("startedBefore", false)){ + startActivity(Intent(this, FirstStartupActivity::class.java)) + tooltipTimer.cancel() + } } override fun onStart(){ @@ -71,6 +93,10 @@ class MainActivity : AppCompatActivity(), loadSettings(sharedPref) + if (currentTheme == "custom") { + activity_main_settings_icon.setTextColor(vibrantColor) + } + mDetector = GestureDetectorCompat(this, this) mDetector.setOnDoubleTapListener(this) @@ -80,13 +106,23 @@ class MainActivity : AppCompatActivity(), override fun onResume() { super.onResume() + // TODO: do this immediately after changing preferences + if (currentTheme != getSavedTheme(this)) recreate() + 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()) - clockTimer = fixedRateTimer("clockTimer", true, 0L, 1000) { + clockTimer = fixedRateTimer("clockTimer", true, 0L, 100) { this@MainActivity.runOnUiThread { - dateView.text = dateFormat.format(Date()) - timeView.text = timeFormat.format(Date()) + val t = timeFormat.format(Date()) + if (activity_main_time_view.text != t) + activity_main_time_view.text = t + + val d = dateFormat.format(Date()) + if (activity_main_date_view.text != d) + activity_main_date_view.text = d } } @@ -103,15 +139,15 @@ class MainActivity : AppCompatActivity(), loadAppsTimer.cancel() } - private fun openSettings(){ startActivity(Intent(this, SettingsActivity::class.java)) + overridePendingTransition(R.anim.bottom_up, android.R.anim.fade_out) } /** Touch- and Key-related functions to start activities */ override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { - if (keyCode == KeyEvent.KEYCODE_BACK) return true + if (keyCode == KeyEvent.KEYCODE_BACK) { if (settingsIconShown) hideSettingsIcon() } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) launchApp(volumeUpApp, this) else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) launchApp(volumeDownApp, this) return true @@ -153,8 +189,8 @@ class MainActivity : AppCompatActivity(), } private fun showSettingsIcon(){ - settingstooltip.fadeIn() - settingstooltip.visibility = View.VISIBLE + activity_main_settings_icon.fadeRotateIn() + activity_main_settings_icon.visibility = View.VISIBLE settingsIconShown = true tooltipTimer = fixedRateTimer("tooltipTimer", true, 10000, 1000) { @@ -164,8 +200,8 @@ class MainActivity : AppCompatActivity(), private fun hideSettingsIcon(){ tooltipTimer.cancel() - settingstooltip.fadeOut() - 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 3eef8b4..36af54a 100644 --- a/app/src/main/java/com/finnmglas/launcher/SettingsActivity.kt +++ b/app/src/main/java/com/finnmglas/launcher/SettingsActivity.kt @@ -1,19 +1,26 @@ package com.finnmglas.launcher +import android.Manifest import android.app.AlertDialog import android.content.* +import android.content.pm.PackageManager import android.net.Uri import android.os.Build import android.os.Bundle +import android.provider.MediaStore import android.provider.Settings import android.view.View import android.view.WindowManager 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.fragment_settings_apps.* +import kotlinx.android.synthetic.main.fragment_settings_theme.* class SettingsActivity : AppCompatActivity() { @@ -22,153 +29,49 @@ class SettingsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + + setTheme( + when (getSavedTheme(this)) { + "dark" -> R.style.darkTheme + "finn" -> R.style.finnmglasTheme + else -> R.style.finnmglasTheme + } + ) + setContentView(R.layout.activity_settings) window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN) 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) - } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if(requestCode == 5000) - { - 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) - } - else { - super.onActivityResult(requestCode, resultCode, data) + // As older APIs somehow do not recognize the xml defined onClick + activity_settings_close.setOnClickListener() { finish() } + activity_settings_device_settings.setOnClickListener { + startActivityForResult(Intent(android.provider.Settings.ACTION_SETTINGS), 0) } } - /** 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")} + override fun onStart() { + super.onStart() - 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, 5000) - } + if (getSavedTheme(this) == "custom") { + activity_settings_container.setBackgroundColor(dominantColor) + activity_settings_app_bar.setBackgroundColor(dominantColor) - 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() + activity_settings_device_settings.setTextColor(vibrantColor) + activity_settings_close.setTextColor(vibrantColor) + activity_settings_tabs.setSelectedTabIndicatorColor(vibrantColor) } } - 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 - } - 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) - .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() - } - } + /** Theme - related */ - fun viewTutorial (view: View){ - startActivity(Intent(this, FirstStartupActivity::class.java)) - } - // Show a dialog prompting for confirmation - fun resetSettingsClick(view: View) { - AlertDialog.Builder(this) - .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() - } } 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 61% rename from app/src/main/java/com/finnmglas/launcher/Functions.kt rename to app/src/main/java/com/finnmglas/launcher/extern/Functions.kt index 6bbc484..2049178 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 @@ -8,14 +8,19 @@ import android.content.Intent 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.AlphaAnimation -import android.view.animation.Animation -import android.view.animation.DecelerateInterpolator +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 = "" @@ -30,6 +35,20 @@ var clockApp = "" var appsList : MutableList = mutableListOf() +var background : Bitmap? = null + +var dominantColor = 0 +var vibrantColor = 0 + +/** REQUEST CODES */ + +val REQUEST_PICK_IMAGE = 1 +val REQUEST_CHOOSE_APP = 2 +val REQUEST_UNINSTALL = 3 +val REQUEST_PERMISSION_STORAGE = 4 + +/** Animate */ + // Taken from https://stackoverflow.com/questions/47293269 fun View.blink( times: Int = Animation.INFINITE, @@ -47,20 +66,58 @@ fun View.blink( }) } -fun View.fadeIn(duration: Long = 1000L) { +fun View.fadeIn(duration: Long = 300L) { startAnimation(AlphaAnimation(0f, 1f).also { it.interpolator = DecelerateInterpolator() it.duration = duration }) } -fun View.fadeOut(duration: Long = 1000L) { +fun View.fadeOut(duration: Long = 300L) { startAnimation(AlphaAnimation(1f, 0f).also { it.interpolator = DecelerateInterpolator() it.duration = duration }) } +fun View.fadeRotateIn(duration: Long = 500L) { + val combined = AnimationSet(false) + combined.addAnimation( + AlphaAnimation(0f, 1F).also { + it.interpolator = DecelerateInterpolator() + it.duration = duration + } + ) + combined.addAnimation( + RotateAnimation(0F, 180F, Animation.RELATIVE_TO_SELF, + 0.5f, Animation.RELATIVE_TO_SELF,0.5f).also { + it.duration = duration * 2 + it.interpolator = DecelerateInterpolator() + } + ) + + startAnimation(combined) +} + +fun View.fadeRotateOut(duration: Long = 500L) { + val combined = AnimationSet(false) + combined.addAnimation( + AlphaAnimation(1F, 0F).also { + it.interpolator = AccelerateInterpolator() + it.duration = duration + } + ) + combined.addAnimation( + RotateAnimation(0F, 180F, Animation.RELATIVE_TO_SELF, + 0.5f, Animation.RELATIVE_TO_SELF,0.5f).also { + it.duration = duration + it.interpolator = AccelerateInterpolator() + } + ) + + startAnimation(combined) +} + /** Activity related */ fun isInstalled(uri: String, context: Context): Boolean { @@ -86,10 +143,11 @@ private fun getIntent(packageName: String, context: Context): Intent? { } fun launchApp(packageName: String, context: Context) { - val intent1 = getIntent(packageName, context) + val intent = + getIntent(packageName, context) - if (intent1 != null) { - context.startActivity(intent1) + if (intent != null) { + context.startActivity(intent) if (context is Activity) { context.overridePendingTransition(0, 0) @@ -97,12 +155,17 @@ fun launchApp(packageName: String, context: Context) { } else { if (isInstalled(packageName, context)){ - AlertDialog.Builder(context) + 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) @@ -124,6 +187,24 @@ fun openNewTabWindow(urls: String, context : Context) { /** Settings related functions */ +fun getSavedTheme(context : Context) : String { + val sharedPref = context.getSharedPreferences( + context.getString(R.string.preference_file_key), Context.MODE_PRIVATE) + + return sharedPref.getString("theme", "finn").toString() +} + +fun saveTheme(context : Context, themeName : String) : String { + val sharedPref = context.getSharedPreferences( + context.getString(R.string.preference_file_key), Context.MODE_PRIVATE) + + val editor: SharedPreferences.Editor = sharedPref.edit() + editor.putString("theme", themeName) + editor.apply() + + return themeName +} + fun openAppSettings(pkg :String, context:Context){ val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) intent.data = Uri.parse("package:$pkg") @@ -140,39 +221,67 @@ fun loadSettings(sharedPref : SharedPreferences){ calendarApp = sharedPref.getString("action_calendarApp", "").toString() clockApp = sharedPref.getString("action_clockApp", "").toString() + + dominantColor = sharedPref.getInt("custom_dominant", 0) + vibrantColor = sharedPref.getInt("custom_vibrant", 0) } fun resetSettings(sharedPref : SharedPreferences, context: Context) : MutableList{ + + // set default theme + saveTheme(context, "finn") + val defaultList :MutableList = mutableListOf() 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() @@ -194,7 +303,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] @@ -203,3 +312,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.MULTIPLY) + 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..62189ad --- /dev/null +++ b/app/src/main/java/com/finnmglas/launcher/settings/SettingsFragmentApps.kt @@ -0,0 +1,113 @@ +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.* +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() { + + if (getSavedTheme(context!!) == "custom") { + fragment_settings_apps_container.setBackgroundColor(dominantColor) + + setButtonColor(fragment_settings_apps_choose_up_btn, vibrantColor) + setButtonColor(fragment_settings_apps_choose_down_btn, vibrantColor) + setButtonColor(fragment_settings_apps_choose_left_btn, vibrantColor) + setButtonColor(fragment_settings_apps_choose_right_btn, vibrantColor) + setButtonColor(fragment_settings_apps_choose_vol_up_btn, vibrantColor) + setButtonColor(fragment_settings_apps_choose_vol_down_btn, vibrantColor) + + setButtonColor(fragment_settings_apps_launch_btn, vibrantColor) + setButtonColor(fragment_settings_apps_install_btn, vibrantColor) + setButtonColor(fragment_settings_apps_remove_btn, vibrantColor) + } + + // 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..56b1a64 --- /dev/null +++ b/app/src/main/java/com/finnmglas/launcher/settings/SettingsFragmentMeta.kt @@ -0,0 +1,141 @@ +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.* +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() { + + if (getSavedTheme(context!!) == "custom") { + fragment_settings_meta_container.setBackgroundColor(dominantColor) + + setButtonColor(fragment_settings_meta_select_launcher_btn, vibrantColor) + setButtonColor(fragment_settings_meta_view_tutorial_btn, vibrantColor) + setButtonColor(fragment_settings_meta_reset_settings_btn, vibrantColor) + + fragment_settings_meta_footer_play_icon.setTextColor(vibrantColor) + fragment_settings_meta_footer_github_icon.setTextColor(vibrantColor) + fragment_settings_meta_footer_globe_icon.setTextColor(vibrantColor) + } + + // 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() + } + + // Footer onClicks + fragment_settings_meta_footer_github_icon.setOnClickListener { + openNewTabWindow(getString(R.string.settings_footer_repo), this.context!!) + } + // rate app + fragment_settings_meta_footer_play_icon.setOnClickListener { + try { + val rateIntent = rateIntentForUrl("market://details") + startActivity(rateIntent) + } catch (e: ActivityNotFoundException) { + val rateIntent = rateIntentForUrl("https://play.google.com/store/apps/details") + startActivity(rateIntent) + } + } + + /*fragment_settings_meta_footer_website_icon.setOnClickListener { + openNewTabWindow(getString(R.string.settings_footer_web), this.context!!) + }*/ + fragment_settings_meta_footer_globe_icon.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..43441f5 --- /dev/null +++ b/app/src/main/java/com/finnmglas/launcher/settings/SettingsFragmentTheme.kt @@ -0,0 +1,123 @@ +package com.finnmglas.launcher.settings + +import android.Manifest +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +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 androidx.palette.graphics.Palette +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(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) + fragment_settings_theme_container.setBackgroundColor(dominantColor) + setButtonColor(fragment_settings_theme_select_finn_btn, vibrantColor) + setButtonColor(fragment_settings_theme_select_dark_btn, vibrantColor) + setButtonColor(fragment_settings_theme_select_custom_btn, vibrantColor) + } + } + + // Theme changing buttons + fragment_settings_theme_select_dark_btn.setOnClickListener { + saveTheme(context!!, "dark") + activity!!.recreate() + } + fragment_settings_theme_select_finn_btn.setOnClickListener { + saveTheme(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(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(context!!.contentResolver, imageUri) + + Palette.Builder(background!!).generate { + it?.let { palette -> + dominantColor = palette.getDominantColor(ContextCompat.getColor(context!!, R.color.darkTheme_accent_color)) + vibrantColor = palette.getVibrantColor(ContextCompat.getColor(context!!, R.color.darkTheme_accent_color)) + + /* Save image Uri as string */ + val editor: SharedPreferences.Editor = context!!.getSharedPreferences( + context!!.getString(R.string.preference_file_key), Context.MODE_PRIVATE).edit() + editor.putString("background_uri", imageUri.toString()) + editor.putInt("custom_dominant", dominantColor) + editor.putInt("custom_vibrant", vibrantColor) + editor.apply() + + saveTheme(context!!, "custom") + activity!!.recreate() + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/anim/bottom_up.xml b/app/src/main/res/anim/bottom_up.xml new file mode 100644 index 0000000..7832abf --- /dev/null +++ b/app/src/main/res/anim/bottom_up.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-de/custom_theme.jpg b/app/src/main/res/drawable-de/custom_theme.jpg new file mode 100644 index 0000000..947a003 Binary files /dev/null and b/app/src/main/res/drawable-de/custom_theme.jpg differ diff --git a/app/src/main/res/drawable-de/dark_theme.jpg b/app/src/main/res/drawable-de/dark_theme.jpg new file mode 100644 index 0000000..60018be Binary files /dev/null and b/app/src/main/res/drawable-de/dark_theme.jpg differ diff --git a/app/src/main/res/drawable-de/finnmglas_theme.jpg b/app/src/main/res/drawable-de/finnmglas_theme.jpg new file mode 100644 index 0000000..1b65f60 Binary files /dev/null and b/app/src/main/res/drawable-de/finnmglas_theme.jpg differ diff --git a/app/src/main/res/drawable-fr/custom_theme.jpg b/app/src/main/res/drawable-fr/custom_theme.jpg new file mode 100644 index 0000000..9fb4383 Binary files /dev/null and b/app/src/main/res/drawable-fr/custom_theme.jpg differ diff --git a/app/src/main/res/drawable-fr/dark_theme.jpg b/app/src/main/res/drawable-fr/dark_theme.jpg new file mode 100644 index 0000000..19e8861 Binary files /dev/null and b/app/src/main/res/drawable-fr/dark_theme.jpg differ diff --git a/app/src/main/res/drawable-fr/finnmglas_theme.jpg b/app/src/main/res/drawable-fr/finnmglas_theme.jpg new file mode 100644 index 0000000..2f0b6f7 Binary files /dev/null and b/app/src/main/res/drawable-fr/finnmglas_theme.jpg differ diff --git a/app/src/main/res/drawable/custom_theme.jpg b/app/src/main/res/drawable/custom_theme.jpg new file mode 100644 index 0000000..fd0e7ca Binary files /dev/null and b/app/src/main/res/drawable/custom_theme.jpg differ diff --git a/app/src/main/res/drawable/dark_theme.jpg b/app/src/main/res/drawable/dark_theme.jpg new file mode 100644 index 0000000..df2414a Binary files /dev/null and b/app/src/main/res/drawable/dark_theme.jpg differ diff --git a/app/src/main/res/drawable/finnmglas_theme.jpg b/app/src/main/res/drawable/finnmglas_theme.jpg new file mode 100644 index 0000000..97996e1 Binary files /dev/null and b/app/src/main/res/drawable/finnmglas_theme.jpg differ diff --git a/app/src/main/res/layout/activity_choose.xml b/app/src/main/res/layout/activity_choose.xml index b989abc..e15028a 100644 --- a/app/src/main/res/layout/activity_choose.xml +++ b/app/src/main/res/layout/activity_choose.xml @@ -1,20 +1,19 @@ - @@ -24,7 +23,7 @@ android:layout_height="match_parent"> - + 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 4581de9..74ae392 100644 --- a/app/src/main/res/layout/activity_firststartup.xml +++ b/app/src/main/res/layout/activity_firststartup.xml @@ -1,16 +1,66 @@ - + + + + + + + + + + + + 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 7d4be6e..f1ba21e 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,21 +1,30 @@ - + + - - + android:layout_height="wrap_content"> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - + + + app:tabTextColor="?attr/android:textColor" /> diff --git a/app/src/main/res/layout/fragment_settings_apps.xml b/app/src/main/res/layout/fragment_settings_apps.xml index 0c7e1d4..cf0ca53 100644 --- a/app/src/main/res/layout/fragment_settings_apps.xml +++ b/app/src/main/res/layout/fragment_settings_apps.xml @@ -2,17 +2,18 @@ + android:paddingLeft="32sp" + android:paddingTop="16sp" + android:paddingRight="32sp" + tools:context=".settings.SettingsFragmentApps">