Refactor: Created more usable functions.
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
package com.github.droidworksstudio.ktx
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.WindowManager
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
fun Activity.getDisplayWidth(): Int {
|
||||
val windowManager = getSystemService<WindowManager>() ?: return 0
|
||||
val width = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
val windowMetrics = windowManager.currentWindowMetrics
|
||||
val bounds = windowMetrics.bounds
|
||||
bounds.width()
|
||||
} else {
|
||||
val displayMetrics = DisplayMetrics()
|
||||
@Suppress("DEPRECATION")
|
||||
windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||
displayMetrics.widthPixels
|
||||
}
|
||||
return width
|
||||
}
|
||||
|
||||
|
||||
fun Activity.enablePortraitScreenOrientationForMobile(isPortrait: Boolean) {
|
||||
if (!isTabletConfig()) {
|
||||
requestedOrientation = if (isPortrait) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
else ActivityInfo.SCREEN_ORIENTATION_SENSOR
|
||||
}
|
||||
}
|
||||
|
||||
fun Activity.lockScreenOrientationChanges(lock: Boolean) {
|
||||
requestedOrientation =
|
||||
if (lock) ActivityInfo.SCREEN_ORIENTATION_LOCKED else ActivityInfo.SCREEN_ORIENTATION_SENSOR
|
||||
}
|
||||
|
||||
fun Activity.openEmailClient(emailTo: String, subject: String) {
|
||||
val uriString = "mailto:$emailTo?subject=$subject"
|
||||
val emailIntent = Intent(Intent.ACTION_SENDTO)
|
||||
|
||||
emailIntent.data = Uri.parse(uriString)
|
||||
|
||||
if (emailIntent.resolveActivity(packageManager) != null) {
|
||||
startActivity(emailIntent)
|
||||
} else {
|
||||
startActivity(Intent.createChooser(emailIntent, null))
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
fun Activity.openNotificationSettings() {
|
||||
val intent = Intent()
|
||||
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
|
||||
intent.putExtra("app_package", packageName)
|
||||
intent.putExtra("app_uid", applicationInfo.uid)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting from appcompat v.1.1.0 system overrides baseContext after [attachBaseContext]
|
||||
* causing resetting locale settings. To prevent this we must call [applyOverrideConfiguration]
|
||||
* with modified [overrideConfiguration] object.
|
||||
*
|
||||
* That's a workaround until this behaviour will be fixed in future appcompat releases.
|
||||
*/
|
||||
fun Activity.setupOverrideConfiguration(overrideConfiguration: Configuration?): Configuration? {
|
||||
if (overrideConfiguration != null) {
|
||||
val uiMode = overrideConfiguration.uiMode
|
||||
overrideConfiguration.setTo(baseContext.resources.configuration)
|
||||
overrideConfiguration.uiMode = uiMode
|
||||
}
|
||||
|
||||
return overrideConfiguration
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the current Google user's account
|
||||
* subscriptions in Google Play app.
|
||||
*/
|
||||
fun Activity.openGooglePlaySubscriptions() {
|
||||
val uriString = "https://play.google.com/store/account/subscriptions"
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(uriString))
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun Activity.showLongToast(message: String) {
|
||||
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
fun Activity.showShortToast(message: String) {
|
||||
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package com.github.droidworksstudio.ktx
|
||||
|
||||
import android.app.SearchManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.LauncherApps
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.AdaptiveIconDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.UserHandle
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.core.os.ConfigurationCompat
|
||||
import androidx.lifecycle.LifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
|
||||
fun Context.isTabletConfig(): Boolean =
|
||||
resources.configuration.smallestScreenWidthDp >= SMALLEST_WIDTH_600
|
||||
|
||||
fun Context.isPortraitSw600Config(): Boolean =
|
||||
resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT &&
|
||||
resources.configuration.smallestScreenWidthDp >= SMALLEST_WIDTH_600
|
||||
|
||||
fun Context.isLandscapeSw600Config(): Boolean =
|
||||
resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE &&
|
||||
resources.configuration.smallestScreenWidthDp >= SMALLEST_WIDTH_600
|
||||
|
||||
fun Context.isLandscapeDisplayOrientation(): Boolean =
|
||||
resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
|
||||
internal fun Context.addLifecycleObserver(observer: LifecycleObserver) {
|
||||
when (this) {
|
||||
is LifecycleOwner -> this.lifecycle.addObserver(observer)
|
||||
is ContextThemeWrapper -> this.baseContext.addLifecycleObserver(observer)
|
||||
is androidx.appcompat.view.ContextThemeWrapper -> this.baseContext.addLifecycleObserver(
|
||||
observer
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.getMiddleScreenX(): Int {
|
||||
val screenEndX = this.resources.displayMetrics.widthPixels
|
||||
return (screenEndX / 2)
|
||||
}
|
||||
|
||||
fun Context.getMiddleScreenY(): Int {
|
||||
val screenEndY = this.resources.displayMetrics.heightPixels
|
||||
return (screenEndY / 2)
|
||||
}
|
||||
|
||||
const val SMALLEST_WIDTH_600: Int = 600
|
||||
fun Context.createIconWithResourceCompat(
|
||||
@DrawableRes vectorIconId: Int,
|
||||
@DrawableRes adaptiveIconForegroundId: Int,
|
||||
@DrawableRes adaptiveIconBackgroundId: Int
|
||||
): IconCompat {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val adaptiveIconDrawable = AdaptiveIconDrawable(
|
||||
ContextCompat.getDrawable(this, adaptiveIconBackgroundId),
|
||||
ContextCompat.getDrawable(this, adaptiveIconForegroundId)
|
||||
)
|
||||
|
||||
IconCompat.createWithAdaptiveBitmap(
|
||||
adaptiveIconDrawable.toBitmap(
|
||||
config = Bitmap.Config.ARGB_8888
|
||||
)
|
||||
)
|
||||
} else {
|
||||
IconCompat.createWithResource(this, vectorIconId)
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.currentLanguage() = ConfigurationCompat.getLocales(resources.configuration)[0]?.language
|
||||
|
||||
fun Context.openBrowser(url: String, clearFromRecent: Boolean = true) {
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
browserIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
if (clearFromRecent) browserIntent.flags =
|
||||
browserIntent.flags or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
|
||||
startActivity(browserIntent)
|
||||
}
|
||||
|
||||
fun Context.inflate(resource: Int, root: ViewGroup? = null, attachToRoot: Boolean = false): View {
|
||||
return LayoutInflater.from(this).inflate(resource, root, attachToRoot)
|
||||
}
|
||||
|
||||
fun Context.getColorCompat(@ColorRes color: Int) = ContextCompat.getColor(this, color)
|
||||
|
||||
fun Context.getDrawableCompat(@DrawableRes drawable: Int) =
|
||||
ContextCompat.getDrawable(this, drawable)
|
||||
|
||||
fun Context.showLongToast(message: String) {
|
||||
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
fun Context.showShortToast(message: String) {
|
||||
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
fun Context.openSearch(query: String? = null) {
|
||||
val intent = Intent(Intent.ACTION_WEB_SEARCH)
|
||||
intent.putExtra(SearchManager.QUERY, query ?: "")
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun Context.openUrl(url: String) {
|
||||
if (url.isEmpty()) return
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.data = Uri.parse(url)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun Context.isPackageInstalled(
|
||||
packageName: String,
|
||||
userHandle: UserHandle = android.os.Process.myUserHandle()
|
||||
): Boolean {
|
||||
val launcher = getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
|
||||
val activityInfo = launcher.getActivityList(packageName, userHandle)
|
||||
return activityInfo.size > 0
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.github.droidworksstudio.ktx
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
fun Fragment.showKeyboard() {
|
||||
val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
||||
view?.let { v ->
|
||||
v.requestFocus()
|
||||
imm?.showSoftInput(v, InputMethodManager.SHOW_IMPLICIT)
|
||||
}
|
||||
}
|
||||
|
||||
fun Fragment.hideKeyboard() {
|
||||
val view = view?.findFocus() ?: return
|
||||
val imm: InputMethodManager? =
|
||||
view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
|
||||
imm?.hideSoftInputFromWindow(view.windowToken, 0)
|
||||
view.clearFocus()
|
||||
}
|
||||
|
||||
fun Fragment.showLongToast(message: String) {
|
||||
Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
fun Fragment.showShortToast(message: String) {
|
||||
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
fun Fragment.restartApp() {
|
||||
val packageManager = requireContext().packageManager
|
||||
val intent = packageManager.getLaunchIntentForPackage(requireContext().packageName)
|
||||
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
if (intent != null) {
|
||||
startActivity(intent)
|
||||
}
|
||||
requireActivity().finish()
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.github.droidworksstudio.ktx
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.util.TypedValue
|
||||
|
||||
/**
|
||||
* Converts value in pixels (px) into value in device-independent pixels (dp)
|
||||
*/
|
||||
fun Int.pxToDp(): Int {
|
||||
val metrics = Resources.getSystem().displayMetrics
|
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, this.toFloat(), metrics).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts value in pixels (px) into value in scaled pixels (sp)
|
||||
*/
|
||||
fun Int.pxToSp(): Int {
|
||||
val metrics = Resources.getSystem().displayMetrics
|
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, this.toFloat(), metrics).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts value in device-independent pixels (dp) into value in pixels (px)
|
||||
*/
|
||||
fun Int.dpToPx(): Int {
|
||||
val metrics = Resources.getSystem().displayMetrics
|
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), metrics).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts value in density-independent pixels (dp) into value in scaled pixels (sp)
|
||||
*/
|
||||
fun Int.dpToSp(): Int {
|
||||
val metrics = Resources.getSystem().displayMetrics
|
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), metrics).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts value in scaled pixels (sp) into value in device-independent pixels (dp)
|
||||
*/
|
||||
fun Int.spToDp(): Int {
|
||||
val metrics = Resources.getSystem().displayMetrics
|
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, this.toFloat(), metrics).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts value in scaled pixels (sp) into value in pixels (px)
|
||||
*/
|
||||
fun Int.spToPx(): Int {
|
||||
val metrics = Resources.getSystem().displayMetrics
|
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, this.toFloat(), metrics).toInt()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.github.droidworksstudio.ktx
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.text.Html
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.TextPaint
|
||||
import android.text.style.ClickableSpan
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.StyleSpan
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.core.text.getSpans
|
||||
|
||||
/**
|
||||
* Returns [Spannable] where the term is
|
||||
* highlighted as a bold text.
|
||||
*/
|
||||
fun String.highlightTerm(term: String): SpannableString {
|
||||
val regex = Regex(term.lowercase())
|
||||
val matches = regex.findAll(this.lowercase())
|
||||
val ranges = matches.map { it.range }
|
||||
val spannable = SpannableString(this)
|
||||
ranges.forEach {
|
||||
spannable.setSpan(
|
||||
ForegroundColorSpan(Color.BLACK),
|
||||
it.first,
|
||||
it.last + 1,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
|
||||
return spannable
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns [Spannable] where all <em> (e.g. italic) tags are replaced
|
||||
* with <strong> (e.g. bold)
|
||||
*
|
||||
* Important: works only with <em> tags replacing it only with <strong> tags.
|
||||
* Using deprecated [Html.fromHtml] as it's deprecated only from API 24.
|
||||
*/
|
||||
fun String.replaceItalicWithBold(): SpannableString {
|
||||
val spannable = SpannableString(Html.fromHtml(this, Html.FROM_HTML_MODE_LEGACY))
|
||||
|
||||
spannable.getSpans<StyleSpan>(0, spannable.length)
|
||||
.replaceItalicInSpannable(spannable)
|
||||
|
||||
return spannable
|
||||
}
|
||||
|
||||
fun Array<out StyleSpan>.replaceItalicInSpannable(spannable: SpannableString) = forEach {
|
||||
if (it.style == Typeface.ITALIC) {
|
||||
spannable.setSpan(
|
||||
StyleSpan(Typeface.BOLD),
|
||||
spannable.getSpanStart(it),
|
||||
spannable.getSpanEnd(it),
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
spannable.setSpan(
|
||||
ForegroundColorSpan(Color.BLACK),
|
||||
spannable.getSpanStart(it),
|
||||
spannable.getSpanEnd(it),
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
spannable.removeSpan(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun CharSequence.makeTextClickable(
|
||||
clickableText: String,
|
||||
clickableTextColor: Int,
|
||||
clickListener: () -> (Unit)
|
||||
): SpannableString {
|
||||
val regex = Regex(clickableText)
|
||||
val matches = regex.findAll(this)
|
||||
val ranges = matches.map { it.range }
|
||||
val spannable = SpannableString(this)
|
||||
val clickableSpan = object : ClickableSpan() {
|
||||
override fun onClick(widget: View) {
|
||||
clickListener.invoke()
|
||||
}
|
||||
|
||||
override fun updateDrawState(ds: TextPaint) {
|
||||
ds.color = clickableTextColor
|
||||
ds.isUnderlineText = false
|
||||
}
|
||||
}
|
||||
ranges.forEach {
|
||||
spannable.setSpan(clickableSpan, it.first, it.last + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
|
||||
return spannable
|
||||
}
|
||||
|
||||
fun String.showLongToast(context: Context) {
|
||||
Toast.makeText(context, this, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
fun String.showShortToast(context: Context) {
|
||||
Toast.makeText(context, this, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
package com.github.droidworksstudio.ktx
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.Window
|
||||
import android.view.WindowManager
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.AutoCompleteTextView
|
||||
import android.widget.CheckBox
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.core.text.toSpannable
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.core.widget.TextViewCompat
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
fun EditText.value() = text.toString()
|
||||
|
||||
inline fun EditText.setOnEditorActionListener(crossinline onAction: (Int) -> Boolean) {
|
||||
setOnEditorActionListener { _, actionId, _ -> onAction(actionId) }
|
||||
}
|
||||
|
||||
inline fun EditText.setOnDoneEditorActionListener(crossinline onAction: (Int) -> Unit = {}) {
|
||||
setOnEditorActionListener { actionId ->
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
onAction(actionId)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs [onAction] block and hides a keyboard when [EditorInfo.IME_ACTION_DONE] is fired.
|
||||
*
|
||||
* @param onAction function to be called when [[EditorInfo.IME_ACTION_DONE] is fired
|
||||
*/
|
||||
inline fun EditText.setHideKeyboardEditorActionListener(crossinline onAction: (Int) -> Unit = {}) {
|
||||
setOnDoneEditorActionListener {
|
||||
hideKeyboard()
|
||||
onAction(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun View.showKeyboard() {
|
||||
val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
|
||||
this.let { v ->
|
||||
v.requestFocus()
|
||||
imm?.showSoftInput(v, InputMethodManager.SHOW_IMPLICIT)
|
||||
}
|
||||
}
|
||||
|
||||
fun View.hideKeyboard() {
|
||||
val imm: InputMethodManager? =
|
||||
context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
|
||||
imm?.hideSoftInputFromWindow(windowToken, 0)
|
||||
this.clearFocus()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return [View.VISIBLE] if Boolean value is true, [View.GONE] otherwise.
|
||||
*/
|
||||
fun Boolean.asVisibleOrGoneFlag() = if (this) View.VISIBLE else View.GONE
|
||||
|
||||
/**
|
||||
* @return [View.GONE] if Boolean value is true, [View.VISIBLE] otherwise.
|
||||
*/
|
||||
fun Boolean.asGoneOrVisibleFlag() = if (this) View.GONE else View.VISIBLE
|
||||
|
||||
/**
|
||||
* @return [View.VISIBLE] if Boolean value is true, [View.INVISIBLE] otherwise.
|
||||
*/
|
||||
fun Boolean.asVisibleOrInvisibleFlag() = if (this) View.VISIBLE else View.INVISIBLE
|
||||
|
||||
/**
|
||||
* Resets nested vertical scroll position.
|
||||
*
|
||||
* Can be used to reset [CoordinatorLayout]'s behaviours that depends on scrolling views.
|
||||
*/
|
||||
fun RecyclerView.resetNestedVerticalScroll() {
|
||||
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
|
||||
dispatchNestedPreScroll(0, -Integer.MAX_VALUE, null, null)
|
||||
dispatchNestedScroll(0, -Integer.MAX_VALUE, 0, 0, null)
|
||||
stopNestedScroll()
|
||||
}
|
||||
|
||||
@SuppressLint("SwitchIntDef")
|
||||
fun LinearLayoutManager.getCurrentPosition(midScreenX: Int, midScreenY: Int): Int =
|
||||
when (this.orientation) {
|
||||
LinearLayoutManager.HORIZONTAL -> getCurrentPositionForHorizontalOrientation(midScreenX)
|
||||
LinearLayoutManager.VERTICAL -> getCurrentPositionForVerticalOrientation(midScreenY)
|
||||
else -> RecyclerView.NO_POSITION
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current position for [LinearLayoutManager.HORIZONTAL]
|
||||
*
|
||||
* @return position of center item if it exists else
|
||||
* @return first completely visible or first visible item position
|
||||
*
|
||||
* @return [RecyclerView.NO_POSITION] if [LinearLayoutManager] doesn't have horizontal orientation
|
||||
*/
|
||||
private fun LinearLayoutManager.getCurrentPositionForHorizontalOrientation(midScreenX: Int): Int {
|
||||
if (this.orientation == LinearLayoutManager.HORIZONTAL) {
|
||||
val centerPosition = getCenterPositionForHorizontalOrientation(midScreenX)
|
||||
return if (centerPosition == RecyclerView.NO_POSITION) {
|
||||
getCompletelyVisibleOrFirstPosition()
|
||||
} else centerPosition
|
||||
}
|
||||
|
||||
return RecyclerView.NO_POSITION
|
||||
}
|
||||
|
||||
private fun LinearLayoutManager.getCompletelyVisibleOrFirstPosition(): Int {
|
||||
val firstCompletelyVisiblePosition = findFirstCompletelyVisibleItemPosition()
|
||||
|
||||
return if (firstCompletelyVisiblePosition == RecyclerView.NO_POSITION) {
|
||||
findFirstVisibleItemPosition()
|
||||
} else {
|
||||
firstCompletelyVisiblePosition
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get position of center item for [LinearLayoutManager.HORIZONTAL]
|
||||
*
|
||||
* @return position of item with start X less than middle of the screen and end X more than middle of the screen
|
||||
*
|
||||
* @return [RecyclerView.NO_POSITION] if [LinearLayoutManager] doesn't have horizontal orientation or there is no item that meets requirements
|
||||
*/
|
||||
private fun LinearLayoutManager.getCenterPositionForHorizontalOrientation(midScreenX: Int): Int {
|
||||
if (this.orientation == LinearLayoutManager.HORIZONTAL) {
|
||||
val firstPosition = this.findFirstVisibleItemPosition()
|
||||
val lastPosition = this.findLastVisibleItemPosition()
|
||||
|
||||
for (position in firstPosition..lastPosition) {
|
||||
val view = this.findViewByPosition(position)
|
||||
if (view != null) {
|
||||
val viewWidth = view.measuredWidth
|
||||
val viewStartX = view.x
|
||||
val viewEndX = viewStartX + viewWidth
|
||||
if (viewStartX < midScreenX && viewEndX > midScreenX) {
|
||||
return position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RecyclerView.NO_POSITION
|
||||
}
|
||||
|
||||
return RecyclerView.NO_POSITION
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current position for [LinearLayoutManager.VERTICAL]
|
||||
*
|
||||
* @return position of center item if it exists else
|
||||
* @return first completely visible or first visible item position
|
||||
*
|
||||
* @return [RecyclerView.NO_POSITION] if [LinearLayoutManager] doesn't have vertical orientation
|
||||
*/
|
||||
|
||||
private fun LinearLayoutManager.getCurrentPositionForVerticalOrientation(midScreenY: Int): Int {
|
||||
if (this.orientation == LinearLayoutManager.VERTICAL) {
|
||||
val centerPosition = getCenterPositionForVerticalOrientation(midScreenY)
|
||||
return if (centerPosition == RecyclerView.NO_POSITION) {
|
||||
getCompletelyVisibleOrFirstPosition()
|
||||
} else centerPosition
|
||||
}
|
||||
|
||||
return RecyclerView.NO_POSITION
|
||||
}
|
||||
|
||||
/**
|
||||
* Get position of center item for [LinearLayoutManager.VERTICAL]
|
||||
*
|
||||
* @return position of item with start Y less than middle of the screen and end Y more than middle of the screen
|
||||
*
|
||||
* @return [RecyclerView.NO_POSITION] if [LinearLayoutManager] doesn't have vertical orientation or there is no item that meets requirements
|
||||
*/
|
||||
private fun LinearLayoutManager.getCenterPositionForVerticalOrientation(midScreenY: Int): Int {
|
||||
if (this.orientation == LinearLayoutManager.VERTICAL) {
|
||||
val firstPosition = this.findFirstVisibleItemPosition()
|
||||
val lastPosition = this.findLastVisibleItemPosition()
|
||||
|
||||
for (position in firstPosition..lastPosition) {
|
||||
val view = this.findViewByPosition(position)
|
||||
if (view != null) {
|
||||
val viewHeight = view.measuredHeight
|
||||
val viewStartY = view.y
|
||||
val viewEndY = viewStartY + viewHeight
|
||||
if (viewStartY < midScreenY && viewEndY > midScreenY) {
|
||||
return position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RecyclerView.NO_POSITION
|
||||
}
|
||||
|
||||
return RecyclerView.NO_POSITION
|
||||
}
|
||||
|
||||
fun View.setBottomPadding(bottomPadding: Int) {
|
||||
setPadding(paddingLeft, paddingTop, paddingRight, bottomPadding)
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers when recycler view state changes to [RecyclerView.SCROLL_STATE_IDLE].
|
||||
*/
|
||||
inline fun RecyclerView.addOnIdleStateListener(crossinline listener: (RecyclerView) -> Unit) {
|
||||
addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
listener.invoke(recyclerView)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun View.setProportionalHeight(imageWidth: Float, imageHeight: Float) {
|
||||
doOnPreDraw {
|
||||
val maxWidth = it.width.toFloat()
|
||||
it.layoutParams =
|
||||
it.layoutParams.calculateProportionalHeight(maxWidth, imageWidth, imageHeight)
|
||||
}
|
||||
}
|
||||
|
||||
fun View.setProportionalAspectRatio(imageWidth: Float, imageHeight: Float) {
|
||||
doOnPreDraw {
|
||||
val maxWidth = it.width.toFloat()
|
||||
val maxHeight = it.height.toFloat()
|
||||
it.layoutParams =
|
||||
it.layoutParams.calculateAspectRatio(maxWidth, maxHeight, imageWidth, imageHeight)
|
||||
}
|
||||
}
|
||||
|
||||
fun View.setElevationCompat(value: Float) {
|
||||
ViewCompat.setElevation(this, value)
|
||||
}
|
||||
|
||||
fun View.getElevationCompat() = ViewCompat.getElevation(this)
|
||||
|
||||
fun View.enableChildrenViews(enable: Boolean) {
|
||||
this.isEnabled = enable
|
||||
if (this is ViewGroup) {
|
||||
this.children.forEach { it.enableChildrenViews(enable) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches Live data to Edit Text.
|
||||
* This will update Live Data data with all text changes from Edit Text.
|
||||
*/
|
||||
fun <T> AutoCompleteTextView.attachLiveData(data: MutableLiveData<T>) {
|
||||
setOnItemClickListener { parent, _, position, _ ->
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
data.value = parent.adapter.getItem(position) as T
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attaches Live data to Edit Text.
|
||||
* This will update Live Data data with all text changes from Edit Text.
|
||||
*/
|
||||
fun EditText.attachLiveData(data: MutableLiveData<String>) {
|
||||
data.value = text.toString()
|
||||
addTextChangedListener {
|
||||
data.value = it.toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches Live data to Check Box.
|
||||
* This will update Live Data data with all state changes from Check Box.
|
||||
*/
|
||||
fun CheckBox.attachLiveData(data: MutableLiveData<Boolean>) {
|
||||
data.value = isChecked
|
||||
setOnCheckedChangeListener { _, isChecked ->
|
||||
data.value = isChecked
|
||||
}
|
||||
}
|
||||
|
||||
inline fun EditText.addTextChangedListener(crossinline onTextChanged: (text: CharSequence?) -> Unit) {
|
||||
addTextChangedListener(object : TextWatcher {
|
||||
override fun afterTextChanged(p0: Editable?) {
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
|
||||
}
|
||||
|
||||
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
|
||||
onTextChanged.invoke(p0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun TextView.setTextAppearanceCompat(appearanceId: Int) {
|
||||
TextViewCompat.setTextAppearance(this, appearanceId)
|
||||
}
|
||||
|
||||
fun Window.disableScreenshots() {
|
||||
this.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_SECURE,
|
||||
WindowManager.LayoutParams.FLAG_SECURE
|
||||
)
|
||||
}
|
||||
|
||||
fun Snackbar.centerMessage(): Snackbar = apply {
|
||||
val textView =
|
||||
this.view.findViewById<AppCompatTextView>(com.google.android.material.R.id.snackbar_text)
|
||||
textView.textAlignment = View.TEXT_ALIGNMENT_CENTER
|
||||
}
|
||||
|
||||
fun Snackbar.show(isCentered: Boolean) {
|
||||
if (isCentered) {
|
||||
this.centerMessage()
|
||||
}
|
||||
|
||||
this.show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for [clickableText] inside the TextView's text property.
|
||||
* If found, highlights [clickableText] with [clickableTextColor] and sets [clickListener] to it.
|
||||
*
|
||||
* @param clickableText text that must be present inside TextView's text property
|
||||
* @param clickableTextColor color for highlighting [clickableText]
|
||||
* @param clickListener functions that will we invoked on [clickableText] click
|
||||
*/
|
||||
fun TextView.makeTextClickable(
|
||||
clickableText: String,
|
||||
clickableTextColor: Int,
|
||||
clickListener: () -> (Unit)
|
||||
) {
|
||||
this.movementMethod = LinkMovementMethod.getInstance()
|
||||
this.text =
|
||||
this.text.toString().makeTextClickable(clickableText, clickableTextColor, clickListener)
|
||||
}
|
||||
|
||||
fun TextView.makeSpannableTextClickable(
|
||||
clickableText: String,
|
||||
clickableTextColor: Int,
|
||||
clickListener: () -> (Unit)
|
||||
) {
|
||||
this.movementMethod = LinkMovementMethod.getInstance()
|
||||
this.text =
|
||||
this.text.toSpannable().makeTextClickable(clickableText, clickableTextColor, clickListener)
|
||||
}
|
||||
|
||||
fun View.removeFocus() {
|
||||
clearFocus()
|
||||
isFocusable = false
|
||||
isFocusableInTouchMode = false
|
||||
}
|
||||
|
||||
fun View.setFocus() {
|
||||
isFocusable = true
|
||||
isFocusableInTouchMode = true
|
||||
}
|
||||
|
||||
fun Boolean.asTextOrNullInputType() = if (this) EditorInfo.TYPE_CLASS_TEXT else EditorInfo.TYPE_NULL
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.github.droidworksstudio.ktx
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.ViewGroup
|
||||
import android.widget.RadioButton
|
||||
import android.widget.RadioGroup
|
||||
import androidx.appcompat.view.SupportMenuInflater
|
||||
import androidx.appcompat.widget.ActionMenuView
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import kotlin.math.min
|
||||
|
||||
fun ViewGroup.LayoutParams.calculateAspectRatio(
|
||||
maxWidth: Float,
|
||||
maxHeight: Float,
|
||||
imageWidth: Float,
|
||||
imageHeight: Float
|
||||
): ViewGroup.LayoutParams = apply {
|
||||
val widthRatio = maxWidth / imageWidth
|
||||
val heightRatio = maxHeight / imageHeight
|
||||
val bestRatio = min(widthRatio, heightRatio)
|
||||
|
||||
this.width = (imageWidth * bestRatio).toInt()
|
||||
this.height = (imageHeight * bestRatio).toInt()
|
||||
}
|
||||
|
||||
fun ViewGroup.LayoutParams.calculateProportionalHeight(
|
||||
maxWidth: Float,
|
||||
imageWidth: Float,
|
||||
imageHeight: Float
|
||||
): ViewGroup.LayoutParams = apply {
|
||||
val aspectRatio = maxWidth / imageWidth
|
||||
this.height = (imageHeight * aspectRatio).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches Live data to Radio Group.
|
||||
* This will update Live Data data with selected radio button text.
|
||||
*/
|
||||
fun RadioGroup.attachLiveDataForValue(data: MutableLiveData<String>) {
|
||||
data.value = getCheckedView()?.text.toString()
|
||||
setOnCheckedChangeListener { group, checkedId ->
|
||||
val checkedView = group.findViewById<RadioButton>(checkedId)
|
||||
data.value = checkedView.text.toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches Live data to Radio Group.
|
||||
* This will update Live Data data with selected radio button text.
|
||||
*/
|
||||
fun RadioGroup.attachLiveDataForId(data: MutableLiveData<Int>) {
|
||||
data.value = checkedRadioButtonId
|
||||
setOnCheckedChangeListener { _, checkedId ->
|
||||
data.value = checkedId
|
||||
}
|
||||
}
|
||||
|
||||
fun RadioGroup.getCheckedView() = findViewById<RadioButton>(checkedRadioButtonId) ?: null
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
fun ActionMenuView.inflateMenu(menuId: Int) = SupportMenuInflater(context).inflate(menuId, menu)
|
||||
@@ -1,53 +1,15 @@
|
||||
package com.github.droidworksstudio.launcher.helper
|
||||
|
||||
import android.app.SearchManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.LauncherApps
|
||||
import android.net.Uri
|
||||
import android.os.UserHandle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.EditText
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.github.droidworksstudio.ktx.openUrl
|
||||
import com.github.droidworksstudio.launcher.Constants
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
fun View.hideKeyboard() {
|
||||
this.clearFocus()
|
||||
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.hideSoftInputFromWindow(windowToken, 0)
|
||||
}
|
||||
|
||||
fun View.showKeyboard(show: Boolean = true) {
|
||||
if (show.not()) return
|
||||
if (this.requestFocus())
|
||||
this.postDelayed({
|
||||
this.findViewById<EditText>(androidx.appcompat.R.id.search_src_text).apply {
|
||||
isCursorVisible = false
|
||||
}
|
||||
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
@Suppress("DEPRECATION")
|
||||
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY)
|
||||
}, 100)
|
||||
}
|
||||
|
||||
fun Context.openSearch(query: String? = null) {
|
||||
val intent = Intent(Intent.ACTION_WEB_SEARCH)
|
||||
intent.putExtra(SearchManager.QUERY, query ?: "")
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun Context.openUrl(url: String) {
|
||||
if (url.isEmpty()) return
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.data = Uri.parse(url)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
fun Context.searchOnPlayStore(query: String? = null): Boolean {
|
||||
return try {
|
||||
val playStoreIntent = Intent(Intent.ACTION_VIEW)
|
||||
@@ -78,7 +40,8 @@ fun Context.searchCustomSearchEngine(searchQuery: String? = null): Boolean {
|
||||
}
|
||||
|
||||
fun Context.backupSharedPreferences(backupFileName: String) {
|
||||
val sharedPreferences: SharedPreferences = this.getSharedPreferences(Constants.PREFS_FILENAME, 0)
|
||||
val sharedPreferences: SharedPreferences =
|
||||
this.getSharedPreferences(Constants.PREFS_FILENAME, 0)
|
||||
val allPrefs = sharedPreferences.all
|
||||
val backupFile = File(filesDir, backupFileName)
|
||||
|
||||
@@ -116,7 +79,8 @@ fun Context.backupSharedPreferences(backupFileName: String) {
|
||||
}
|
||||
|
||||
fun Context.restoreSharedPreferences(backupFileName: String) {
|
||||
val sharedPreferences: SharedPreferences = this.getSharedPreferences(Constants.PREFS_FILENAME, 0)
|
||||
val sharedPreferences: SharedPreferences =
|
||||
this.getSharedPreferences(Constants.PREFS_FILENAME, 0)
|
||||
val editor = sharedPreferences.edit()
|
||||
val backupFile = File(filesDir, backupFileName)
|
||||
|
||||
@@ -127,7 +91,11 @@ fun Context.restoreSharedPreferences(backupFileName: String) {
|
||||
backupFile.forEachLine { line ->
|
||||
val (key, value) = line.split("=", limit = 2)
|
||||
when {
|
||||
value.toBooleanStrictOrNull() != null -> editor.putBoolean(key, value.toBoolean())
|
||||
value.toBooleanStrictOrNull() != null -> editor.putBoolean(
|
||||
key,
|
||||
value.toBoolean()
|
||||
)
|
||||
|
||||
value.toIntOrNull() != null -> editor.putInt(key, value.toInt())
|
||||
value.toFloatOrNull() != null -> editor.putFloat(key, value.toFloat())
|
||||
value.toLongOrNull() != null -> editor.putLong(key, value.toLong())
|
||||
@@ -145,20 +113,4 @@ fun Context.restoreSharedPreferences(backupFileName: String) {
|
||||
} else {
|
||||
println("Backup file does not exist.")
|
||||
}
|
||||
}
|
||||
|
||||
fun Fragment.restartApp() {
|
||||
val packageManager = requireContext().packageManager
|
||||
val intent = packageManager.getLaunchIntentForPackage(requireContext().packageName)
|
||||
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
if (intent != null) {
|
||||
startActivity(intent)
|
||||
}
|
||||
requireActivity().finish()
|
||||
}
|
||||
|
||||
fun Context.isPackageInstalled(packageName: String, userHandle: UserHandle = android.os.Process.myUserHandle()): Boolean {
|
||||
val launcher = getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
|
||||
val activityInfo = launcher.getActivityList(packageName, userHandle)
|
||||
return activityInfo.size > 0
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import android.view.WindowManager
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.widget.LinearLayoutCompat
|
||||
import com.github.droidworksstudio.ktx.showLongToast
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.github.droidworksstudio.launcher.Constants
|
||||
import com.github.droidworksstudio.launcher.R
|
||||
@@ -163,7 +164,7 @@ class AppHelper @Inject constructor() {
|
||||
if (intent != null) {
|
||||
context.startActivity(intent)
|
||||
} else {
|
||||
showToast(context, "Failed to open the application")
|
||||
context.showLongToast("Failed to open the application")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +209,7 @@ class AppHelper @Inject constructor() {
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
// Digital Wellbeing app is not installed or cannot be opened
|
||||
// Handle this case as needed
|
||||
showToast(context, "Digital Wellbeing is not available on this device.")
|
||||
context.showLongToast("Digital Wellbeing is not available on this device.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +221,7 @@ class AppHelper @Inject constructor() {
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
// Battery manager settings cannot be opened
|
||||
// Handle this case as needed
|
||||
showToast(context, "Battery manager settings are not available on this device.")
|
||||
context.showLongToast("Battery manager settings are not available on this device.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,11 +257,6 @@ class AppHelper @Inject constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun showToast(context: Context, message: String) {
|
||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
fun showStatusBar(window: Window) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
window.insetsController?.show(WindowInsets.Type.statusBars())
|
||||
|
||||
@@ -5,6 +5,7 @@ import androidx.biometric.BiometricPrompt
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.github.droidworksstudio.ktx.showLongToast
|
||||
import com.github.droidworksstudio.launcher.R
|
||||
import com.github.droidworksstudio.launcher.data.entities.AppInfo
|
||||
import javax.inject.Inject
|
||||
@@ -66,21 +67,16 @@ class FingerprintHelper @Inject constructor(private val fragment: Fragment) {
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
appHelper.showToast(
|
||||
fragment.requireContext(),
|
||||
fragment.getString(R.string.authentication_failed)
|
||||
)
|
||||
fragment.requireContext()
|
||||
.showLongToast(fragment.getString(R.string.authentication_failed))
|
||||
}
|
||||
|
||||
override fun onAuthenticationError(errorCode: Int, errorMessage: CharSequence) {
|
||||
when (errorCode) {
|
||||
BiometricPrompt.ERROR_USER_CANCELED -> appHelper.showToast(
|
||||
fragment.requireContext(),
|
||||
fragment.getString(R.string.authentication_cancel)
|
||||
)
|
||||
BiometricPrompt.ERROR_USER_CANCELED -> fragment.requireContext()
|
||||
.showLongToast(fragment.getString(R.string.authentication_cancel))
|
||||
|
||||
else -> appHelper.showToast(
|
||||
fragment.requireContext(),
|
||||
else -> fragment.requireContext().showLongToast(
|
||||
fragment.getString(R.string.authentication_error)
|
||||
.format(errorMessage, errorCode)
|
||||
)
|
||||
@@ -111,10 +107,8 @@ class FingerprintHelper @Inject constructor(private val fragment: Fragment) {
|
||||
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE,
|
||||
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> sendToTarget(runNavigation)
|
||||
|
||||
else -> appHelper.showToast(
|
||||
fragment.requireContext(),
|
||||
fragment.getString(R.string.authentication_failed)
|
||||
)
|
||||
else -> fragment.requireContext()
|
||||
.showLongToast(fragment.getString(R.string.authentication_failed))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,10 +116,8 @@ class FingerprintHelper @Inject constructor(private val fragment: Fragment) {
|
||||
try {
|
||||
fragment.findNavController().navigate(runNavigation)
|
||||
} catch (e: Exception) {
|
||||
appHelper.showToast(
|
||||
fragment.requireContext(),
|
||||
fragment.getString(R.string.authentication_failed)
|
||||
)
|
||||
fragment.requireContext()
|
||||
.showLongToast(fragment.getString(R.string.authentication_failed))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,16 @@ import android.app.admin.DeviceAdminReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.widget.Toast
|
||||
import com.github.droidworksstudio.ktx.showLongToast
|
||||
|
||||
class DeviceAdmin : DeviceAdminReceiver() {
|
||||
override fun onEnabled(context: Context, intent: Intent) {
|
||||
super.onEnabled(context, intent)
|
||||
Toast.makeText(context, "Enabled", Toast.LENGTH_SHORT).show()
|
||||
context.showLongToast("Enabled")
|
||||
}
|
||||
|
||||
override fun onDisabled(context: Context, intent: Intent) {
|
||||
super.onDisabled(context, intent)
|
||||
Toast.makeText(context, "Disabled", Toast.LENGTH_SHORT).show()
|
||||
context.showLongToast("Disabled")
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import android.view.ViewGroup
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.github.droidworksstudio.ktx.showLongToast
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.github.droidworksstudio.launcher.R
|
||||
import com.github.droidworksstudio.launcher.data.entities.AppInfo
|
||||
@@ -74,9 +75,12 @@ class AppInfoBottomSheetFragment(private val appInfo: AppInfo) : BottomSheetDial
|
||||
bottomDialogHelper.setupDialogStyle(dialog)
|
||||
|
||||
binding.run {
|
||||
bottomSheetFavHidden.text = getString(if (!appInfo.favorite) R.string.bottom_dialog_add_to_home else R.string.bottom_dialog_remove_from_home)
|
||||
bottomSheetHidden.text = getString(if (!appInfo.hidden) R.string.bottom_dialog_add_to_hidden else R.string.bottom_dialog_remove_to_hidden)
|
||||
bottomSheetLock.text = getString(if (!appInfo.lock) R.string.bottom_dialog_add_to_lock else R.string.bottom_dialog_remove_to_unlock)
|
||||
bottomSheetFavHidden.text =
|
||||
getString(if (!appInfo.favorite) R.string.bottom_dialog_add_to_home else R.string.bottom_dialog_remove_from_home)
|
||||
bottomSheetHidden.text =
|
||||
getString(if (!appInfo.hidden) R.string.bottom_dialog_add_to_hidden else R.string.bottom_dialog_remove_to_hidden)
|
||||
bottomSheetLock.text =
|
||||
getString(if (!appInfo.lock) R.string.bottom_dialog_add_to_lock else R.string.bottom_dialog_remove_to_unlock)
|
||||
bottomSheetRename.setText(appInfo.appName)
|
||||
bottomSheetOrder.text = appInfo.appOrder.toString()
|
||||
}
|
||||
@@ -121,7 +125,12 @@ class AppInfoBottomSheetFragment(private val appInfo: AppInfo) : BottomSheetDial
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
if (s.isNullOrEmpty()) {
|
||||
binding.bottomSheetRename.setHintTextColor(ContextCompat.getColor(requireContext(), R.color.white))
|
||||
binding.bottomSheetRename.setHintTextColor(
|
||||
ContextCompat.getColor(
|
||||
requireContext(),
|
||||
R.color.white
|
||||
)
|
||||
)
|
||||
binding.bottomSheetRename.hint = appName
|
||||
appInfo.appName = appName ?: ""
|
||||
} else {
|
||||
@@ -149,8 +158,7 @@ class AppInfoBottomSheetFragment(private val appInfo: AppInfo) : BottomSheetDial
|
||||
binding.bottomSheetLock.setOnClickListener {
|
||||
if (appInfo.lock) {
|
||||
fingerHelper.startFingerprintAuth(appInfo, this)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
appInfo.lock = true
|
||||
viewModel.updateAppLock(appInfo, appInfo.lock)
|
||||
dismiss()
|
||||
@@ -175,14 +183,19 @@ class AppInfoBottomSheetFragment(private val appInfo: AppInfo) : BottomSheetDial
|
||||
viewModel.updateAppLock(appInfo, appInfo.lock)
|
||||
dismiss()
|
||||
|
||||
appHelper.showToast(requireContext(), getString(R.string.authentication_succeeded))
|
||||
requireContext().showLongToast(getString(R.string.authentication_succeeded))
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
appHelper.showToast(requireContext(), getString(R.string.authentication_failed))
|
||||
requireContext().showLongToast(getString(R.string.authentication_failed))
|
||||
}
|
||||
|
||||
override fun onAuthenticationError(errorCode: Int, errorMessage: CharSequence?) {
|
||||
appHelper.showToast(requireContext(), getString(R.string.authentication_error))
|
||||
requireContext().showLongToast(
|
||||
getString(R.string.authentication_error).format(
|
||||
errorMessage,
|
||||
errorCode
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,9 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.github.droidworksstudio.ktx.showLongToast
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.github.droidworksstudio.launcher.databinding.BottomsheetdialogColorSettingsBinding
|
||||
import com.github.droidworksstudio.launcher.helper.BottomDialogHelper
|
||||
@@ -52,7 +52,7 @@ class ColorBottomSheetDialogFragment : BottomSheetDialogFragment() {
|
||||
observeClickListener()
|
||||
}
|
||||
|
||||
private fun initView(){
|
||||
private fun initView() {
|
||||
bottomDialogHelper.setupDialogStyle(dialog)
|
||||
|
||||
binding.selectDateTextColor.apply {
|
||||
@@ -82,7 +82,7 @@ class ColorBottomSheetDialogFragment : BottomSheetDialogFragment() {
|
||||
}
|
||||
|
||||
|
||||
private fun observeClickListener(){
|
||||
private fun observeClickListener() {
|
||||
binding.bottomColorDateView.setOnClickListener {
|
||||
showColorPickerDialog(
|
||||
binding.selectDateTextColor,
|
||||
@@ -165,7 +165,7 @@ class ColorBottomSheetDialogFragment : BottomSheetDialogFragment() {
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Toast.makeText(context, "onCancel", Toast.LENGTH_SHORT).show()
|
||||
context?.showLongToast("onCancel")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
@@ -15,17 +14,18 @@ import androidx.lifecycle.coroutineScope
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||
import com.github.droidworksstudio.ktx.hideKeyboard
|
||||
import com.github.droidworksstudio.ktx.openSearch
|
||||
import com.github.droidworksstudio.ktx.showKeyboard
|
||||
import com.github.droidworksstudio.ktx.showLongToast
|
||||
import com.github.droidworksstudio.launcher.R
|
||||
import com.github.droidworksstudio.launcher.data.entities.AppInfo
|
||||
import com.github.droidworksstudio.launcher.databinding.FragmentDrawBinding
|
||||
import com.github.droidworksstudio.launcher.helper.AppHelper
|
||||
import com.github.droidworksstudio.launcher.helper.FingerprintHelper
|
||||
import com.github.droidworksstudio.launcher.helper.PreferenceHelper
|
||||
import com.github.droidworksstudio.launcher.helper.hideKeyboard
|
||||
import com.github.droidworksstudio.launcher.helper.openSearch
|
||||
import com.github.droidworksstudio.launcher.helper.searchCustomSearchEngine
|
||||
import com.github.droidworksstudio.launcher.helper.searchOnPlayStore
|
||||
import com.github.droidworksstudio.launcher.helper.showKeyboard
|
||||
import com.github.droidworksstudio.launcher.listener.OnItemClickedListener
|
||||
import com.github.droidworksstudio.launcher.listener.OnSwipeTouchListener
|
||||
import com.github.droidworksstudio.launcher.listener.ScrollEventListener
|
||||
@@ -244,19 +244,21 @@ class DrawFragment : Fragment(),
|
||||
}
|
||||
|
||||
override fun onAuthenticationSucceeded(appInfo: AppInfo) {
|
||||
Toast.makeText(context, getString(R.string.authentication_succeeded), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
context.showLongToast(getString(R.string.authentication_succeeded))
|
||||
appHelper.launchApp(context, appInfo)
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
Toast.makeText(context, getString(R.string.authentication_failed), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
context.showLongToast(getString(R.string.authentication_failed))
|
||||
}
|
||||
|
||||
override fun onAuthenticationError(errorCode: Int, errorMessage: CharSequence?) {
|
||||
Toast.makeText(context, getString(R.string.authentication_error), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
context.showLongToast(
|
||||
getString(R.string.authentication_error).format(
|
||||
errorMessage,
|
||||
errorCode
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
@@ -17,6 +16,7 @@ import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||
import com.github.droidworksstudio.ktx.showLongToast
|
||||
import com.github.droidworksstudio.launcher.R
|
||||
import com.github.droidworksstudio.launcher.data.entities.AppInfo
|
||||
import com.github.droidworksstudio.launcher.databinding.FragmentFavoriteBinding
|
||||
@@ -214,19 +214,21 @@ class FavoriteFragment : Fragment(),
|
||||
}
|
||||
|
||||
override fun onAuthenticationSucceeded(appInfo: AppInfo) {
|
||||
Toast.makeText(context, getString(R.string.authentication_succeeded), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
context.showLongToast(getString(R.string.authentication_succeeded))
|
||||
appHelper.launchApp(context, appInfo)
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
Toast.makeText(context, getString(R.string.authentication_failed), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
context.showLongToast(getString(R.string.authentication_failed))
|
||||
}
|
||||
|
||||
override fun onAuthenticationError(errorCode: Int, errorMessage: CharSequence?) {
|
||||
Toast.makeText(context, getString(R.string.authentication_error), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
context.showLongToast(
|
||||
getString(R.string.authentication_error).format(
|
||||
errorMessage,
|
||||
errorCode
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onViewMoved(oldPosition: Int, newPosition: Int): Boolean {
|
||||
|
||||
@@ -7,13 +7,13 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||
import com.github.droidworksstudio.ktx.showLongToast
|
||||
import com.github.droidworksstudio.launcher.R
|
||||
import com.github.droidworksstudio.launcher.data.entities.AppInfo
|
||||
import com.github.droidworksstudio.launcher.databinding.FragmentHiddenBinding
|
||||
@@ -155,18 +155,20 @@ class HiddenFragment : Fragment(),
|
||||
}
|
||||
|
||||
override fun onAuthenticationSucceeded(appInfo: AppInfo) {
|
||||
Toast.makeText(context, getString(R.string.authentication_succeeded), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
context.showLongToast(getString(R.string.authentication_succeeded))
|
||||
appHelper.launchApp(context, appInfo)
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
Toast.makeText(context, getString(R.string.authentication_failed), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
context.showLongToast(getString(R.string.authentication_failed))
|
||||
}
|
||||
|
||||
override fun onAuthenticationError(errorCode: Int, errorMessage: CharSequence?) {
|
||||
Toast.makeText(context, getString(R.string.authentication_error), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
context.showLongToast(
|
||||
getString(R.string.authentication_error).format(
|
||||
errorMessage,
|
||||
errorCode
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||
import com.github.droidworksstudio.ktx.hideKeyboard
|
||||
import com.github.droidworksstudio.ktx.showLongToast
|
||||
import com.github.droidworksstudio.launcher.R
|
||||
import com.github.droidworksstudio.launcher.accessibility.ActionService
|
||||
import com.github.droidworksstudio.launcher.data.entities.AppInfo
|
||||
@@ -26,7 +28,6 @@ import com.github.droidworksstudio.launcher.databinding.FragmentHomeBinding
|
||||
import com.github.droidworksstudio.launcher.helper.AppHelper
|
||||
import com.github.droidworksstudio.launcher.helper.FingerprintHelper
|
||||
import com.github.droidworksstudio.launcher.helper.PreferenceHelper
|
||||
import com.github.droidworksstudio.launcher.helper.hideKeyboard
|
||||
import com.github.droidworksstudio.launcher.listener.OnItemClickedListener
|
||||
import com.github.droidworksstudio.launcher.listener.OnSwipeTouchListener
|
||||
import com.github.droidworksstudio.launcher.listener.ScrollEventListener
|
||||
@@ -295,13 +296,11 @@ class HomeFragment : Fragment(),
|
||||
errString: CharSequence
|
||||
) {
|
||||
when (errorCode) {
|
||||
BiometricPrompt.ERROR_USER_CANCELED -> appHelper.showToast(
|
||||
requireContext(),
|
||||
BiometricPrompt.ERROR_USER_CANCELED -> requireContext().showLongToast(
|
||||
getString(R.string.authentication_cancel)
|
||||
)
|
||||
|
||||
else -> appHelper.showToast(
|
||||
requireContext(),
|
||||
else -> requireContext().showLongToast(
|
||||
getString(R.string.authentication_error).format(
|
||||
errString,
|
||||
errorCode
|
||||
@@ -315,10 +314,7 @@ class HomeFragment : Fragment(),
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
appHelper.showToast(
|
||||
requireContext(),
|
||||
getString(R.string.authentication_failed)
|
||||
)
|
||||
requireContext().showLongToast(getString(R.string.authentication_failed))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -364,23 +360,22 @@ class HomeFragment : Fragment(),
|
||||
|
||||
override fun onAuthenticationSucceeded(appInfo: AppInfo) {
|
||||
appHelper.launchApp(context, appInfo)
|
||||
appHelper.showToast(context, getString(R.string.authentication_succeeded))
|
||||
requireContext().showLongToast(getString(R.string.authentication_succeeded))
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
appHelper.showToast(context, getString(R.string.authentication_failed))
|
||||
requireContext().showLongToast(getString(R.string.authentication_failed))
|
||||
}
|
||||
|
||||
override fun onAuthenticationError(errorCode: Int, errorMessage: CharSequence?) {
|
||||
when (errorCode) {
|
||||
BiometricPrompt.ERROR_USER_CANCELED -> appHelper.showToast(
|
||||
requireContext(),
|
||||
getString(R.string.authentication_cancel)
|
||||
)
|
||||
BiometricPrompt.ERROR_USER_CANCELED -> requireContext().showLongToast(getString(R.string.authentication_cancel))
|
||||
|
||||
else -> appHelper.showToast(
|
||||
requireContext(),
|
||||
getString(R.string.authentication_error).format(errorMessage, errorCode)
|
||||
else -> requireContext().showLongToast(
|
||||
getString(R.string.authentication_error).format(
|
||||
errorMessage,
|
||||
errorCode
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,16 +7,16 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.github.droidworksstudio.ktx.restartApp
|
||||
import com.github.droidworksstudio.ktx.showLongToast
|
||||
import com.github.droidworksstudio.launcher.R
|
||||
import com.github.droidworksstudio.launcher.databinding.FragmentSettingsBinding
|
||||
import com.github.droidworksstudio.launcher.helper.AppHelper
|
||||
import com.github.droidworksstudio.launcher.helper.PreferenceHelper
|
||||
import com.github.droidworksstudio.launcher.helper.restartApp
|
||||
import com.github.droidworksstudio.launcher.listener.OnSwipeTouchListener
|
||||
import com.github.droidworksstudio.launcher.listener.ScrollEventListener
|
||||
import com.github.droidworksstudio.launcher.ui.bottomsheetdialog.AlignmentBottomSheetDialogFragment
|
||||
@@ -154,20 +154,12 @@ class SettingsFragment : Fragment(),
|
||||
|
||||
binding.backupView.setOnClickListener {
|
||||
appHelper.backupSharedPreferences(requireContext())
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
getString(R.string.settings_reload_app_backup),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
context.showLongToast(getString(R.string.settings_reload_app_backup))
|
||||
}
|
||||
|
||||
binding.restoreView.setOnClickListener {
|
||||
appHelper.restoreSharedPreferences(requireContext())
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
getString(R.string.settings_reload_app_restore),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
context.showLongToast(getString(R.string.settings_reload_app_restore))
|
||||
restartApp()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user