mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-04 16:18:55 +01:00
Upstream merge
Internal permission change Fix url adder
This commit is contained in:
60
app/src/main/java/exh/ui/lock/LockActivity.kt
Executable file
60
app/src/main/java/exh/ui/lock/LockActivity.kt
Executable file
@@ -0,0 +1,60 @@
|
||||
package exh.ui.lock
|
||||
|
||||
import android.os.Bundle
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.andrognito.pinlockview.PinLockListener
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||
import kotlinx.android.synthetic.main.activity_lock.*
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class LockActivity : BaseActivity() {
|
||||
|
||||
val prefs: PreferencesHelper by injectLazy()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
disableLock = true
|
||||
|
||||
setTheme(R.style.Theme_Tachiyomi_Dark)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if(!lockEnabled(prefs)) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_lock)
|
||||
|
||||
pin_lock_view.attachIndicatorDots(indicator_dots)
|
||||
|
||||
pin_lock_view.pinLength = prefs.lockLength().getOrDefault()
|
||||
pin_lock_view.setPinLockListener(object : PinLockListener {
|
||||
override fun onEmpty() {}
|
||||
|
||||
override fun onComplete(pin: String) {
|
||||
if(sha512(pin, prefs.lockSalt().get()!!) == prefs.lockHash().get()) {
|
||||
//Yay!
|
||||
finish()
|
||||
} else {
|
||||
MaterialDialog.Builder(this@LockActivity)
|
||||
.title("PIN code incorrect")
|
||||
.content("The PIN code you entered is incorrect. Please try again.")
|
||||
.cancelable(true)
|
||||
.canceledOnTouchOutside(true)
|
||||
.positiveText("Ok")
|
||||
.autoDismiss(true)
|
||||
.show()
|
||||
pin_lock_view.resetPinLockView()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPinChange(pinLength: Int, intermediatePin: String?) {}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
moveTaskToBack(true)
|
||||
}
|
||||
}
|
||||
85
app/src/main/java/exh/ui/lock/LockPreference.kt
Executable file
85
app/src/main/java/exh/ui/lock/LockPreference.kt
Executable file
@@ -0,0 +1,85 @@
|
||||
package exh.ui.lock
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v7.preference.Preference
|
||||
import android.text.InputType
|
||||
import android.util.AttributeSet
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.math.BigInteger
|
||||
import java.security.SecureRandom
|
||||
|
||||
class LockPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
Preference(context, attrs) {
|
||||
|
||||
val secureRandom by lazy { SecureRandom() }
|
||||
|
||||
val prefs: PreferencesHelper by injectLazy()
|
||||
|
||||
override fun onAttached() {
|
||||
super.onAttached()
|
||||
updateSummary()
|
||||
}
|
||||
|
||||
fun updateSummary() {
|
||||
if(lockEnabled(prefs)) {
|
||||
summary = "Application is locked"
|
||||
} else {
|
||||
summary = "Application is not locked, tap to lock"
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick() {
|
||||
super.onClick()
|
||||
if(!notifyLockSecurity(context)) {
|
||||
MaterialDialog.Builder(context)
|
||||
.title("Lock application")
|
||||
.content("Enter a pin to lock the application. Enter nothing to disable the pin lock.")
|
||||
.inputRangeRes(0, 10, R.color.material_red_500)
|
||||
.inputType(InputType.TYPE_CLASS_NUMBER)
|
||||
.input("", "", { _, c ->
|
||||
val progressDialog = MaterialDialog.Builder(context)
|
||||
.title("Saving password")
|
||||
.progress(true, 0)
|
||||
.cancelable(false)
|
||||
.show()
|
||||
Observable.fromCallable {
|
||||
savePassword(c.toString())
|
||||
}.subscribeOn(Schedulers.computation())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
progressDialog.dismiss()
|
||||
updateSummary()
|
||||
}
|
||||
})
|
||||
.negativeText("Cancel")
|
||||
.autoDismiss(true)
|
||||
.cancelable(true)
|
||||
.canceledOnTouchOutside(true)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
fun savePassword(password: String) {
|
||||
val salt: String?
|
||||
val hash: String?
|
||||
val length: Int
|
||||
if(password.isEmpty()) {
|
||||
salt = null
|
||||
hash = null
|
||||
length = -1
|
||||
} else {
|
||||
salt = BigInteger(130, secureRandom).toString(32)
|
||||
hash = sha512(password, salt)
|
||||
length = password.length
|
||||
}
|
||||
prefs.lockSalt().set(salt)
|
||||
prefs.lockHash().set(hash)
|
||||
prefs.lockLength().set(length)
|
||||
}
|
||||
}
|
||||
91
app/src/main/java/exh/ui/lock/LockUtils.kt
Executable file
91
app/src/main/java/exh/ui/lock/LockUtils.kt
Executable file
@@ -0,0 +1,91 @@
|
||||
package exh.ui.lock
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Activity
|
||||
import android.app.AppOpsManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.security.MessageDigest
|
||||
import kotlin.experimental.and
|
||||
|
||||
|
||||
/**
|
||||
* Password hashing utils
|
||||
*/
|
||||
|
||||
/**
|
||||
* Yes, I know SHA512 is fast, but bcrypt on mobile devices is too slow apparently
|
||||
*/
|
||||
fun sha512(passwordToHash: String, salt: String): String {
|
||||
val md = MessageDigest.getInstance("SHA-512")
|
||||
md.update(salt.toByteArray(charset("UTF-8")))
|
||||
val bytes = md.digest(passwordToHash.toByteArray(charset("UTF-8")))
|
||||
val sb = StringBuilder()
|
||||
for (i in bytes.indices) {
|
||||
sb.append(Integer.toString((bytes[i] and 0xff.toByte()) + 0x100, 16).substring(1))
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if lock is enabled
|
||||
*/
|
||||
fun lockEnabled(prefs: PreferencesHelper = Injekt.get())
|
||||
= prefs.lockHash().get() != null
|
||||
&& prefs.lockSalt().get() != null
|
||||
&& prefs.lockLength().getOrDefault() != -1
|
||||
|
||||
/**
|
||||
* Lock the screen
|
||||
*/
|
||||
fun showLockActivity(activity: Activity) {
|
||||
activity.startActivity(Intent(activity, LockActivity::class.java))
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the lock will function properly
|
||||
*
|
||||
* @return true if action is required, false if lock is working properly
|
||||
*/
|
||||
fun notifyLockSecurity(context: Context): Boolean {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !hasAccessToUsageStats(context)) {
|
||||
MaterialDialog.Builder(context)
|
||||
.title("Permission required")
|
||||
.content("${context.getString(R.string.app_name)} requires the usage stats permission to detect when you leave the app. " +
|
||||
"This is required for the application lock to function properly. " +
|
||||
"Press OK to grant this permission now.")
|
||||
.negativeText("Cancel")
|
||||
.positiveText("Ok")
|
||||
.onPositive { _, _ ->
|
||||
context.startActivity(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS))
|
||||
}
|
||||
.autoDismiss(true)
|
||||
.cancelable(false)
|
||||
.show()
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
fun hasAccessToUsageStats(context: Context): Boolean {
|
||||
try {
|
||||
val packageManager = context.packageManager
|
||||
val applicationInfo = packageManager.getApplicationInfo(context.packageName, 0)
|
||||
val appOpsManager = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
|
||||
val mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName)
|
||||
return (mode == AppOpsManager.MODE_ALLOWED)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user