diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d9008aa..4da5647 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,7 +14,7 @@ android:supportsRtl="true" android:theme="@style/finnmglasTheme"> - @@ -28,7 +28,7 @@ android:screenOrientation="portrait" tools:ignore="LockedOrientationActivity"> - diff --git a/app/src/main/java/com/finnmglas/launcher/extern/Functions.kt b/app/src/main/java/com/finnmglas/launcher/Functions.kt similarity index 80% rename from app/src/main/java/com/finnmglas/launcher/extern/Functions.kt rename to app/src/main/java/com/finnmglas/launcher/Functions.kt index f079f5b..454b5c4 100644 --- a/app/src/main/java/com/finnmglas/launcher/extern/Functions.kt +++ b/app/src/main/java/com/finnmglas/launcher/Functions.kt @@ -1,4 +1,4 @@ -package com.finnmglas.launcher.extern +package com.finnmglas.launcher import android.app.Activity import android.app.AlertDialog @@ -12,19 +12,28 @@ import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.Settings +import android.util.DisplayMetrics import android.view.View import android.view.animation.* import android.widget.Button import android.widget.ImageView import android.widget.Toast -import com.finnmglas.launcher.choose.ChooseActivity -import com.finnmglas.launcher.R +import com.finnmglas.launcher.list.ListActivity +import com.finnmglas.launcher.list.apps.AppsRecyclerAdapter import com.finnmglas.launcher.settings.SettingsActivity import com.finnmglas.launcher.settings.intendedSettingsPause +import com.finnmglas.launcher.tutorial.TutorialActivity import kotlin.math.roundToInt +/* Preferences (global, initialised when app is started) */ +lateinit var launcherPreferences: SharedPreferences + +/* Objects used by multiple activities */ +lateinit var appListViewAdapter: AppsRecyclerAdapter + +/* Variables containing settings */ +val displayMetrics = DisplayMetrics() -/** Variables for all of the app */ var upApp = "" var downApp = "" var rightApp = "" @@ -42,14 +51,14 @@ var background : Bitmap? = null var dominantColor = 0 var vibrantColor = 0 -/** REQUEST CODES */ +/* REQUEST CODES */ const val REQUEST_PICK_IMAGE = 1 const val REQUEST_CHOOSE_APP = 2 const val REQUEST_UNINSTALL = 3 const val REQUEST_PERMISSION_STORAGE = 4 -/** Animate */ +/* Animate */ // Taken from https://stackoverflow.com/questions/47293269 fun View.blink( @@ -120,7 +129,7 @@ fun View.fadeRotateOut(duration: Long = 500L) { startAnimation(combined) } -/** Activity related */ +/* Activity related */ fun isInstalled(uri: String, context: Context): Boolean { try { @@ -137,15 +146,18 @@ private fun getIntent(packageName: String, context: Context): Intent? { return intent } -// select what to launch -fun launch(data: String, activity: Activity) { +fun launch(data: String, activity: Activity, + animationIn: Int = android.R.anim.fade_in, animationOut: Int = android.R.anim.fade_out) { + if (data.startsWith("launcher:")) // [type]:[info] when(data.split(":")[1]) { "settings" -> openSettings(activity) "choose" -> openAppsList(activity) + "tutorial" -> openTutorial(activity) } else launchApp(data, activity) // app + activity.overridePendingTransition(animationIn, animationOut) } fun launchApp(packageName: String, context: Context) { @@ -186,22 +198,16 @@ fun openNewTabWindow(urls: String, context : Context) { context.startActivity(intents) } -/** Settings related functions */ +/* 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() + return launcherPreferences.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() +fun saveTheme(themeName : String) : String { + launcherPreferences.edit() + .putString("theme", themeName) + .apply() return themeName } @@ -216,39 +222,43 @@ fun openSettings(activity: Activity){ activity.startActivity(Intent(activity, SettingsActivity::class.java)) } +fun openTutorial(activity: Activity){ + activity.startActivity(Intent(activity, TutorialActivity::class.java)) +} + fun openAppsList(activity: Activity){ - val intent = Intent(activity, ChooseActivity::class.java) - intent.putExtra("action", "view") + val intent = Intent(activity, ListActivity::class.java) + intent.putExtra("intention", "view") intendedSettingsPause = true activity.startActivity(intent) } -fun loadSettings(sharedPref : SharedPreferences){ - upApp = sharedPref.getString("action_upApp", "").toString() - downApp = sharedPref.getString("action_downApp", "").toString() - rightApp = sharedPref.getString("action_rightApp", "").toString() - leftApp = sharedPref.getString("action_leftApp", "").toString() - volumeUpApp = sharedPref.getString("action_volumeUpApp", "").toString() - volumeDownApp = sharedPref.getString("action_volumeDownApp", "").toString() +fun loadSettings(){ + upApp = launcherPreferences.getString("action_upApp", "").toString() + downApp = launcherPreferences.getString("action_downApp", "").toString() + rightApp = launcherPreferences.getString("action_rightApp", "").toString() + leftApp = launcherPreferences.getString("action_leftApp", "").toString() + volumeUpApp = launcherPreferences.getString("action_volumeUpApp", "").toString() + volumeDownApp = launcherPreferences.getString("action_volumeDownApp", "").toString() - doubleClickApp = sharedPref.getString("action_doubleClickApp", "").toString() - longClickApp = sharedPref.getString("action_longClickApp", "").toString() + doubleClickApp = launcherPreferences.getString("action_doubleClickApp", "").toString() + longClickApp = launcherPreferences.getString("action_longClickApp", "").toString() - calendarApp = sharedPref.getString("action_calendarApp", "").toString() - clockApp = sharedPref.getString("action_clockApp", "").toString() + calendarApp = launcherPreferences.getString("action_calendarApp", "").toString() + clockApp = launcherPreferences.getString("action_clockApp", "").toString() - dominantColor = sharedPref.getInt("custom_dominant", 0) - vibrantColor = sharedPref.getInt("custom_vibrant", 0) + dominantColor = launcherPreferences.getInt("custom_dominant", 0) + vibrantColor = launcherPreferences.getInt("custom_vibrant", 0) } -fun resetSettings(sharedPref : SharedPreferences, context: Context) : MutableList{ +fun resetSettings(context: Context) : MutableList{ // set default theme - saveTheme(context, "finn") + saveTheme("finn") val defaultList :MutableList = mutableListOf() - val editor: SharedPreferences.Editor = sharedPref.edit() + val editor = launcherPreferences.edit() val (chosenUpName, chosenUpPackage) = pickDefaultApp( "action_upApp", @@ -331,14 +341,7 @@ 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 -} +/* Bitmaps */ fun setButtonColor(btn: Button, color: Int) { if (Build.VERSION.SDK_INT >= 29) diff --git a/app/src/main/java/com/finnmglas/launcher/MainActivity.kt b/app/src/main/java/com/finnmglas/launcher/HomeActivity.kt similarity index 51% rename from app/src/main/java/com/finnmglas/launcher/MainActivity.kt rename to app/src/main/java/com/finnmglas/launcher/HomeActivity.kt index 3768a38..c174b89 100644 --- a/app/src/main/java/com/finnmglas/launcher/MainActivity.kt +++ b/app/src/main/java/com/finnmglas/launcher/HomeActivity.kt @@ -6,136 +6,93 @@ 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 androidx.recyclerview.widget.RecyclerView -import com.finnmglas.launcher.choose.apps.AppsRecyclerAdapter -import com.finnmglas.launcher.extern.* +import com.finnmglas.launcher.list.apps.AppsRecyclerAdapter import com.finnmglas.launcher.tutorial.TutorialActivity -import kotlinx.android.synthetic.main.activity_main.* +import kotlinx.android.synthetic.main.home.* import java.text.SimpleDateFormat import java.util.* import kotlin.concurrent.fixedRateTimer import kotlin.math.abs -// used for the apps drawer / menu (ChooseActivity) -lateinit var viewAdapter: RecyclerView.Adapter<*> -lateinit var viewManager: RecyclerView.LayoutManager - -class MainActivity : AppCompatActivity(), +/** + * [HomeActivity] is the actual application Launcher, + * what makes this application special / unique. + * + * In this activity we display the date and time, + * and we listen for actions like tapping, swiping or button presses. + * + * As it also is the first thing that is started when someone opens Launcher, + * it also contains some logic related to the overall application: + * - Setting global variables (preferences etc.) + * - Opening the [TutorialActivity] on new installations + */ +class HomeActivity: UIObject, AppCompatActivity(), GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener { - private var currentTheme = "" // keep track of theme changes - - /** Variables for this activity */ private lateinit var mDetector: GestureDetectorCompat - // get device dimensions - private val displayMetrics = DisplayMetrics() - // timers private var clockTimer = Timer() private var tooltipTimer = Timer() private var settingsIconShown = false - /** Activity Lifecycle functions */ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - // Preferences - val sharedPref = this.getSharedPreferences( + // Initialise globals + launcherPreferences = this.getSharedPreferences( getString(R.string.preference_file_key), Context.MODE_PRIVATE) - // Flags - window.setFlags( - WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN - ) - window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + windowManager.defaultDisplay.getMetrics(displayMetrics) - currentTheme = getSavedTheme(this) + loadSettings() - 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") + // Preload list of apps (speed up loading time) + AsyncTask.execute { + appListViewAdapter = AppsRecyclerAdapter(this) } - setTheme( - when (currentTheme) { - "dark" -> R.style.darkTheme - "finn" -> R.style.finnmglasTheme - else -> R.style.customTheme - } - ) - setContentView(R.layout.activity_main) - - // Start by showing the settings icon - showSettingsIcon() - - // As older APIs somehow do not recognize the xml defined onClick - activity_main_settings_icon.setOnClickListener() { - openSettings(this) - overridePendingTransition(R.anim.bottom_up, android.R.anim.fade_out) - } - - // Load apps list first - speed up settings that way - AsyncTask.execute { viewAdapter = - AppsRecyclerAdapter(this, "", "") - } - - // First Startup - if (!sharedPref.getBoolean("startedBefore", false)){ + // First time opening the app: show Tutorial + if (!launcherPreferences.getBoolean("startedBefore", false)) startActivity(Intent(this, TutorialActivity::class.java)) - tooltipTimer.cancel() - } + + // Initialise layout + setContentView(R.layout.home) } override fun onStart(){ - super.onStart() - - // Preferences - val sharedPref = this.getSharedPreferences( - getString(R.string.preference_file_key), Context.MODE_PRIVATE) - - loadSettings(sharedPref) - - if (currentTheme == "custom") { - activity_main_settings_icon.setTextColor(vibrantColor) - } + super.onStart() mDetector = GestureDetectorCompat(this, this) mDetector.setOnDoubleTapListener(this) - windowManager.defaultDisplay.getMetrics(displayMetrics) + // for if the settings changed + loadSettings() + super.onStart() } 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) + if (home_background_image != null && getSavedTheme(this) == "custom") + home_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, 100) { - this@MainActivity.runOnUiThread { + this@HomeActivity.runOnUiThread { val t = timeFormat.format(Date()) - if (activity_main_time_view.text != t) - activity_main_time_view.text = t + if (home_time_view.text != t) + home_time_view.text = t val d = dateFormat.format(Date()) - if (activity_main_date_view.text != d) - activity_main_date_view.text = d + if (home_date_view.text != d) + home_date_view.text = d } } } @@ -143,20 +100,19 @@ class MainActivity : AppCompatActivity(), override fun onPause() { super.onPause() clockTimer.cancel() - } - /** Touch- and Key-related functions to start activities */ + hideSettingsIcon() + } override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { if (keyCode == KeyEvent.KEYCODE_BACK) { if (settingsIconShown) hideSettingsIcon() } - else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) launch(volumeUpApp, this) - else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) launch(volumeDownApp, this) + else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) + launch(volumeUpApp, this,0, 0) + else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) + launch(volumeDownApp, this,0, 0) return true } - fun dateViewOnTouch(v: View) { launch(calendarApp, this) } - fun timeViewOnTouch(v: View) { launch(clockApp, this) } - override fun onFling(e1: MotionEvent, e2: MotionEvent, dX: Float, dY: Float): Boolean { val width = displayMetrics.widthPixels @@ -168,24 +124,26 @@ class MainActivity : AppCompatActivity(), val strictness = 4 // how distinguished the swipe has to be to be accepted // Only open if the swipe was not from the phones top edge - if (diffY < -height / 8 && abs(diffY) > strictness * abs(diffX) && e1.y > 100) launch(downApp, this) - else if (diffY > height / 8 && abs(diffY) > strictness * abs(diffX)) { - launch(upApp, this) - overridePendingTransition(R.anim.bottom_up, android.R.anim.fade_out) - } - else if (diffX > width / 4 && abs(diffX) > strictness * abs(diffY)) launch(leftApp, this) - else if (diffX < -width / 4 && abs(diffX) > strictness * abs(diffY)) launch(rightApp, this) + if (diffY < -height / 8 && abs(diffY) > strictness * abs(diffX) && e1.y > 100) + launch(downApp,this, R.anim.top_down) + else if (diffY > height / 8 && abs(diffY) > strictness * abs(diffX)) + launch(upApp, this, R.anim.bottom_up) + else if (diffX > width / 4 && abs(diffX) > strictness * abs(diffY)) + launch(leftApp,this, R.anim.right_left) + else if (diffX < -width / 4 && abs(diffX) > strictness * abs(diffY)) + launch(rightApp, this, R.anim.left_right) return true } override fun onLongPress(event: MotionEvent) { - if(longClickApp != "") launch(longClickApp, this) - else openSettings(this) + launch(longClickApp, this) + overridePendingTransition(0, 0) } override fun onDoubleTap(event: MotionEvent): Boolean { launch(doubleClickApp, this) + overridePendingTransition(0, 0) return false } @@ -201,28 +159,75 @@ class MainActivity : AppCompatActivity(), } private fun showSettingsIcon(){ - activity_main_settings_icon.fadeRotateIn() - activity_main_settings_icon.visibility = View.VISIBLE + home_settings_icon.fadeRotateIn() + home_settings_icon.visibility = View.VISIBLE settingsIconShown = true tooltipTimer = fixedRateTimer("tooltipTimer", true, 10000, 1000) { - this@MainActivity.runOnUiThread { hideSettingsIcon() } + this@HomeActivity.runOnUiThread { hideSettingsIcon() } } } private fun hideSettingsIcon(){ tooltipTimer.cancel() - activity_main_settings_icon.fadeRotateOut() - activity_main_settings_icon.visibility = View.INVISIBLE + home_settings_icon.fadeRotateOut() + home_settings_icon.visibility = View.INVISIBLE settingsIconShown = false } - fun settingsIconOnTouch(view: View){ openSettings(this) } - override fun onTouchEvent(event: MotionEvent): Boolean { return if (mDetector.onTouchEvent(event)) { false } else { super.onTouchEvent(event) } } + override fun applyTheme() { + // Start by showing the settings icon + showSettingsIcon() + + home_settings_icon.setTextColor(vibrantColor) + home_container.setBackgroundColor(dominantColor) + + if (launcherPreferences.getString("background_uri", "") != "") { + try { + background = MediaStore.Images.Media.getBitmap( + this.contentResolver, Uri.parse( + launcherPreferences.getString("background_uri", "") + ) + ) + } catch (e: Exception) { } + + if (background == null) { // same as in Settings - TODO make function called by both + dominantColor = resources.getColor(R.color.finnmglasTheme_background_color) + vibrantColor = resources.getColor(R.color.finnmglasTheme_accent_color) + + launcherPreferences.edit() + .putString("background_uri", "") + .putInt("custom_dominant", dominantColor) + .putInt("custom_vibrant", vibrantColor) + .apply() + + saveTheme("finn") + recreate() + } + home_background_image.visibility = View.VISIBLE + } else { + home_background_image.visibility = View.INVISIBLE + } + } + + override fun setOnClicks() { + home_settings_icon.setOnClickListener() { + launch("launcher:settings", this, R.anim.bottom_up) + } + + home_date_view.setOnClickListener() { + launch(calendarApp, this) + } + + home_time_view.setOnClickListener() { + launch(clockApp,this) + } + } + /* TODO: Remove those. For now they are necessary * because this inherits from GestureDetector.OnGestureListener */ override fun onDoubleTapEvent(event: MotionEvent): Boolean { return false } diff --git a/app/src/main/java/com/finnmglas/launcher/UIObject.kt b/app/src/main/java/com/finnmglas/launcher/UIObject.kt new file mode 100644 index 0000000..7fd0f2b --- /dev/null +++ b/app/src/main/java/com/finnmglas/launcher/UIObject.kt @@ -0,0 +1,27 @@ +package com.finnmglas.launcher + +import android.app.Activity +import android.view.WindowManager + +/** + * An interface implemented by every [Activity], Fragment etc. in Launcher. + * It handles themes and window flags - a useful abstraction as it is the same everywhere. + */ +interface UIObject { + fun onStart() { + if (this is Activity){ + window.setFlags(0,0) // clear flags + window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN) + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + + applyTheme() + setOnClicks() + adjustLayout() + } + + // Don't use actual themes, rather create them on the fly for faster theme-switching + fun applyTheme() { } + fun setOnClicks() { } + fun adjustLayout() { } +} \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/choose/ChooseActivity.kt b/app/src/main/java/com/finnmglas/launcher/choose/ChooseActivity.kt deleted file mode 100644 index f582dc2..0000000 --- a/app/src/main/java/com/finnmglas/launcher/choose/ChooseActivity.kt +++ /dev/null @@ -1,103 +0,0 @@ -package com.finnmglas.launcher.choose - -import android.app.Activity -import android.content.Intent -import android.os.Bundle -import android.view.View -import android.view.WindowManager -import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity -import androidx.viewpager.widget.ViewPager -import com.finnmglas.launcher.R -import com.finnmglas.launcher.extern.* -import com.finnmglas.launcher.settings.intendedSettingsPause -import com.google.android.material.tabs.TabLayout -import kotlinx.android.synthetic.main.activity_choose.* - -var intendedChoosePause = false // know when to close - -// TODO: Better solution for this (used in choose-fragments) -var action = "view" -var forApp = "" - - -class ChooseActivity : AppCompatActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN) - 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_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() } - - // get info about which action this activity is open for - val bundle = intent.extras - if (bundle != null) { - action = bundle.getString("action")!! // why choose an app - if (action != "view") - forApp = bundle.getString("forApp")!! // which app we choose - } - - // Hide tabs for the "view" action - if (action == "view") { - activity_choose_tabs.visibility = View.GONE - } - - when (action) { - "view" -> activity_choose_heading.text = getString(R.string.choose_title_view) - "pick" -> activity_choose_heading.text = getString(R.string.choose_title) - } - - val sectionsPagerAdapter = ChooseSectionsPagerAdapter(this, supportFragmentManager) - val viewPager: ViewPager = findViewById(R.id.activity_choose_view_pager) - viewPager.adapter = sectionsPagerAdapter - val tabs: TabLayout = findViewById(R.id.activity_choose_tabs) - tabs.setupWithViewPager(viewPager) - } - - override fun onPause() { - super.onPause() - intendedSettingsPause = false - if(!intendedChoosePause) finish() - } - - override fun onResume() { - super.onResume() - intendedChoosePause = false - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (requestCode == REQUEST_UNINSTALL) { - if (resultCode == Activity.RESULT_OK) { - Toast.makeText(this, getString(R.string.choose_removed_toast), Toast.LENGTH_LONG).show() - finish() - } else if (resultCode == Activity.RESULT_FIRST_USER) { - Toast.makeText(this, getString(R.string.choose_not_removed_toast), Toast.LENGTH_LONG).show() - finish() - } - } - } - - /** onClick functions */ - - fun backHome(view: View) { finish() } - -} diff --git a/app/src/main/java/com/finnmglas/launcher/choose/ChooseSectionsPagerAdapter.kt b/app/src/main/java/com/finnmglas/launcher/choose/ChooseSectionsPagerAdapter.kt deleted file mode 100644 index 62563b6..0000000 --- a/app/src/main/java/com/finnmglas/launcher/choose/ChooseSectionsPagerAdapter.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.finnmglas.launcher.choose - -import android.content.Context -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentPagerAdapter -import com.finnmglas.launcher.* -import com.finnmglas.launcher.choose.apps.ChooseFragmentApps -import com.finnmglas.launcher.choose.other.ChooseFragmentOther - -private val TAB_TITLES = arrayOf( - R.string.choose_tab_app, - R.string.choose_tab_other -) - -/** Returns the fragment corresponding to the selected tab.*/ -class ChooseSectionsPagerAdapter(private val context: Context, fm: FragmentManager) - : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { - - override fun getItem(position: Int): Fragment { - return when (position){ - 0 -> ChooseFragmentApps() - 1 -> ChooseFragmentOther() - else -> Fragment() - } - } - - override fun getPageTitle(position: Int): CharSequence? { - return context.resources.getString(TAB_TITLES[position]) - } - - override fun getCount(): Int { - return when (action) { - "view" -> 1 - else -> 2 - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/choose/apps/ChooseFragmentApps.kt b/app/src/main/java/com/finnmglas/launcher/choose/apps/ChooseFragmentApps.kt deleted file mode 100644 index b7e6dee..0000000 --- a/app/src/main/java/com/finnmglas/launcher/choose/apps/ChooseFragmentApps.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.finnmglas.launcher.choose.apps - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import androidx.recyclerview.widget.LinearLayoutManager -import com.finnmglas.launcher.R -import com.finnmglas.launcher.choose.action -import com.finnmglas.launcher.extern.* -import com.finnmglas.launcher.choose.forApp -import kotlinx.android.synthetic.main.fragment_choose_apps.* - - -/** The 'Apps' Tab associated Fragment in the Chooser */ - -class ChooseFragmentApps : Fragment() { - - /** Lifecycle functions */ - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_choose_apps, container, false) - } - - override fun onStart() { - super.onStart() - - if (getSavedTheme(context!!) == "custom") { - fragment_choose_apps_container.setBackgroundColor(dominantColor) - } - - // set up the list / recycler - val viewManager = LinearLayoutManager(context) - val viewAdapter = AppsRecyclerAdapter( - activity!!, - action, - forApp - ) - - fragment_choose_apps_recycler_view.apply { - // improve performance (since content changes don't change the layout size) - setHasFixedSize(true) - layoutManager = viewManager - adapter = viewAdapter - } - - } -} \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/choose/other/OtherInfo.kt b/app/src/main/java/com/finnmglas/launcher/choose/other/OtherInfo.kt deleted file mode 100644 index e93c275..0000000 --- a/app/src/main/java/com/finnmglas/launcher/choose/other/OtherInfo.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.finnmglas.launcher.choose.other - -class OtherInfo(label: String, data: String, icon: String) { - var label: CharSequence? = label - var data: CharSequence? = data - var icon: CharSequence? = icon -} \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/extern/FontAwesome.kt b/app/src/main/java/com/finnmglas/launcher/libraries/FontAwesome.kt similarity index 96% rename from app/src/main/java/com/finnmglas/launcher/extern/FontAwesome.kt rename to app/src/main/java/com/finnmglas/launcher/libraries/FontAwesome.kt index 4b8f2fb..99cbf71 100644 --- a/app/src/main/java/com/finnmglas/launcher/extern/FontAwesome.kt +++ b/app/src/main/java/com/finnmglas/launcher/libraries/FontAwesome.kt @@ -1,4 +1,4 @@ -package com.finnmglas.launcher.extern // replace with your package +package com.finnmglas.launcher.libraries // replace with your package // On GitHub: https://github.com/finnmglas/fontawesome-android diff --git a/app/src/main/java/com/finnmglas/launcher/list/ListActivity.kt b/app/src/main/java/com/finnmglas/launcher/list/ListActivity.kt new file mode 100644 index 0000000..4d31f4f --- /dev/null +++ b/app/src/main/java/com/finnmglas/launcher/list/ListActivity.kt @@ -0,0 +1,141 @@ +package com.finnmglas.launcher.list + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.viewpager.widget.ViewPager +import com.finnmglas.launcher.* +import com.finnmglas.launcher.settings.intendedSettingsPause +import com.google.android.material.tabs.TabLayout +import kotlinx.android.synthetic.main.list.* +import android.content.Context +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentPagerAdapter +import com.finnmglas.launcher.list.apps.ListFragmentApps +import com.finnmglas.launcher.list.other.ListFragmentOther + +var intendedChoosePause = false // know when to close + +// TODO: Better solution for this (used in list-fragments) +var intention = "view" +var forApp = "" + +/** + * The [ListActivity] is the most general purpose activity in Launcher: + * - used to view all apps and edit their settings + * - used to choose an app / intent to be launched + * + * The activity itself can also be chosen to be launched as an action. + */ +class ListActivity : AppCompatActivity(), UIObject { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // Initialise layout + setContentView(R.layout.list) + } + + override fun onStart(){ + super.onStart() + super.onStart() + } + + override fun onPause() { + super.onPause() + intendedSettingsPause = false + if(!intendedChoosePause) finish() + } + + override fun onResume() { + super.onResume() + intendedChoosePause = false + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == REQUEST_UNINSTALL) { + if (resultCode == Activity.RESULT_OK) { + Toast.makeText(this, getString(R.string.choose_removed_toast), Toast.LENGTH_LONG).show() + finish() + } else if (resultCode == Activity.RESULT_FIRST_USER) { + Toast.makeText(this, getString(R.string.choose_not_removed_toast), Toast.LENGTH_LONG).show() + finish() + } + } + } + + override fun applyTheme() { + list_container.setBackgroundColor(dominantColor) + list_appbar.setBackgroundColor(dominantColor) + list_close.setTextColor(vibrantColor) + + list_tabs.setSelectedTabIndicatorColor(vibrantColor) + } + + override fun setOnClicks() { + list_close.setOnClickListener() { finish() } + } + + override fun adjustLayout() { + // get info about which action this activity is open for + val bundle = intent.extras + if (bundle != null) { + intention = bundle.getString("intention")!! // why choose an app + if (intention != "view") + forApp = bundle.getString("forApp")!! // which app we choose + } + + // Hide tabs for the "view" action + if (intention == "view") { + list_tabs.visibility = View.GONE + } + + when (intention) { + "view" -> list_heading.text = getString(R.string.choose_title_view) + "pick" -> list_heading.text = getString(R.string.choose_title) + } + + val sectionsPagerAdapter = ListSectionsPagerAdapter(this, supportFragmentManager) + val viewPager: ViewPager = findViewById(R.id.list_viewpager) + viewPager.adapter = sectionsPagerAdapter + val tabs: TabLayout = findViewById(R.id.list_tabs) + tabs.setupWithViewPager(viewPager) + } +} + +private val TAB_TITLES = arrayOf( + R.string.choose_tab_app, + R.string.choose_tab_other +) + +/** + * The [ListSectionsPagerAdapter] returns the fragment, + * which corresponds to the selected tab in [ListActivity]. + */ +class ListSectionsPagerAdapter(private val context: Context, fm: FragmentManager) + : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { + + override fun getItem(position: Int): Fragment { + return when (position){ + 0 -> ListFragmentApps() + 1 -> ListFragmentOther() + else -> Fragment() + } + } + + override fun getPageTitle(position: Int): CharSequence? { + return context.resources.getString(TAB_TITLES[position]) + } + + override fun getCount(): Int { + return when (intention) { + "view" -> 1 + else -> 2 + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/choose/apps/AppInfo.kt b/app/src/main/java/com/finnmglas/launcher/list/apps/AppInfo.kt similarity index 54% rename from app/src/main/java/com/finnmglas/launcher/choose/apps/AppInfo.kt rename to app/src/main/java/com/finnmglas/launcher/list/apps/AppInfo.kt index 9de1cdd..1122df7 100644 --- a/app/src/main/java/com/finnmglas/launcher/choose/apps/AppInfo.kt +++ b/app/src/main/java/com/finnmglas/launcher/list/apps/AppInfo.kt @@ -1,7 +1,12 @@ -package com.finnmglas.launcher.choose.apps +package com.finnmglas.launcher.list.apps import android.graphics.drawable.Drawable +/** + * Stores information used to create [AppsRecyclerAdapter] rows. + * + * Represents an app installed on the users device. + */ class AppInfo { var label: CharSequence? = null var packageName: CharSequence? = null diff --git a/app/src/main/java/com/finnmglas/launcher/choose/apps/AppsRecyclerAdapter.kt b/app/src/main/java/com/finnmglas/launcher/list/apps/AppsRecyclerAdapter.kt similarity index 76% rename from app/src/main/java/com/finnmglas/launcher/choose/apps/AppsRecyclerAdapter.kt rename to app/src/main/java/com/finnmglas/launcher/list/apps/AppsRecyclerAdapter.kt index a365086..e05e140 100644 --- a/app/src/main/java/com/finnmglas/launcher/choose/apps/AppsRecyclerAdapter.kt +++ b/app/src/main/java/com/finnmglas/launcher/list/apps/AppsRecyclerAdapter.kt @@ -1,4 +1,4 @@ -package com.finnmglas.launcher.choose.apps +package com.finnmglas.launcher.list.apps import android.app.Activity import android.content.Context @@ -12,27 +12,37 @@ import android.widget.ImageView import android.widget.PopupMenu import android.widget.TextView import androidx.recyclerview.widget.RecyclerView -import com.finnmglas.launcher.R -import com.finnmglas.launcher.extern.* -import com.finnmglas.launcher.choose.intendedChoosePause +import com.finnmglas.launcher.* +import com.finnmglas.launcher.libraries.* +import com.finnmglas.launcher.list.intendedChoosePause -class AppsRecyclerAdapter(val activity: Activity, val action: String?, val forApp: String?): +/** + * A [RecyclerView] (efficient scrollable list) containing all apps on the users device. + * The apps details are represented by [AppInfo]. + * + * @param activity - the activity this is in + * @param intention - why the list is displayed ("view", "pick") + * @param forApp - the action which an app is chosen for (when the intention is "pick") + */ +class AppsRecyclerAdapter(val activity: Activity, + val intention: String? = "view", + val forApp: String? = ""): RecyclerView.Adapter() { private val appsList: MutableList inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { - var textView: TextView = itemView.findViewById(R.id.choose_row_app_name) - var img: ImageView = itemView.findViewById(R.id.choose_row_app_icon) as ImageView - var menuDots: FontAwesome = itemView.findViewById(R.id.choose_row_app_menu) + var textView: TextView = itemView.findViewById(R.id.list_apps_row_name) + var img: ImageView = itemView.findViewById(R.id.list_apps_row_icon) as ImageView + var menuDots: FontAwesome = itemView.findViewById(R.id.list_apps_row_menu) override fun onClick(v: View) { val pos = adapterPosition val context: Context = v.context val appPackageName = appsList[pos].packageName.toString() - when (action){ + when (intention){ "view" -> { val launchIntent: Intent = context.packageManager .getLaunchIntentForPackage(appPackageName)!! @@ -60,10 +70,12 @@ class AppsRecyclerAdapter(val activity: Activity, val action: String?, val forAp viewHolder.textView.text = appLabel viewHolder.img.setImageDrawable(appIcon) - if (getSavedTheme(activity) == "dark") transformGrayscale(viewHolder.img) + if (getSavedTheme(activity) == "dark") transformGrayscale( + viewHolder.img + ) // decide when to show the options popup menu about - if (isSystemApp || action == "pick") { + if (isSystemApp || intention == "pick") { viewHolder.menuDots.visibility = View.INVISIBLE } else { @@ -93,13 +105,18 @@ class AppsRecyclerAdapter(val activity: Activity, val action: String?, val forAp val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE) intent.data = Uri.parse("package:$appPackageName") intent.putExtra(Intent.EXTRA_RETURN_RESULT, true) - activity.startActivityForResult(intent, REQUEST_UNINSTALL) + activity.startActivityForResult(intent, + REQUEST_UNINSTALL + ) true } R.id.app_menu_info -> { // open app settings intendedChoosePause = true - openAppSettings(appPackageName, activity) + openAppSettings( + appPackageName, + activity + ) true } else -> false @@ -114,7 +131,7 @@ class AppsRecyclerAdapter(val activity: Activity, val action: String?, val forAp override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val inflater = LayoutInflater.from(parent.context) - val view: View = inflater.inflate(R.layout.recycler_apps_row, parent, false) + val view: View = inflater.inflate(R.layout.list_apps_row, parent, false) return ViewHolder(view) } diff --git a/app/src/main/java/com/finnmglas/launcher/list/apps/ListFragmentApps.kt b/app/src/main/java/com/finnmglas/launcher/list/apps/ListFragmentApps.kt new file mode 100644 index 0000000..5504371 --- /dev/null +++ b/app/src/main/java/com/finnmglas/launcher/list/apps/ListFragmentApps.kt @@ -0,0 +1,52 @@ +package com.finnmglas.launcher.list.apps + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.LinearLayoutManager +import com.finnmglas.launcher.R +import com.finnmglas.launcher.UIObject +import com.finnmglas.launcher.list.intention +import com.finnmglas.launcher.list.forApp +import com.finnmglas.launcher.dominantColor +import com.finnmglas.launcher.getSavedTheme +import kotlinx.android.synthetic.main.list_apps.* + + +/** + * The [ListFragmentApps] is used as a tab in ListActivity. + * + * It is a list of all installed applications that are can be launched. + */ +class ListFragmentApps : Fragment(), UIObject { + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.list_apps, container, false) + } + + override fun onStart() { + super.onStart() + super.onStart() + } + + override fun applyTheme() { + list_apps_container.setBackgroundColor(dominantColor) + } + + override fun setOnClicks() { } + + override fun adjustLayout() { + // set up the list / recycler + list_apps_rview.apply { + // improve performance (since content changes don't change the layout size) + setHasFixedSize(true) + layoutManager = LinearLayoutManager(context) + adapter = AppsRecyclerAdapter(activity!!, intention, forApp) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/choose/other/ChooseFragmentOther.kt b/app/src/main/java/com/finnmglas/launcher/list/other/ListFragmentOther.kt similarity index 57% rename from app/src/main/java/com/finnmglas/launcher/choose/other/ChooseFragmentOther.kt rename to app/src/main/java/com/finnmglas/launcher/list/other/ListFragmentOther.kt index 6e1d55c..3641234 100644 --- a/app/src/main/java/com/finnmglas/launcher/choose/other/ChooseFragmentOther.kt +++ b/app/src/main/java/com/finnmglas/launcher/list/other/ListFragmentOther.kt @@ -1,4 +1,4 @@ -package com.finnmglas.launcher.choose.other +package com.finnmglas.launcher.list.other import android.os.Bundle import android.view.LayoutInflater @@ -7,33 +7,35 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager import com.finnmglas.launcher.R -import com.finnmglas.launcher.extern.dominantColor -import com.finnmglas.launcher.extern.getSavedTheme -import kotlinx.android.synthetic.main.fragment_choose_other.* +import com.finnmglas.launcher.dominantColor +import com.finnmglas.launcher.getSavedTheme +import kotlinx.android.synthetic.main.list_other.* -/** The 'Other' Tab associated Fragment in the Chooser */ - -class ChooseFragmentOther : Fragment() { - - /** Lifecycle functions */ +/** + * The [ListFragmentOther] is used as a tab in ListActivity, + * when the `intention` for launching the ListActivity was to select something to be launched. + * + * It lists `other` things like internal activities to be launched as an action. + */ +class ListFragmentOther : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_choose_other, container, false) + return inflater.inflate(R.layout.list_other, container, false) } override fun onStart() { if (getSavedTheme(context!!) == "custom") { - fragment_choose_other_container.setBackgroundColor(dominantColor) + list_other_container.setBackgroundColor(dominantColor) } // set up the list / recycler val viewManager = LinearLayoutManager(context) val viewAdapter = OtherRecyclerAdapter(activity!!) - fragment_choose_other_recycler_view.apply { + list_other_rview.apply { // improve performance (since content changes don't change the layout size) setHasFixedSize(true) layoutManager = viewManager diff --git a/app/src/main/java/com/finnmglas/launcher/list/other/OtherInfo.kt b/app/src/main/java/com/finnmglas/launcher/list/other/OtherInfo.kt new file mode 100644 index 0000000..036a401 --- /dev/null +++ b/app/src/main/java/com/finnmglas/launcher/list/other/OtherInfo.kt @@ -0,0 +1,15 @@ +package com.finnmglas.launcher.list.other + +/** + * Stores information used in [OtherRecyclerAdapter] rows. + * + * Represents an `other` action - something that can be selected to be launched + * when an action is recognized. + * + * @param data - a string identifying the thing to be launched +*/ +class OtherInfo(label: String, data: String, icon: String) { + var label: CharSequence? = label + var data: CharSequence? = data + var icon: CharSequence? = icon +} \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/choose/other/OtherRecyclerAdapter.kt b/app/src/main/java/com/finnmglas/launcher/list/other/OtherRecyclerAdapter.kt similarity index 73% rename from app/src/main/java/com/finnmglas/launcher/choose/other/OtherRecyclerAdapter.kt rename to app/src/main/java/com/finnmglas/launcher/list/other/OtherRecyclerAdapter.kt index 36ab5dc..e8c9bc6 100644 --- a/app/src/main/java/com/finnmglas/launcher/choose/other/OtherRecyclerAdapter.kt +++ b/app/src/main/java/com/finnmglas/launcher/list/other/OtherRecyclerAdapter.kt @@ -1,4 +1,4 @@ -package com.finnmglas.launcher.choose.other +package com.finnmglas.launcher.list.other import android.app.Activity import android.content.Intent @@ -8,10 +8,17 @@ import android.view.ViewGroup import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import com.finnmglas.launcher.R -import com.finnmglas.launcher.extern.* -import com.finnmglas.launcher.choose.forApp +import com.finnmglas.launcher.REQUEST_CHOOSE_APP +import com.finnmglas.launcher.libraries.* +import com.finnmglas.launcher.list.forApp -/* Will only be used if an app / action is picked */ +/** + * The [OtherRecyclerAdapter] will only be displayed in the ListActivity, + * if an app / intent / etc. is picked to be launched when an action is recognized. + * + * It lists `other` things to be launched that are not really represented by a URI, + * rather by Launcher- internal conventions. + */ class OtherRecyclerAdapter(val activity: Activity): RecyclerView.Adapter() { @@ -19,8 +26,8 @@ class OtherRecyclerAdapter(val activity: Activity): inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { - var textView: TextView = itemView.findViewById(R.id.row_other_name) - var iconView: FontAwesome = itemView.findViewById(R.id.row_other_fa_icon) + var textView: TextView = itemView.findViewById(R.id.list_other_row_name) + var iconView: FontAwesome = itemView.findViewById(R.id.list_other_row_icon) override fun onClick(v: View) { @@ -45,7 +52,7 @@ class OtherRecyclerAdapter(val activity: Activity): override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val inflater = LayoutInflater.from(parent.context) - val view: View = inflater.inflate(R.layout.recycler_other_row, parent, false) + val view: View = inflater.inflate(R.layout.list_other_row, parent, false) return ViewHolder(view) } diff --git a/app/src/main/java/com/finnmglas/launcher/settings/SettingsActivity.kt b/app/src/main/java/com/finnmglas/launcher/settings/SettingsActivity.kt index 192bb39..de88bc9 100644 --- a/app/src/main/java/com/finnmglas/launcher/settings/SettingsActivity.kt +++ b/app/src/main/java/com/finnmglas/launcher/settings/SettingsActivity.kt @@ -2,66 +2,51 @@ package com.finnmglas.launcher.settings import android.content.Context import android.content.Intent -import android.content.SharedPreferences import android.os.Bundle import android.provider.Settings -import android.view.View -import android.view.WindowManager import androidx.appcompat.app.AppCompatActivity import androidx.viewpager.widget.ViewPager -import com.finnmglas.launcher.R -import com.finnmglas.launcher.extern.* +import com.finnmglas.launcher.* import com.google.android.material.tabs.TabLayout -import kotlinx.android.synthetic.main.activity_settings.* +import kotlinx.android.synthetic.main.settings.* +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentPagerAdapter +import com.finnmglas.launcher.settings.actions.SettingsFragmentActions +import com.finnmglas.launcher.settings.theme.SettingsFragmentTheme +import com.finnmglas.launcher.settings.meta.SettingsFragmentMeta + var intendedSettingsPause = false // know when to close -class SettingsActivity : AppCompatActivity() { - - /** Activity Lifecycle functions */ +/** + * The [SettingsActivity] is a tabbed activity: + * + * | Actions | Choose apps or intents to be launched | [SettingsFragmentActions] | + * | Theme | Select a theme / Customize | [SettingsFragmentTheme] | + * | Meta | About Launcher / Contact etc. | [SettingsFragmentMeta] | + * + * Settings are closed automatically if the activity goes `onPause` unexpectedly. + */ +class SettingsActivity: AppCompatActivity(), UIObject { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setTheme( - when (getSavedTheme(this)) { - "dark" -> R.style.darkTheme - "finn" -> R.style.finnmglasTheme - else -> R.style.customTheme - } - ) - - setContentView(R.layout.activity_settings) - - window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN) - window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + // Initialise layout + setContentView(R.layout.settings) + // set up tabs and swiping in settings val sectionsPagerAdapter = SettingsSectionsPagerAdapter(this, supportFragmentManager) - val viewPager: ViewPager = findViewById(R.id.activity_settings_view_pager) + val viewPager: ViewPager = findViewById(R.id.settings_viewpager) viewPager.adapter = sectionsPagerAdapter - val tabs: TabLayout = findViewById(R.id.activity_settings_tabs) + val tabs: TabLayout = findViewById(R.id.settings_tabs) tabs.setupWithViewPager(viewPager) - - // As older APIs somehow do not recognize the xml defined onClick - activity_settings_close.setOnClickListener() { finish() } - // open device settings (see https://stackoverflow.com/a/62092663/12787264) - activity_settings_device_settings.setOnClickListener { - intendedSettingsPause = true - startActivity(Intent(Settings.ACTION_SETTINGS)) - } } override fun onStart() { - super.onStart() - - if (getSavedTheme(this) == "custom") { - activity_settings_container.setBackgroundColor(dominantColor) - activity_settings_app_bar.setBackgroundColor(dominantColor) - - activity_settings_device_settings.setTextColor(vibrantColor) - activity_settings_close.setTextColor(vibrantColor) - activity_settings_tabs.setSelectedTabIndicatorColor(vibrantColor) - } + super.onStart() + super.onStart() } override fun onResume() { @@ -74,27 +59,63 @@ class SettingsActivity : AppCompatActivity() { if (!intendedSettingsPause) finish() } - fun backHome(view: View) { 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) + launcherPreferences.edit() + .putString("action_$forApp", value.toString()) + .apply() - val editor : SharedPreferences.Editor = sharedPref.edit() - editor.putString("action_$forApp", value.toString()) - editor.apply() - - loadSettings(sharedPref) + loadSettings() } else -> super.onActivityResult(requestCode, resultCode, data) } } + override fun applyTheme() { + settings_container.setBackgroundColor(dominantColor) + settings_appbar.setBackgroundColor(dominantColor) + settings_system.setTextColor(vibrantColor) + settings_close.setTextColor(vibrantColor) + settings_tabs.setSelectedTabIndicatorColor(vibrantColor) + } + + override fun setOnClicks(){ + // As older APIs somehow do not recognize the xml defined onClick + settings_close.setOnClickListener() { finish() } + // open device settings (see https://stackoverflow.com/a/62092663/12787264) + settings_system.setOnClickListener { + intendedSettingsPause = true + startActivity(Intent(Settings.ACTION_SETTINGS)) + } + } } + +private val TAB_TITLES = arrayOf( + R.string.settings_tab_app, + R.string.settings_tab_theme, + R.string.settings_tab_launcher +) + +class SettingsSectionsPagerAdapter(private val context: Context, fm: FragmentManager) + : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { + + override fun getItem(position: Int): Fragment { + return when (position){ + 0 -> SettingsFragmentActions() + 1 -> SettingsFragmentTheme() + 2 -> SettingsFragmentMeta() + else -> Fragment() + } + } + + override fun getPageTitle(position: Int): CharSequence? { + return context.resources.getString(TAB_TITLES[position]) + } + + override fun getCount(): Int { return 3 } +} \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/settings/SettingsSectionsPagerAdapter.kt b/app/src/main/java/com/finnmglas/launcher/settings/SettingsSectionsPagerAdapter.kt deleted file mode 100644 index a539f2b..0000000 --- a/app/src/main/java/com/finnmglas/launcher/settings/SettingsSectionsPagerAdapter.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.finnmglas.launcher.settings - -import android.content.Context -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentPagerAdapter -import com.finnmglas.launcher.* -import com.finnmglas.launcher.settings.actions.SettingsFragmentApps -import com.finnmglas.launcher.settings.meta.SettingsFragmentMeta -import com.finnmglas.launcher.settings.theme.SettingsFragmentTheme - -private val TAB_TITLES = arrayOf( - R.string.settings_tab_app, - R.string.settings_tab_theme, - R.string.settings_tab_launcher -) - -/** Returns the fragment corresponding to the selected tab.*/ -class SettingsSectionsPagerAdapter(private val context: Context, fm: FragmentManager) - : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { - - override fun getItem(position: Int): Fragment { - return when (position){ - 0 -> SettingsFragmentApps() - 1 -> SettingsFragmentTheme() - 2 -> SettingsFragmentMeta() - else -> Fragment() - } - } - - override fun getPageTitle(position: Int): CharSequence? { - return context.resources.getString(TAB_TITLES[position]) - } - - override fun getCount(): Int { return 3 } -} \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/settings/actions/ActionInfo.kt b/app/src/main/java/com/finnmglas/launcher/settings/actions/ActionInfo.kt index 7b1efcb..78d1902 100644 --- a/app/src/main/java/com/finnmglas/launcher/settings/actions/ActionInfo.kt +++ b/app/src/main/java/com/finnmglas/launcher/settings/actions/ActionInfo.kt @@ -1,7 +1,14 @@ package com.finnmglas.launcher.settings.actions -class ActionInfo(actionText: CharSequence, actionName: CharSequence, content: CharSequence) { - var actionName: CharSequence? = actionName - var actionText: CharSequence? = actionText - var content: CharSequence? = content +/** + * Stores information used in [ActionsRecyclerAdapter] rows. + * + * Represents an action - something to be triggered by swiping, clicking etc. + * + * @param data - a string identifying the app / action / intent to be launched + */ +class ActionInfo(actionText: CharSequence, actionName: CharSequence, data: CharSequence) { + val actionName: CharSequence? = actionName + val actionText: CharSequence? = actionText + val data: CharSequence? = data } \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/settings/actions/ActionsRecyclerAdapter.kt b/app/src/main/java/com/finnmglas/launcher/settings/actions/ActionsRecyclerAdapter.kt index 68649fe..136075c 100644 --- a/app/src/main/java/com/finnmglas/launcher/settings/actions/ActionsRecyclerAdapter.kt +++ b/app/src/main/java/com/finnmglas/launcher/settings/actions/ActionsRecyclerAdapter.kt @@ -1,9 +1,7 @@ package com.finnmglas.launcher.settings.actions import android.app.Activity -import android.content.Context import android.content.Intent -import android.content.SharedPreferences import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -11,10 +9,9 @@ import android.widget.Button import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.RecyclerView -import com.finnmglas.launcher.choose.ChooseActivity -import com.finnmglas.launcher.R -import com.finnmglas.launcher.extern.FontAwesome -import com.finnmglas.launcher.extern.* +import com.finnmglas.launcher.* +import com.finnmglas.launcher.list.ListActivity +import com.finnmglas.launcher.libraries.FontAwesome import com.finnmglas.launcher.settings.intendedSettingsPause import java.lang.Exception @@ -26,19 +23,13 @@ class ActionsRecyclerAdapter(val activity: Activity): inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { - var textView: TextView = itemView.findViewById(R.id.row_action_name) - var fontAwesome: FontAwesome = itemView.findViewById(R.id.row_app_fa_icon) - var img: ImageView = itemView.findViewById(R.id.row_app_icon) as ImageView - var chooseButton: Button = itemView.findViewById(R.id.row_choose_button) - var removeAction: FontAwesome = itemView.findViewById(R.id.row_remove_action) + var textView: TextView = itemView.findViewById(R.id.settings_actions_row_name) + var fontAwesome: FontAwesome = itemView.findViewById(R.id.settings_actions_row_icon) + var img: ImageView = itemView.findViewById(R.id.settings_actions_row_icon_img) as ImageView + var chooseButton: Button = itemView.findViewById(R.id.settings_actions_row_button_choose) + var removeAction: FontAwesome = itemView.findViewById(R.id.settings_actions_row_remove) - - override fun onClick(v: View) { - val pos = adapterPosition - val context: Context = v.context - val content = actionsList[pos] - - } + override fun onClick(v: View) { } init { itemView.setOnClickListener(this) } } @@ -46,25 +37,23 @@ class ActionsRecyclerAdapter(val activity: Activity): override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) { val actionText = actionsList[i].actionText val actionName = actionsList[i].actionName - val content = actionsList[i].content + val content = actionsList[i].data viewHolder.textView.text = actionText viewHolder.removeAction.setOnClickListener{ - val sharedPref = activity.getSharedPreferences( - activity.getString(R.string.preference_file_key), Context.MODE_PRIVATE) - val editor : SharedPreferences.Editor = sharedPref.edit() - editor.putString("action_$actionName", "") // clear it - editor.apply() + launcherPreferences.edit() + .putString("action_$actionName", "") // clear it + .apply() viewHolder.fontAwesome.visibility = View.INVISIBLE viewHolder.img.visibility = View.INVISIBLE viewHolder.removeAction.visibility = View.GONE viewHolder.chooseButton.visibility = View.VISIBLE viewHolder.chooseButton.setOnClickListener{ chooseApp(actionName.toString()) } - if (getSavedTheme(activity) =="custom") - setButtonColor(viewHolder.chooseButton, vibrantColor) + + setButtonColor(viewHolder.chooseButton, vibrantColor) } if (content!!.startsWith("launcher")) { @@ -84,15 +73,16 @@ class ActionsRecyclerAdapter(val activity: Activity): viewHolder.img.setImageDrawable(activity.packageManager.getApplicationIcon(content.toString())) viewHolder.img.setOnClickListener{ chooseApp(actionName.toString()) } - if (getSavedTheme(activity) == "dark") transformGrayscale(viewHolder.img) + if (getSavedTheme(activity) == "dark") transformGrayscale( + viewHolder.img + ) } catch (e : Exception) { // the button is shown, user asked to select an action viewHolder.img.visibility = View.INVISIBLE viewHolder.removeAction.visibility = View.GONE viewHolder.chooseButton.visibility = View.VISIBLE viewHolder.chooseButton.setOnClickListener{ chooseApp(actionName.toString()) } - if (getSavedTheme(activity) =="custom") - setButtonColor(viewHolder.chooseButton, vibrantColor) + setButtonColor(viewHolder.chooseButton, vibrantColor) } } } @@ -101,28 +91,46 @@ class ActionsRecyclerAdapter(val activity: Activity): override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val inflater = LayoutInflater.from(parent.context) - val view: View = inflater.inflate(R.layout.recycler_actions_row, parent, false) + val view: View = inflater.inflate(R.layout.settings_actions_row, parent, false) return ViewHolder(view) } init { actionsList = ArrayList() - actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_up),"upApp", upApp)) - actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_down),"downApp", downApp)) - actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_left), "leftApp", leftApp)) - actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_right), "rightApp", rightApp)) - actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_vol_up), "volumeUpApp", volumeUpApp)) - actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_vol_down), "volumeDownApp", volumeDownApp)) - actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_double_click), "doubleClickApp", doubleClickApp)) - actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_long_click), "longClickApp", longClickApp)) + actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_up),"upApp", + upApp + )) + actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_down),"downApp", + downApp + )) + actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_left), "leftApp", + leftApp + )) + actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_right), "rightApp", + rightApp + )) + actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_vol_up), "volumeUpApp", + volumeUpApp + )) + actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_vol_down), "volumeDownApp", + volumeDownApp + )) + actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_double_click), "doubleClickApp", + doubleClickApp + )) + actionsList.add(ActionInfo(activity.getString(R.string.settings_choose_long_click), "longClickApp", + longClickApp + )) } /* */ private fun chooseApp(forAction: String) { - val intent = Intent(activity, ChooseActivity::class.java) - intent.putExtra("action", "pick") + val intent = Intent(activity, ListActivity::class.java) + intent.putExtra("intention", "pick") intent.putExtra("forApp", forAction) // for which action we choose the app intendedSettingsPause = true - activity.startActivityForResult(intent, REQUEST_CHOOSE_APP) + activity.startActivityForResult(intent, + REQUEST_CHOOSE_APP + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/settings/actions/SettingsFragmentApps.kt b/app/src/main/java/com/finnmglas/launcher/settings/actions/SettingsFragmentActions.kt similarity index 57% rename from app/src/main/java/com/finnmglas/launcher/settings/actions/SettingsFragmentApps.kt rename to app/src/main/java/com/finnmglas/launcher/settings/actions/SettingsFragmentActions.kt index b060540..c4a4e34 100644 --- a/app/src/main/java/com/finnmglas/launcher/settings/actions/SettingsFragmentApps.kt +++ b/app/src/main/java/com/finnmglas/launcher/settings/actions/SettingsFragmentActions.kt @@ -10,55 +10,62 @@ import android.view.ViewGroup import android.widget.Toast import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager -import com.finnmglas.launcher.choose.ChooseActivity -import com.finnmglas.launcher.R -import com.finnmglas.launcher.extern.* +import com.finnmglas.launcher.* +import com.finnmglas.launcher.list.ListActivity import com.finnmglas.launcher.settings.intendedSettingsPause -import kotlinx.android.synthetic.main.fragment_settings_apps.* +import kotlinx.android.synthetic.main.settings_actions.* -/** The 'Apps' Tab associated Fragment in Settings */ +/** + * The [SettingsFragmentActions] is a used as a tab in the SettingsActivity. + * + * It is used to change Apps / Intents to be launched when a specific action + * is triggered. + * It also allows the user to view all apps ([ListActivity]) or install new ones. + */ -class SettingsFragmentApps : Fragment() { - - /** Lifecycle functions */ +class SettingsFragmentActions : Fragment(), UIObject { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_settings_apps, container, false) + return inflater.inflate(R.layout.settings_actions, container, false) } override fun onStart() { - - if (getSavedTheme(context!!) == "custom") { - fragment_settings_apps_container.setBackgroundColor(dominantColor) - - setButtonColor(fragment_settings_apps_btn, vibrantColor) - setButtonColor(fragment_settings_apps_install_btn, vibrantColor) - } - + super.onStart() + super.onStart() // set up the list / recycler val actionViewManager = LinearLayoutManager(context) val actionViewAdapter = ActionsRecyclerAdapter( activity!! ) - activity_settings_actions_recycler_view.apply { + settings_actions_rview.apply { // improve performance (since content changes don't change the layout size) setHasFixedSize(true) layoutManager = actionViewManager adapter = actionViewAdapter } + } + + override fun applyTheme() { + settings_actions_container.setBackgroundColor(dominantColor) + + setButtonColor(settings_actions_button_view_apps, vibrantColor) + setButtonColor(settings_actions_button_install_apps, vibrantColor) + } + + override fun setOnClicks() { // App management buttons - fragment_settings_apps_btn.setOnClickListener{ - val intent = Intent(this.context, ChooseActivity::class.java) - intent.putExtra("action", "view") + settings_actions_button_view_apps.setOnClickListener{ + val intent = Intent(this.context, ListActivity::class.java) + intent.putExtra("intention", "view") intendedSettingsPause = true startActivity(intent) } - fragment_settings_apps_install_btn.setOnClickListener{ + settings_actions_button_install_apps.setOnClickListener{ try { val rateIntent = Intent( Intent.ACTION_VIEW, @@ -70,7 +77,5 @@ class SettingsFragmentApps : Fragment() { .show() } } - - super.onStart() } } \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/settings/meta/SettingsFragmentMeta.kt b/app/src/main/java/com/finnmglas/launcher/settings/meta/SettingsFragmentMeta.kt index 73cc1d8..1a46578 100644 --- a/app/src/main/java/com/finnmglas/launcher/settings/meta/SettingsFragmentMeta.kt +++ b/app/src/main/java/com/finnmglas/launcher/settings/meta/SettingsFragmentMeta.kt @@ -2,7 +2,6 @@ package com.finnmglas.launcher.settings.meta import android.app.AlertDialog import android.content.ActivityNotFoundException -import android.content.Context import android.content.DialogInterface import android.content.Intent import android.net.Uri @@ -13,131 +12,33 @@ import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import com.finnmglas.launcher.* import com.finnmglas.launcher.tutorial.TutorialActivity -import com.finnmglas.launcher.R -import com.finnmglas.launcher.extern.* import com.finnmglas.launcher.settings.intendedSettingsPause -import kotlinx.android.synthetic.main.fragment_settings_meta.* +import kotlinx.android.synthetic.main.settings_meta.* -/** The 'Meta' Tab associated Fragment in Settings */ - -class SettingsFragmentMeta : Fragment() { - - /** Lifecycle functions */ +/** + * The [SettingsFragmentMeta] is a used as a tab in the SettingsActivity. + * + * It is used to change settings and access resources about Launcher, + * that are not directly related to the behaviour of the app itself. + * + * (greek `meta` = above, next level) + */ +class SettingsFragmentMeta : Fragment(), UIObject { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_settings_meta, container, false) + return inflater.inflate(R.layout.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) - setButtonColor(fragment_settings_meta_contact_btn, vibrantColor) - setButtonColor(fragment_settings_meta_donate_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 { - intendedSettingsPause = true - // 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 { - intendedSettingsPause = true - startActivity(Intent(this.context, TutorialActivity::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 { - intendedSettingsPause = true - openNewTabWindow(getString(R.string.settings_footer_repo), this.context!!) - } - // rate app - fragment_settings_meta_footer_play_icon.setOnClickListener { - try { - val rateIntent = rateIntentForUrl("market://details") - intendedSettingsPause = true - startActivity(rateIntent) - } catch (e: ActivityNotFoundException) { - val rateIntent = rateIntentForUrl("https://play.google.com/store/apps/details") - intendedSettingsPause = true - startActivity(rateIntent) - } - } - - fragment_settings_meta_footer_globe_icon.setOnClickListener { - intendedSettingsPause = true - openNewTabWindow(getString(R.string.settings_footer_web), this.context!!) - } - - // contact developer - fragment_settings_meta_contact_btn.setOnClickListener { - intendedSettingsPause = true - openNewTabWindow(getString(R.string.settings_meta_contact_url), context!!) - } - - // donate - fragment_settings_meta_donate_btn.setOnClickListener { - intendedSettingsPause = true - openNewTabWindow(getString(R.string.settings_meta_donate_url), context!!) - } - - super.onStart() + super.onStart() + super.onStart() } - /** Extra functions */ - // Rate App // Just copied code from https://stackoverflow.com/q/10816757/12787264 // that is how we write good software ^^ @@ -156,4 +57,120 @@ class SettingsFragmentMeta : Fragment() { intent.addFlags(flags) return intent } + + override fun applyTheme() { + settings_meta_container.setBackgroundColor(dominantColor) + + setButtonColor(settings_meta_button_select_launcher, vibrantColor) + setButtonColor(settings_meta_button_view_tutorial, vibrantColor) + setButtonColor(settings_meta_button_reset_settings, vibrantColor) + setButtonColor(settings_meta_button_contact, vibrantColor) + setButtonColor(settings_meta_button_donate, vibrantColor) + + settings_meta_icon_google_play.setTextColor(vibrantColor) + settings_meta_icon_github.setTextColor(vibrantColor) + settings_meta_icon_globe.setTextColor(vibrantColor) + } + + override fun setOnClicks() { + + // Button onClicks + + settings_meta_button_select_launcher.setOnClickListener { + intendedSettingsPause = true + // 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() + } + } + + settings_meta_button_view_tutorial.setOnClickListener { + intendedSettingsPause = true + startActivity(Intent(this.context, TutorialActivity::class.java)) + } + + // prompting for settings-reset confirmation + settings_meta_button_reset_settings.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!!) + activity!!.finish() + }) + .setNegativeButton(android.R.string.no, null) + .setIcon(android.R.drawable.ic_dialog_alert) + .show() + } + + // Footer onClicks + settings_meta_icon_github.setOnClickListener { + intendedSettingsPause = true + openNewTabWindow( + getString(R.string.settings_footer_repo), + this.context!! + ) + } + // rate app + settings_meta_icon_google_play.setOnClickListener { + try { + val rateIntent = rateIntentForUrl("market://details") + intendedSettingsPause = true + startActivity(rateIntent) + } catch (e: ActivityNotFoundException) { + val rateIntent = rateIntentForUrl("https://play.google.com/store/apps/details") + intendedSettingsPause = true + startActivity(rateIntent) + } + } + + settings_meta_icon_globe.setOnClickListener { + intendedSettingsPause = true + openNewTabWindow( + getString(R.string.settings_footer_web), + this.context!! + ) + } + + // contact developer + settings_meta_button_contact.setOnClickListener { + intendedSettingsPause = true + openNewTabWindow( + getString(R.string.settings_meta_contact_url), + context!! + ) + } + + // donate + settings_meta_button_donate.setOnClickListener { + intendedSettingsPause = true + openNewTabWindow( + getString(R.string.settings_meta_donate_url), + context!! + ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/settings/theme/SettingsFragmentTheme.kt b/app/src/main/java/com/finnmglas/launcher/settings/theme/SettingsFragmentTheme.kt index bae4281..e667745 100644 --- a/app/src/main/java/com/finnmglas/launcher/settings/theme/SettingsFragmentTheme.kt +++ b/app/src/main/java/com/finnmglas/launcher/settings/theme/SettingsFragmentTheme.kt @@ -1,9 +1,7 @@ package com.finnmglas.launcher.settings.theme 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 @@ -16,73 +14,27 @@ 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 com.finnmglas.launcher.* import com.finnmglas.launcher.settings.intendedSettingsPause -import kotlinx.android.synthetic.main.fragment_settings_theme.* +import kotlinx.android.synthetic.main.settings_theme.* -/** The 'Theme' Tab associated Fragment in Settings */ - -class SettingsFragmentTheme : Fragment() { - - /** Lifecycle functions */ +/** + * The [SettingsFragmentTheme] is a used as a tab in the SettingsActivity. + * + * It is used to change themes, select wallpapers ... theme related stuff + */ +class SettingsFragmentTheme : Fragment(), UIObject { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_settings_theme, container, false) + return inflater.inflate(R.layout.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) - setButtonColor(fragment_settings_theme_custom_examples_btn, vibrantColor) - } - } - - // Theme changing buttons - fragment_settings_theme_select_dark_btn.setOnClickListener { - intendedSettingsPause = true - saveTheme(context!!, "dark") - activity!!.recreate() - } - fragment_settings_theme_select_finn_btn.setOnClickListener { - intendedSettingsPause = true - saveTheme(context!!, "finn") - activity!!.recreate() - } - fragment_settings_theme_select_custom_btn.setOnClickListener { - intendedSettingsPause = true - // Request permission (on newer APIs) - if (Build.VERSION.SDK_INT >= 23) { - when { - ContextCompat.checkSelfPermission(context!!, - Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED - -> letUserPickImage(true) - shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) - -> {} - else - -> requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_PERMISSION_STORAGE) - } - } - else letUserPickImage() - } - fragment_settings_theme_custom_examples_btn.setOnClickListener { - intendedSettingsPause = true - // Show example usage - openNewTabWindow("https://github.com/finnmglas/Launcher/blob/master/docs/README.md", context!!) - } - - super.onStart() + super.onStart() + super.onStart() } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { @@ -94,8 +46,6 @@ class SettingsFragmentTheme : Fragment() { } } - /** Extra functions */ - private fun letUserPickImage(crop: Boolean = false) { val intent = Intent() intent.type = "image/*" @@ -114,7 +64,6 @@ class SettingsFragmentTheme : Fragment() { } private fun handlePickedImage(resultCode: Int, data: Intent?) { - if (resultCode == AppCompatActivity.RESULT_OK) { if (data == null) return @@ -128,23 +77,106 @@ class SettingsFragmentTheme : Fragment() { // never let dominantColor equal vibrantColor if(dominantColor == vibrantColor) { - vibrantColor = manipulateColor(vibrantColor, 1.2F) - dominantColor = manipulateColor(dominantColor, 0.8F) + vibrantColor = + manipulateColor( + vibrantColor, + 1.2F + ) + dominantColor = + manipulateColor( + dominantColor, + 0.8F + ) } /* 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() + launcherPreferences.edit() + .putString("background_uri", imageUri.toString()) + .putInt("custom_dominant", dominantColor) + .putInt("custom_vibrant", vibrantColor) + .apply() - saveTheme(context!!, "custom") + saveTheme("custom") intendedSettingsPause = true activity!!.recreate() } } } } + + override fun applyTheme() { + // Hide 'select' button for the selected theme or allow customisation + when (getSavedTheme(context!!)) { + "dark" -> settings_theme_dark_button_select.visibility = View.INVISIBLE + "finn" -> settings_theme_finn_button_select.visibility = View.INVISIBLE + "custom" -> + settings_theme_custom_button_select.text = getString(R.string.settings_select_image) + } + + settings_theme_container.setBackgroundColor(dominantColor) + setButtonColor(settings_theme_finn_button_select, vibrantColor) + setButtonColor(settings_theme_dark_button_select, vibrantColor) + setButtonColor(settings_theme_custom_button_select, vibrantColor) + setButtonColor(settings_theme_custom_button_examples, vibrantColor) + } + + override fun setOnClicks() { + // Theme changing buttons + settings_theme_dark_button_select.setOnClickListener { + dominantColor = resources.getColor(R.color.darkTheme_background_color) + vibrantColor = resources.getColor(R.color.darkTheme_accent_color) + + launcherPreferences.edit() + .putString("background_uri", "") + .putInt("custom_dominant", dominantColor) + .putInt("custom_vibrant", vibrantColor) + .apply() + + saveTheme("dark") + + intendedSettingsPause = true + activity!!.recreate() + } + settings_theme_finn_button_select.setOnClickListener { + dominantColor = resources.getColor(R.color.finnmglasTheme_background_color) + vibrantColor = resources.getColor(R.color.finnmglasTheme_accent_color) + + launcherPreferences.edit() + .putString("background_uri", "") + .putInt("custom_dominant", dominantColor) + .putInt("custom_vibrant", vibrantColor) + .apply() + + saveTheme("finn") + + intendedSettingsPause = true + activity!!.recreate() + } + settings_theme_custom_button_select.setOnClickListener { + intendedSettingsPause = true + // Request permission (on newer APIs) + if (Build.VERSION.SDK_INT >= 23) { + when { + ContextCompat.checkSelfPermission(context!!, + Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED + -> letUserPickImage(true) + shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) + -> {} + else + -> requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), + REQUEST_PERMISSION_STORAGE + ) + } + } + else letUserPickImage() + } + settings_theme_custom_button_examples.setOnClickListener { + intendedSettingsPause = true + // Show example usage + openNewTabWindow( + "https://github.com/finnmglas/Launcher/blob/master/docs/README.md", + context!! + ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/finnmglas/launcher/tutorial/TutorialActivity.kt b/app/src/main/java/com/finnmglas/launcher/tutorial/TutorialActivity.kt index 4c47ec2..0c4175d 100644 --- a/app/src/main/java/com/finnmglas/launcher/tutorial/TutorialActivity.kt +++ b/app/src/main/java/com/finnmglas/launcher/tutorial/TutorialActivity.kt @@ -1,68 +1,44 @@ package com.finnmglas.launcher.tutorial import android.content.Context -import android.content.SharedPreferences import android.os.Bundle import android.util.TypedValue import android.view.* import androidx.appcompat.app.AppCompatActivity -import com.finnmglas.launcher.R -import com.finnmglas.launcher.extern.* -import kotlinx.android.synthetic.main.activity_tutorial.* +import com.finnmglas.launcher.* +import kotlinx.android.synthetic.main.tutorial.* - -class TutorialActivity : AppCompatActivity(){ - - /** Variables for this activity */ +/** + * The [TutorialActivity] is displayed automatically on new installations. + * It can also be opened from Settings. + * + * It tells the user about the concept behind launcher + * and helps with the setup process (on new installations) + */ +class TutorialActivity: AppCompatActivity(), UIObject { private var menuNumber = 0 private var defaultApps = mutableListOf() private var isFirstTime = false - /** Activity Lifecycle functions */ - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - // Flags - window.setFlags( - WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN - ) - 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_tutorial) - - 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 + // Initialise layout + setContentView(R.layout.tutorial) loadMenu(this) - val sharedPref = this.getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE) - - isFirstTime = !sharedPref.getBoolean("startedBefore", false) - + // Check if it is the first time starting the app + isFirstTime = !launcherPreferences.getBoolean("startedBefore", false) 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() } + defaultApps = resetSettings(this) // UP, DOWN, RIGHT, LEFT, VOLUME_UP, VOLUME_DOWN + else tutorial_appbar.visibility = View.VISIBLE } - /** Touch- and Key-related functions to navigate */ + override fun onStart() { + super.onStart() + super.onStart() + } override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { if (keyCode == KeyEvent.KEYCODE_VOLUME_UP){ @@ -85,40 +61,43 @@ class TutorialActivity : AppCompatActivity(){ loadMenu(this) } - fun backToSettings(view: View){ - finish() - } - - /** Touch- and Key-related functions to navigate */ - - private fun loadMenu(context :Context) { // Context needed for packageManager - + private fun loadMenu(context: Context) { // Context needed for packageManager val intro = resources.getStringArray(R.array.intro) if (menuNumber < intro.size){ val entry = intro[menuNumber].split("|").toTypedArray() //heading|infoText|hintText|size - activity_firststartup_section_heading.text = entry[0] + tutorial_page_heading.text = entry[0] if (entry[4] == "1" && isFirstTime) - activity_firststartup_descriptive_text.text = String.format(entry[1], + tutorial_page_text.text = String.format(entry[1], defaultApps[0], defaultApps[1], defaultApps[2], defaultApps[3], defaultApps[4], defaultApps[5]) else if (entry[4] == "1" && !isFirstTime) - activity_firststartup_descriptive_text.text = String.format(entry[1], + tutorial_page_text.text = String.format(entry[1], "-", "-", "-", "-", "-", "-") - 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 tutorial_page_text.text = entry[1] + tutorial_page_hint.text = entry[2] + tutorial_page_text.setTextSize(TypedValue.COMPLEX_UNIT_SP, entry[3].toFloat()) } else { // End intro if (isFirstTime){ - val sharedPref = this.getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE) - - val editor: SharedPreferences.Editor = sharedPref.edit() - editor.putBoolean("startedBefore", true) // never auto run this again - editor.putLong("firstStartup", System.currentTimeMillis() / 1000L) // record first startup timestamp - editor.apply() + launcherPreferences.edit() + .putBoolean("startedBefore", true) // never auto run this again + .putLong("firstStartup", System.currentTimeMillis() / 1000L) // record first startup timestamp + .apply() } finish() } } + + override fun applyTheme() { + tutorial_appbar.setBackgroundColor(dominantColor) + tutorial_container.setBackgroundColor(dominantColor) + tutorial_close.setTextColor(vibrantColor) + + tutorial_page_hint.blink() // animate + } + + override fun setOnClicks() { + tutorial_close.setOnClickListener() { finish() } + } } diff --git a/app/src/main/res/anim/left_right.xml b/app/src/main/res/anim/left_right.xml new file mode 100644 index 0000000..1dd2cb4 --- /dev/null +++ b/app/src/main/res/anim/left_right.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/right_left.xml b/app/src/main/res/anim/right_left.xml new file mode 100644 index 0000000..58e2060 --- /dev/null +++ b/app/src/main/res/anim/right_left.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/anim/top_down.xml b/app/src/main/res/anim/top_down.xml new file mode 100644 index 0000000..239c475 --- /dev/null +++ b/app/src/main/res/anim/top_down.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_choose_apps.xml b/app/src/main/res/layout/fragment_choose_apps.xml deleted file mode 100644 index 77194c0..0000000 --- a/app/src/main/res/layout/fragment_choose_apps.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_choose_other.xml b/app/src/main/res/layout/fragment_choose_other.xml deleted file mode 100644 index 56b9c62..0000000 --- a/app/src/main/res/layout/fragment_choose_other.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_settings_apps.xml b/app/src/main/res/layout/fragment_settings_apps.xml deleted file mode 100644 index f2c59f6..0000000 --- a/app/src/main/res/layout/fragment_settings_apps.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - -