diff --git a/app/build.gradle b/app/build.gradle index a5d3b5b6..176115ca 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,8 +17,8 @@ android { applicationId "app.olauncher" minSdkVersion 23 targetSdkVersion 34 - versionCode 78 - versionName "v4.2.2" + versionCode 80 + versionName "v4.3.2" resourceConfigurations += ["en", "ar", "de", "es-rES", "es-rUS", "fr", "hr", "hu", "in", "it", "ja", "pl", "pt-rBR", "ru-rRU", "sv", "tr", "uk", "zh"] testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -62,4 +62,4 @@ dependencies { //Material dependency implementation libs.material -} \ No newline at end of file +} diff --git a/app/src/main/java/app/olauncher/data/Constants.kt b/app/src/main/java/app/olauncher/data/Constants.kt index 30aae8f2..b02229a5 100644 --- a/app/src/main/java/app/olauncher/data/Constants.kt +++ b/app/src/main/java/app/olauncher/data/Constants.kt @@ -5,6 +5,9 @@ object Constants { object Key { const val FLAG = "flag" const val RENAME = "rename" + const val LAUNCH = "launch" + const val APP_NAME = "app_name" + const val LAUNCH_DELAY = "launch_delay" } object Dialog { diff --git a/app/src/main/java/app/olauncher/data/Prefs.kt b/app/src/main/java/app/olauncher/data/Prefs.kt index c281b8cf..1a82f1d8 100644 --- a/app/src/main/java/app/olauncher/data/Prefs.kt +++ b/app/src/main/java/app/olauncher/data/Prefs.kt @@ -7,6 +7,7 @@ import androidx.appcompat.app.AppCompatDelegate class Prefs(context: Context) { private val PREFS_FILENAME = "app.olauncher" + private val LAUNCH_DELAYS_PREFS_FILENAME = "app.olauncher.launchdelays" private val FIRST_OPEN = "FIRST_OPEN" private val FIRST_OPEN_TIME = "FIRST_OPEN_TIME" @@ -88,6 +89,8 @@ class Prefs(context: Context) { private val CALENDAR_APP_CLASS_NAME = "CALENDAR_APP_CLASS_NAME" private val prefs: SharedPreferences = context.getSharedPreferences(PREFS_FILENAME, 0); + private val launchDelayPrefs: SharedPreferences = + context.getSharedPreferences(LAUNCH_DELAYS_PREFS_FILENAME, 0) var firstOpen: Boolean get() = prefs.getBoolean(FIRST_OPEN, true) @@ -448,4 +451,8 @@ class Prefs(context: Context) { fun getAppRenameLabel(appPackage: String): String = prefs.getString(appPackage, "").toString() fun setAppRenameLabel(appPackage: String, renameLabel: String) = prefs.edit().putString(appPackage, renameLabel).apply() + + fun getAppLaunchDelay(appPackage: String): Int = launchDelayPrefs.getInt(appPackage, 0) + + fun setAppLaunchDelay(appPackage: String, time: Int) = launchDelayPrefs.edit().putInt(appPackage, time).apply() } \ No newline at end of file diff --git a/app/src/main/java/app/olauncher/ui/AppDrawerAdapter.kt b/app/src/main/java/app/olauncher/ui/AppDrawerAdapter.kt index 47d7352a..37ba35b9 100644 --- a/app/src/main/java/app/olauncher/ui/AppDrawerAdapter.kt +++ b/app/src/main/java/app/olauncher/ui/AppDrawerAdapter.kt @@ -33,6 +33,7 @@ class AppDrawerAdapter( private val appDeleteListener: (AppModel) -> Unit, private val appHideListener: (AppModel, Int) -> Unit, private val appRenameListener: (AppModel, String) -> Unit, + private val appChangeLaunchDelayListener: (AppModel, Int) -> Unit, ) : ListAdapter(DIFF_CALLBACK), Filterable { companion object { @@ -69,7 +70,8 @@ class AppDrawerAdapter( appDeleteListener, appInfoListener, appHideListener, - appRenameListener + appRenameListener, + appChangeLaunchDelayListener ) } catch (e: Exception) { e.printStackTrace() @@ -156,10 +158,12 @@ class AppDrawerAdapter( appInfoListener: (AppModel) -> Unit, appHideListener: (AppModel, Int) -> Unit, appRenameListener: (AppModel, String) -> Unit, + appChangeLaunchDelayListener: (AppModel, Int) -> Unit, ) = with(binding) { appHideLayout.visibility = View.GONE renameLayout.visibility = View.GONE + changeLaunchDelayLayout.visibility = View.GONE appTitle.text = appModel.appLabel appTitle.gravity = appLabelGravity otherProfileIndicator.isVisible = appModel.user != myUserHandle @@ -188,12 +192,20 @@ class AppDrawerAdapter( etAppRename.imeOptions = EditorInfo.IME_ACTION_DONE; } } - etAppRename.onFocusChangeListener = View.OnFocusChangeListener { v, hasFocus -> - if (hasFocus) - appTitle.visibility = View.INVISIBLE - else - appTitle.visibility = View.VISIBLE + appDelayLaunch.setOnClickListener { + if (appModel.appPackage.isNotEmpty()) { + etLaunchDelay.setText("") + changeLaunchDelayLayout.visibility = View.VISIBLE + renameLayout.visibility = View.GONE + appHideLayout.visibility = View.GONE + etLaunchDelay.showKeyboard() + } + } + val onFocusChangeListener = View.OnFocusChangeListener {_, hasFocus -> + appTitle.visibility = if (hasFocus) View.INVISIBLE else View.VISIBLE } + etAppRename.onFocusChangeListener = onFocusChangeListener + etLaunchDelay.onFocusChangeListener = onFocusChangeListener etAppRename.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) { etAppRename.hint = getAppName(etAppRename.context, appModel.appPackage) @@ -244,6 +256,23 @@ class AppDrawerAdapter( renameLayout.visibility = View.GONE } } + val stopLaunchDelayModification = { + val input = etLaunchDelay.text.toString() + if (input.isNotEmpty()) + appChangeLaunchDelayListener(appModel, input.toInt()) + changeLaunchDelayLayout.visibility = View.GONE + } + etLaunchDelay.setOnEditorActionListener { _, actionCode, _ -> + if (actionCode == EditorInfo.IME_ACTION_DONE) { + stopLaunchDelayModification() + true + } + false + } + tvSaveLaunchDelay.setOnClickListener { + etLaunchDelay.hideKeyboard() + stopLaunchDelayModification() + } appInfo.setOnClickListener { appInfoListener(appModel) } appDelete.setOnClickListener { appDeleteListener(appModel) } appHideLayout.setOnClickListener { appHideLayout.visibility = View.GONE } diff --git a/app/src/main/java/app/olauncher/ui/AppDrawerFragment.kt b/app/src/main/java/app/olauncher/ui/AppDrawerFragment.kt index 762b71e7..f60561a7 100644 --- a/app/src/main/java/app/olauncher/ui/AppDrawerFragment.kt +++ b/app/src/main/java/app/olauncher/ui/AppDrawerFragment.kt @@ -11,6 +11,7 @@ import android.widget.FrameLayout import android.widget.LinearLayout import android.widget.TextView import androidx.appcompat.widget.SearchView +import androidx.core.os.bundleOf import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels @@ -195,11 +196,26 @@ class AppDrawerFragment : Fragment() { appClickListener = { if (it.appPackage.isEmpty()) return@AppDrawerAdapter - viewModel.selectedApp(it, flag) - if (flag == Constants.FLAG_LAUNCH_APP || flag == Constants.FLAG_HIDDEN_APPS) - findNavController().popBackStack(R.id.mainFragment, false) + val navigationController = findNavController() + val launch = { + viewModel.selectedApp(it, flag) + if (flag == Constants.FLAG_LAUNCH_APP || flag == Constants.FLAG_HIDDEN_APPS) + navigationController.popBackStack(R.id.mainFragment, false) + else + navigationController.popBackStack() + } + val launchDelay = prefs.getAppLaunchDelay(it.appPackage) + if ((flag == Constants.FLAG_LAUNCH_APP || flag == Constants.FLAG_HIDDEN_APPS) && launchDelay > 0) + findNavController().navigate( + R.id.action_appListFragment_to_timerFragment, + bundleOf( + Constants.Key.LAUNCH to launch, + Constants.Key.LAUNCH_DELAY to launchDelay, + Constants.Key.APP_NAME to it.appLabel + ) + ) else - findNavController().popBackStack() + launch() }, appInfoListener = { openAppInfo( @@ -243,6 +259,9 @@ class AppDrawerFragment : Fragment() { appRenameListener = { appModel, renameLabel -> prefs.setAppRenameLabel(appModel.appPackage, renameLabel) viewModel.getAppList() + }, + appChangeLaunchDelayListener = { appModel, startDelay -> + prefs.setAppLaunchDelay(appModel.appPackage, startDelay) } ) diff --git a/app/src/main/java/app/olauncher/ui/HomeFragment.kt b/app/src/main/java/app/olauncher/ui/HomeFragment.kt index e64ecd4d..89b994c6 100644 --- a/app/src/main/java/app/olauncher/ui/HomeFragment.kt +++ b/app/src/main/java/app/olauncher/ui/HomeFragment.kt @@ -327,16 +327,30 @@ class HomeFragment : Fragment(), View.OnClickListener, View.OnLongClickListener } private fun launchApp(appName: String, packageName: String, activityClassName: String?, userString: String) { - viewModel.selectedApp( - AppModel( + val launch = { + viewModel.selectedApp( + AppModel( appName, null, packageName, activityClassName, getUserHandleFromString(requireContext(), userString) - ), - Constants.FLAG_LAUNCH_APP - ) + ), + Constants.FLAG_LAUNCH_APP + ) + } + val launchDelay = prefs.getAppLaunchDelay(packageName) + if (launchDelay > 0) + findNavController().navigate( + R.id.action_mainFragment_to_timerFragment, + bundleOf( + Constants.Key.LAUNCH to launch, + Constants.Key.LAUNCH_DELAY to launchDelay, + Constants.Key.APP_NAME to appName + ) + ) + else + launch() } private fun showAppList(flag: Int, rename: Boolean = false, includeHiddenApps: Boolean = false) { diff --git a/app/src/main/java/app/olauncher/ui/TimerFragment.kt b/app/src/main/java/app/olauncher/ui/TimerFragment.kt new file mode 100644 index 00000000..a89fa58d --- /dev/null +++ b/app/src/main/java/app/olauncher/ui/TimerFragment.kt @@ -0,0 +1,63 @@ +package app.olauncher.ui + +import android.os.Bundle +import android.os.CountDownTimer +import android.text.Editable +import android.text.TextWatcher +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import androidx.activity.OnBackPressedCallback +import androidx.core.view.isVisible +import androidx.navigation.fragment.findNavController +import app.olauncher.R +import app.olauncher.data.Constants +import app.olauncher.data.Prefs +import app.olauncher.databinding.FragmentTimerBinding +import app.olauncher.helper.hideKeyboard +import app.olauncher.helper.isSystemApp +import app.olauncher.helper.showKeyboard + +class TimerFragment : Fragment() { + private var _binding: FragmentTimerBinding? = null + private val binding get() = _binding!! + + private lateinit var timer: CountDownTimer + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + _binding = FragmentTimerBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val launchAction = arguments?.getSerializable(Constants.Key.LAUNCH) as () -> Unit + val launchDelay = arguments?.getInt(Constants.Key.LAUNCH_DELAY, 0)!! + val appName = arguments?.getString(Constants.Key.APP_NAME, "unknown")!! + + binding.appnameText.text = getString(R.string.launch_delay_question, appName) + + timer = object : CountDownTimer(launchDelay * 1000L, 1000L) { + override fun onTick(millisUntilFinish: Long) { + val secondsLeft = (millisUntilFinish + 999) / 1000 + binding.timeText.text = getString(R.string.launch_delay_launch_message, secondsLeft) + } + + override fun onFinish() { + launchAction() + } + } + timer.start() + + binding.cancel.setOnClickListener { + findNavController().navigate(R.id.action_timerFragment_to_mainFragment) + } + } + + override fun onStop() { + timer.cancel() + super.onStop() + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_delay_launch.xml b/app/src/main/res/drawable/ic_delay_launch.xml new file mode 100644 index 00000000..4bdb4a04 --- /dev/null +++ b/app/src/main/res/drawable/ic_delay_launch.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/layout/adapter_app_drawer.xml b/app/src/main/res/layout/adapter_app_drawer.xml index 9fe11754..a52a81ab 100644 --- a/app/src/main/res/layout/adapter_app_drawer.xml +++ b/app/src/main/res/layout/adapter_app_drawer.xml @@ -84,6 +84,21 @@ android:textSize="12sp" app:drawableTopCompat="@drawable/ic_hide" /> + + + android:visibility="gone"> + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_timer.xml b/app/src/main/res/layout/fragment_timer.xml new file mode 100644 index 00000000..cf9d4302 --- /dev/null +++ b/app/src/main/res/layout/fragment_timer.xml @@ -0,0 +1,49 @@ + + + + + + + + + + diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index d8bbc8c7..10934d35 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -24,6 +24,13 @@ app:exitAnim="@anim/fade_exit" app:popEnterAnim="@anim/fade_enter" app:popExitAnim="@anim/fade_exit" /> + + - - \ No newline at end of file + + + + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 12865b01..c1b1f641 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -79,6 +79,8 @@ Verstecken Zeige Statusleiste Umbenennen + Verzögern + Übernehemen Datum und Uhrzeit anzeigen Aussehen Gesten @@ -121,9 +123,15 @@ App nach rechts wischen deaktiviert Benutzen Sie Ihr Telefon, oder nutzt Ihr Telefon Sie? Über Olauncher + Digitales Wohlbefinden Erfahren Sie mehr Wussten Sie? Rezension Olauncher ist und bleibt völlig kostenlos. Es gibt keine Werbung. Es werden keine Daten erfasst. Am wichtigsten ist, dass es Ihnen dabei hilft, Ihre Bildschirmzeit zu verkürzen und ein gesünderes digitales Leben zu führen. \n\nWir glauben, dass der Wechsel zu einem Minimal Launcher eine ausgezeichnete Entscheidung ist. Obwohl wir offensichtlich voreingenommen sind, hoffen wir, dass Ihnen die Erfahrung gefällt. Teilen Sie uns gerne Ihre Gedanken mit! + Ausrichtung geändert + Sekunden + Bist du sicher, dass du\n%1$s\nbenutzen möchtest? + Die app wird in\n%1$d\nSekunden geöffnet. + Abbrechen diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 76c6d653..72e926d6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -60,6 +60,8 @@ Hide Status bar on top Rename + Delay + Change Show date time Appearance Gestures @@ -144,4 +146,8 @@ Olauncher is free and always will be. It has no ads. It collects no data. Most importantly, it helps you in reducing your screen time and live a healthier digital life. \n\nWe believe that switching to a minimal launcher is an excellent decision. While we\'re obviously biased, we hope you\'re enjoying the experience. Feel free to share your thoughts with us! Alignment changed ✅ - \ No newline at end of file + Seconds + Are you sure you want to use\n%1$s? + The app will open in\n%1$d\nseconds. + Cancel +