mirror of
https://github.com/mihonapp/mihon.git
synced 2025-03-13 08:10:07 +01:00
Migrate to Tachiyomi 6.1
Rewrite batch add UI Rewrite lock UI
This commit is contained in:
parent
3da7c47bf5
commit
32d02f9329
@ -42,8 +42,8 @@ android {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 25
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
versionCode 5003
|
||||
versionName "v5.0.3-EH"
|
||||
versionCode 6101
|
||||
versionName "v6.1.1-EH"
|
||||
|
||||
buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\""
|
||||
buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\""
|
||||
@ -214,7 +214,7 @@ dependencies {
|
||||
releaseCompile "com.google.firebase:firebase-crash:$firebase_version"
|
||||
|
||||
//SnappyDB (EH)
|
||||
compile 'io.paperdb:paperdb:2.0'
|
||||
compile 'io.paperdb:paperdb:2.1'
|
||||
|
||||
//JVE (Regex) (EH)
|
||||
compile 'ru.lanwen.verbalregex:java-verbal-expressions:1.4'
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="eu.kanade.tachiyomi">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
@ -106,8 +107,8 @@
|
||||
<!-- EH -->
|
||||
<activity
|
||||
android:name="exh.ui.login.LoginActivity"
|
||||
android:label="@string/label_login"
|
||||
android:parentActivityName=".ui.setting.SettingsActivity" >
|
||||
android:label="@string/label_login">
|
||||
<!-- TODO parent activity -->
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
@ -145,17 +146,8 @@
|
||||
android:scheme="https"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="exh.ui.migration.MigrationCompletionActivity"
|
||||
android:label="Complete migration">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="application/json"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="exh.ui.lock.LockActivity"
|
||||
<activity android:name="exh.ui.lock.LockController"
|
||||
android:label="Application locked"/>
|
||||
|
||||
</application>
|
||||
|
@ -67,7 +67,7 @@ class NHentai(context: Context) : HttpSource() {
|
||||
= parseResultPage(response)
|
||||
|
||||
override fun mangaDetailsParse(response: Response)
|
||||
= parseGallery(jsonParser.parse(response.body().string()).asJsonObject)
|
||||
= parseGallery(jsonParser.parse(response.body()!!.string()).asJsonObject)
|
||||
|
||||
//Used so we can use a different URL for fetching manga details and opening the details in the browser
|
||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||
@ -85,7 +85,7 @@ class NHentai(context: Context) : HttpSource() {
|
||||
= nhGet(baseUrl + "/api/gallery/" + url.split("/").last())
|
||||
|
||||
fun parseResultPage(response: Response): MangasPage {
|
||||
val res = jsonParser.parse(response.body().string()).asJsonObject
|
||||
val res = jsonParser.parse(response.body()!!.string()).asJsonObject
|
||||
|
||||
val error = res.get("error")
|
||||
if(error == null) {
|
||||
@ -154,7 +154,7 @@ class NHentai(context: Context) : HttpSource() {
|
||||
?: client.newCall(urlToDetailsRequest(url))
|
||||
.asObservableSuccess()
|
||||
.map {
|
||||
rawParseGallery(jsonParser.parse(it.body().string()).asJsonObject)
|
||||
rawParseGallery(jsonParser.parse(it.body()!!.string()).asJsonObject)
|
||||
}.toBlocking().first()
|
||||
}!!
|
||||
|
||||
|
@ -4,54 +4,8 @@ import android.support.v7.app.AppCompatActivity
|
||||
import eu.kanade.tachiyomi.util.LocaleHelper
|
||||
|
||||
abstract class BaseActivity : AppCompatActivity() {
|
||||
|
||||
init {
|
||||
@Suppress("LeakingThis")
|
||||
LocaleHelper.updateConfiguration(this)
|
||||
}
|
||||
|
||||
var willLock = false
|
||||
var disableLock = false
|
||||
override fun onRestart() {
|
||||
super.onRestart()
|
||||
if(willLock && lockEnabled() && !disableLock) {
|
||||
showLockActivity(this)
|
||||
}
|
||||
|
||||
willLock = false
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
tryLock()
|
||||
}
|
||||
|
||||
fun tryLock() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
val mUsageStatsManager = getSystemService("usagestats") as UsageStatsManager
|
||||
val time = System.currentTimeMillis()
|
||||
// We get usage stats for the last 20 seconds
|
||||
val stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 20, time)
|
||||
// Sort the stats by the last time used
|
||||
if (stats != null) {
|
||||
val mySortedMap = TreeMap<Long, UsageStats>()
|
||||
for (usageStats in stats) {
|
||||
mySortedMap.put(usageStats.lastTimeUsed, usageStats)
|
||||
}
|
||||
if (!mySortedMap.isEmpty()) {
|
||||
if(mySortedMap[mySortedMap.lastKey()]?.packageName != packageName) {
|
||||
willLock = true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val am = getSystemService(Service.ACTIVITY_SERVICE) as ActivityManager
|
||||
val tasks: List<ActivityManager.RunningTaskInfo>
|
||||
tasks = am.getRunningTasks(1)
|
||||
val running = tasks[0]
|
||||
if (running.topActivity.packageName != packageName) {
|
||||
willLock = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,12 +16,6 @@ class LibraryCategoryAdapter(view: LibraryCategoryView) :
|
||||
*/
|
||||
private var mangas: List<LibraryItem> = emptyList()
|
||||
|
||||
//EH
|
||||
private val sourceManager: SourceManager by injectLazy()
|
||||
|
||||
private val searchEngine = SearchEngine()
|
||||
private val metadataHelper = MetadataHelper()
|
||||
|
||||
var asyncSearchText: String? = null
|
||||
|
||||
/**
|
||||
@ -36,49 +30,6 @@ class LibraryCategoryAdapter(view: LibraryCategoryView) :
|
||||
performFilter()
|
||||
}
|
||||
|
||||
// --> EH
|
||||
/**
|
||||
* Filters the list of manga applying [filterObject] for each element.
|
||||
*
|
||||
* @param param the filter. Not used.
|
||||
*/
|
||||
override fun updateDataSet(param: String?) {
|
||||
//Async search filter (EH)
|
||||
val filtered = asyncSearchText?.let { search ->
|
||||
mangas.filter {
|
||||
filterObject(it, search)
|
||||
}
|
||||
} ?: mangas
|
||||
//The rest of the filters run on the main loop
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
filterItems(filtered)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a manga depending on a query.
|
||||
*
|
||||
* @param manga the manga to filter.
|
||||
* @param query the query to apply.
|
||||
* @return true if the manga should be included, false otherwise.
|
||||
*/
|
||||
override fun filterObject(manga: Manga, query: String): Boolean = with(manga) {
|
||||
if(!isLewdSource(manga.source)) {
|
||||
//Regular searching for normal manga
|
||||
title.toLowerCase().contains(query) ||
|
||||
author != null && author!!.toLowerCase().contains(query)
|
||||
} else {
|
||||
//Use gallery search engine for EH manga
|
||||
val metadata = metadataHelper.fetchMetadata(manga.url, manga.source)
|
||||
metadata?.let {
|
||||
searchEngine.matches(it, searchEngine.parseQuery(query))
|
||||
} ?: title.contains(query, ignoreCase = true) //Use regular searching when the metadata is not set up for this gallery
|
||||
}
|
||||
}
|
||||
|
||||
// <-- EH
|
||||
|
||||
/**
|
||||
* Returns the position in the adapter for the given manga.
|
||||
*
|
||||
@ -89,7 +40,9 @@ class LibraryCategoryAdapter(view: LibraryCategoryView) :
|
||||
}
|
||||
|
||||
fun performFilter() {
|
||||
updateDataSet(mangas.filter { it.filter(searchText) })
|
||||
updateDataSet(mangas.filter {
|
||||
it.filter(searchText)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -102,19 +102,6 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
fun onBind(category: Category) {
|
||||
this.category = category
|
||||
|
||||
//TODO Fix
|
||||
// --> EH
|
||||
val presenter = fragment.presenter
|
||||
|
||||
searchSubscription = presenter
|
||||
.searchSubject
|
||||
.debounce(10L, TimeUnit.MILLISECONDS)
|
||||
.subscribe { text -> //Debounce search (EH)
|
||||
adapter.asyncSearchText = text?.trim()?.toLowerCase()
|
||||
adapter.updateDataSet()
|
||||
}
|
||||
// <-- EH
|
||||
|
||||
adapter.mode = if (controller.selectedMangas.isNotEmpty()) {
|
||||
FlexibleAdapter.MODE_MULTI
|
||||
} else {
|
||||
|
@ -12,10 +12,18 @@ import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.util.inflate
|
||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||
import exh.isLewdSource
|
||||
import exh.metadata.MetadataHelper
|
||||
import exh.search.SearchEngine
|
||||
import kotlinx.android.synthetic.main.catalogue_grid_item.view.*
|
||||
|
||||
class LibraryItem(val manga: Manga) : AbstractFlexibleItem<LibraryHolder>(), IFilterable {
|
||||
|
||||
// --> EH
|
||||
private val searchEngine = SearchEngine()
|
||||
private val metadataHelper = MetadataHelper()
|
||||
// <-- EH
|
||||
|
||||
override fun getLayoutRes(): Int {
|
||||
return R.layout.catalogue_grid_item
|
||||
}
|
||||
@ -53,6 +61,15 @@ class LibraryItem(val manga: Manga) : AbstractFlexibleItem<LibraryHolder>(), IFi
|
||||
* @return true if the manga should be included, false otherwise.
|
||||
*/
|
||||
override fun filter(constraint: String): Boolean {
|
||||
// --> EH
|
||||
if(!isLewdSource(manga.source)) {
|
||||
//Use gallery search engine for EH manga
|
||||
metadataHelper.fetchMetadata(manga.url, manga.source)?.let {
|
||||
return searchEngine.matches(it, searchEngine.parseQuery(constraint))
|
||||
}
|
||||
}
|
||||
// <-- EH
|
||||
|
||||
return manga.title.contains(constraint, true) ||
|
||||
(manga.author?.contains(constraint, true) ?: false)
|
||||
}
|
||||
|
@ -1,8 +1,13 @@
|
||||
package eu.kanade.tachiyomi.ui.main
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.app.ActivityManager
|
||||
import android.app.Service
|
||||
import android.app.usage.UsageStats
|
||||
import android.app.usage.UsageStatsManager
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.support.v4.view.GravityCompat
|
||||
import android.support.v4.widget.DrawerLayout
|
||||
@ -10,6 +15,8 @@ import android.support.v7.graphics.drawable.DrawerArrowDrawable
|
||||
import android.view.ViewGroup
|
||||
import com.bluelinelabs.conductor.*
|
||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
|
||||
import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler
|
||||
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler
|
||||
import eu.kanade.tachiyomi.Migrations
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
@ -26,8 +33,14 @@ import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController
|
||||
import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsMainController
|
||||
import exh.ui.batchadd.BatchAddController
|
||||
import exh.ui.lock.LockChangeHandler
|
||||
import exh.ui.lock.LockController
|
||||
import exh.ui.lock.lockEnabled
|
||||
import exh.ui.lock.notifyLockSecurity
|
||||
import kotlinx.android.synthetic.main.main_activity.*
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.*
|
||||
|
||||
|
||||
class MainActivity : BaseActivity() {
|
||||
@ -86,9 +99,8 @@ class MainActivity : BaseActivity() {
|
||||
R.id.nav_drawer_recently_read -> setRoot(RecentlyReadController(), id)
|
||||
R.id.nav_drawer_catalogues -> setRoot(CatalogueController(), id)
|
||||
R.id.nav_drawer_latest_updates -> setRoot(LatestUpdatesController(), id)
|
||||
//TODO
|
||||
// --> EH
|
||||
R.id.nav_drawer_batch_add -> setFragment(BatchAddFragment.newInstance(), id)
|
||||
R.id.nav_drawer_batch_add -> setRoot(BatchAddController(), id)
|
||||
// <-- EH
|
||||
R.id.nav_drawer_downloads -> {
|
||||
router.pushController(RouterTransaction.with(DownloadController())
|
||||
@ -137,6 +149,17 @@ class MainActivity : BaseActivity() {
|
||||
|
||||
})
|
||||
|
||||
//Show lock
|
||||
if (savedInstanceState == null) {
|
||||
val lockEnabled = lockEnabled(preferences)
|
||||
if (lockEnabled) {
|
||||
doLock()
|
||||
|
||||
//Check lock security
|
||||
notifyLockSecurity(this)
|
||||
}
|
||||
}
|
||||
|
||||
syncActivityViewWithController(router.backstack.lastOrNull()?.controller())
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
@ -144,15 +167,6 @@ class MainActivity : BaseActivity() {
|
||||
if (Migrations.upgrade(preferences)) {
|
||||
ChangelogDialogController().showDialog(router)
|
||||
}
|
||||
|
||||
//Show lock
|
||||
val lockEnabled = lockEnabled(preferences)
|
||||
if(lockEnabled) {
|
||||
showLockActivity(this)
|
||||
|
||||
//Check lock security
|
||||
notifyLockSecurity(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -253,6 +267,54 @@ class MainActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
// --> EH
|
||||
//Lock code
|
||||
var willLock = false
|
||||
var disableLock = false
|
||||
override fun onRestart() {
|
||||
super.onRestart()
|
||||
if(willLock && lockEnabled() && !disableLock) {
|
||||
doLock()
|
||||
}
|
||||
|
||||
willLock = false
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
tryLock()
|
||||
}
|
||||
|
||||
fun tryLock() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
val mUsageStatsManager = getSystemService("usagestats") as UsageStatsManager
|
||||
val time = System.currentTimeMillis()
|
||||
// We get usage stats for the last 20 seconds
|
||||
val sortedStats =
|
||||
mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
|
||||
time - 1000 * 20,
|
||||
time)
|
||||
?.associateBy {
|
||||
it.lastTimeUsed
|
||||
}?.toSortedMap()
|
||||
if(sortedStats != null && sortedStats.isNotEmpty())
|
||||
if(sortedStats[sortedStats.lastKey()]?.packageName != packageName)
|
||||
willLock = true
|
||||
} else {
|
||||
val am = getSystemService(Service.ACTIVITY_SERVICE) as ActivityManager
|
||||
val running = am.getRunningTasks(1)[0]
|
||||
if (running.topActivity.packageName != packageName) {
|
||||
willLock = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun doLock() {
|
||||
router.pushController(RouterTransaction.with(LockController())
|
||||
.popChangeHandler(LockChangeHandler()))
|
||||
}
|
||||
// <-- EH
|
||||
|
||||
companion object {
|
||||
// Shortcut actions
|
||||
const val SHORTCUT_LIBRARY = "eu.kanade.tachiyomi.SHOW_LIBRARY"
|
||||
|
@ -1,19 +1,15 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.support.v7.preference.PreferenceScreen
|
||||
import android.view.View
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import exh.ui.migration.MetadataFetchDialog
|
||||
import exh.ui.login.LoginActivity
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
/**
|
||||
* EH Settings fragment
|
||||
*/
|
||||
|
||||
class SettingsEhFragment : SettingsController() {
|
||||
class SettingsEhController : SettingsController() {
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) {
|
||||
title = "E-Hentai"
|
||||
|
||||
@ -27,13 +23,24 @@ class SettingsEhFragment : SettingsController() {
|
||||
.asObservable().subscribeUntilDestroy {
|
||||
isChecked = it
|
||||
}
|
||||
|
||||
onChange { newVal ->
|
||||
newVal as Boolean
|
||||
if(!newVal) {
|
||||
preferences.enableExhentai().set(false)
|
||||
true
|
||||
} else {
|
||||
startActivity(Intent(context, LoginActivity::class.java))
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
title = "Use Hentai@Home Network"
|
||||
summary = "Do you wish to load images through the Hentai@Home Network? Disabling this option will reduce the amount of pages you are able to view"
|
||||
key = "enable_hah"
|
||||
defaultValue = "true"
|
||||
defaultValue = true
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
@ -41,11 +48,11 @@ class SettingsEhFragment : SettingsController() {
|
||||
summaryOn = "Currently showing Japanese titles in search results. Clear the chapter cache after changing this (in the Advanced section)"
|
||||
summaryOff = "Currently showing English/Romanized titles in search results. Clear the chapter cache after changing this (in the Advanced section)"
|
||||
key = "use_jp_title"
|
||||
defaultValue = "false"
|
||||
defaultValue = false
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
defaultValue = "true"
|
||||
defaultValue = true
|
||||
key = "secure_exh"
|
||||
title = "Secure ExHentai/E-Hentai"
|
||||
summary = "Use the HTTPS version of ExHentai/E-Hentai."
|
||||
@ -91,15 +98,13 @@ class SettingsEhFragment : SettingsController() {
|
||||
"rc_2",
|
||||
"rc_3"
|
||||
)
|
||||
dependency = "enable_exhentai"
|
||||
}
|
||||
}.dependency = "enable_exhentai"
|
||||
|
||||
listPreference {
|
||||
defaultValue = "tr_2"
|
||||
title = "Thumbnail rows"
|
||||
summary = "Affects loading speeds. It is recommended to set this to the maximum size your hath perks allow"
|
||||
key = "ex_thumb_rows"
|
||||
dependency = "enable_exhentai"
|
||||
entries = arrayOf(
|
||||
"4",
|
||||
"10 (requires 'More Thumbs' hath perk)",
|
||||
@ -112,7 +117,7 @@ class SettingsEhFragment : SettingsController() {
|
||||
"tr_10",
|
||||
"tr_20"
|
||||
)
|
||||
}
|
||||
}.dependency = "enable_exhentai"
|
||||
|
||||
preferenceCategory {
|
||||
title = "Advanced"
|
||||
@ -122,48 +127,14 @@ class SettingsEhFragment : SettingsController() {
|
||||
title = "Migrate library metadata"
|
||||
isPersistent = false
|
||||
key = "ex_migrate_library"
|
||||
summary = "Fetch the library metadata to enable tag searching in the library. This button will be visible even if you have already fetched the metadata" />
|
||||
summary = "Fetch the library metadata to enable tag searching in the library. This button will be visible even if you have already fetched the metadata"
|
||||
|
||||
onClick {
|
||||
activity?.let {
|
||||
MetadataFetchDialog().askMigration(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val preferences: PreferencesHelper by injectLazy()
|
||||
|
||||
val enableExhentaiPref by lazy {
|
||||
findPreference("enable_exhentai") as SwitchPreference
|
||||
}
|
||||
|
||||
val migrateLibraryPref by lazy {
|
||||
findPreference("ex_migrate_library") as Preference
|
||||
}
|
||||
|
||||
val useJpTitlePref by lazy {
|
||||
findPreference("use_jp_title") as SwitchPreference
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedState: Bundle?) {
|
||||
super.onViewCreated(view, savedState)
|
||||
|
||||
enableExhentaiPref.setOnPreferenceChangeListener { preference, newVal ->
|
||||
newVal as Boolean
|
||||
(activity as SettingsActivity).parentFlags = SettingsActivity.FLAG_EH_RECREATE
|
||||
if(!newVal) {
|
||||
preferences.enableExhentai().set(false)
|
||||
true
|
||||
} else {
|
||||
startActivity(Intent(context, LoginActivity::class.java))
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
migrateLibraryPref.setOnPreferenceClickListener {
|
||||
MetadataFetchDialog().askMigration(activity)
|
||||
true
|
||||
}
|
||||
|
||||
useJpTitlePref.setOnPreferenceChangeListener { preference, any ->
|
||||
(activity as SettingsActivity).parentFlags = SettingsActivity.FLAG_EH_RECREATE
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.util.LocaleHelper
|
||||
import exh.ui.lock.LockPreference
|
||||
import kotlinx.android.synthetic.main.pref_library_columns.view.*
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.Injekt
|
||||
@ -175,6 +176,13 @@ class SettingsGeneralController : SettingsController() {
|
||||
true
|
||||
}
|
||||
}
|
||||
LockPreference(context).apply {
|
||||
key = "pref_app_lock"
|
||||
title = "Application lock"
|
||||
isPersistent = false
|
||||
|
||||
addPreference(this)
|
||||
}
|
||||
}
|
||||
|
||||
class LibraryColumnsDialog : DialogController() {
|
||||
|
@ -48,6 +48,12 @@ class SettingsMainController : SettingsController() {
|
||||
titleRes = R.string.backup
|
||||
onClick { navigateTo(SettingsBackupController()) }
|
||||
}
|
||||
preference {
|
||||
iconRes = R.drawable.eh_ic_ehlogo_red_24dp
|
||||
iconTint = tintColor
|
||||
titleRes = R.string.pref_category_eh
|
||||
onClick { navigateTo(SettingsEhController()) }
|
||||
}
|
||||
preference {
|
||||
iconRes = R.drawable.ic_code_black_24dp
|
||||
iconTint = tintColor
|
||||
|
@ -56,68 +56,75 @@ class GalleryAdder {
|
||||
val outJson = JsonParser().parse(networkHelper.client.newCall(Request.Builder()
|
||||
.url(API_BASE)
|
||||
.post(RequestBody.create(JSON, json.toString()))
|
||||
.build()).execute().body().string()).obj
|
||||
.build()).execute().body()!!.string()).obj
|
||||
|
||||
val obj = outJson["tokenlist"].array.first()
|
||||
return "${uri.scheme}://${uri.host}/g/${obj["gid"].int}/${obj["token"].string}/"
|
||||
}
|
||||
|
||||
fun addGallery(url: String, fav: Boolean = false): Manga {
|
||||
val urlObj = Uri.parse(url)
|
||||
val source = when(urlObj.host) {
|
||||
"g.e-hentai.org", "e-hentai.org" -> EH_SOURCE_ID
|
||||
"exhentai.org" -> EXH_SOURCE_ID
|
||||
else -> throw MalformedURLException("Not a valid gallery URL!")
|
||||
}
|
||||
|
||||
val realUrl = when (urlObj.pathSegments.first().toLowerCase()) {
|
||||
"g" -> {
|
||||
//Is already gallery page, do nothing
|
||||
url
|
||||
}
|
||||
"s" -> {
|
||||
//Is page, fetch gallery token and use that
|
||||
getGalleryUrlFromPage(url)
|
||||
}
|
||||
else -> {
|
||||
throw MalformedURLException("Not a valid gallery URL!")
|
||||
}
|
||||
}
|
||||
|
||||
val sourceObj = sourceManager.get(source)
|
||||
?: throw IllegalStateException("Could not find EH source!")
|
||||
|
||||
val pathOnlyUrl = getUrlWithoutDomain(realUrl)
|
||||
|
||||
//Use manga in DB if possible, otherwise, make a new manga
|
||||
val manga = db.getManga(pathOnlyUrl, source).executeAsBlocking()
|
||||
?: Manga.create(source).apply {
|
||||
this.url = pathOnlyUrl
|
||||
title = realUrl
|
||||
}
|
||||
|
||||
//Copy basics
|
||||
manga.copyFrom(sourceObj.fetchMangaDetails(manga).toBlocking().first())
|
||||
|
||||
//Apply metadata
|
||||
metadataHelper.fetchEhMetadata(realUrl, isExSource(source))?.copyTo(manga)
|
||||
|
||||
if(fav) manga.favorite = true
|
||||
|
||||
db.insertManga(manga).executeAsBlocking().insertedId()?.let {
|
||||
manga.id = it
|
||||
}
|
||||
|
||||
//Fetch and copy chapters
|
||||
fun addGallery(url: String, fav: Boolean = false): GalleryAddEvent {
|
||||
try {
|
||||
sourceObj.fetchChapterList(manga).map {
|
||||
syncChaptersWithSource(db, it, manga, sourceObj)
|
||||
}.toBlocking().first()
|
||||
} catch (e: Exception) {
|
||||
Timber.w(e, "Failed to update chapters for gallery: ${manga.title}!")
|
||||
}
|
||||
val urlObj = Uri.parse(url)
|
||||
val source = when (urlObj.host) {
|
||||
"g.e-hentai.org", "e-hentai.org" -> EH_SOURCE_ID
|
||||
"exhentai.org" -> EXH_SOURCE_ID
|
||||
else -> return GalleryAddEvent.Fail.UnknownType(url)
|
||||
}
|
||||
|
||||
return manga
|
||||
val realUrl = when (urlObj.pathSegments.first().toLowerCase()) {
|
||||
"g" -> {
|
||||
//Is already gallery page, do nothing
|
||||
url
|
||||
}
|
||||
"s" -> {
|
||||
//Is page, fetch gallery token and use that
|
||||
getGalleryUrlFromPage(url)
|
||||
}
|
||||
else -> {
|
||||
return GalleryAddEvent.Fail.UnknownType(url)
|
||||
}
|
||||
}
|
||||
|
||||
val sourceObj = sourceManager.get(source)
|
||||
?: return GalleryAddEvent.Fail.Error(url, "Could not find EH source!")
|
||||
|
||||
val pathOnlyUrl = getUrlWithoutDomain(realUrl)
|
||||
|
||||
//Use manga in DB if possible, otherwise, make a new manga
|
||||
val manga = db.getManga(pathOnlyUrl, source).executeAsBlocking()
|
||||
?: Manga.create(source).apply {
|
||||
this.url = pathOnlyUrl
|
||||
title = realUrl
|
||||
}
|
||||
|
||||
//Copy basics
|
||||
manga.copyFrom(sourceObj.fetchMangaDetails(manga).toBlocking().first())
|
||||
|
||||
//Apply metadata
|
||||
metadataHelper.fetchEhMetadata(realUrl, isExSource(source))?.copyTo(manga)
|
||||
|
||||
if (fav) manga.favorite = true
|
||||
|
||||
db.insertManga(manga).executeAsBlocking().insertedId()?.let {
|
||||
manga.id = it
|
||||
}
|
||||
|
||||
//Fetch and copy chapters
|
||||
try {
|
||||
sourceObj.fetchChapterList(manga).map {
|
||||
syncChaptersWithSource(db, it, manga, sourceObj)
|
||||
}.toBlocking().first()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to update chapters for gallery: ${manga.title}!")
|
||||
return GalleryAddEvent.Fail.Error(url, "Failed to update chapters for gallery: $url")
|
||||
}
|
||||
|
||||
return GalleryAddEvent.Success(url, manga)
|
||||
} catch(e: Exception) {
|
||||
Timber.e(e, "Could not add gallery!")
|
||||
return GalleryAddEvent.Fail.Error(url,
|
||||
((e.message ?: "Unknown error!") + " (Gallery: $url)").trim())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUrlWithoutDomain(orig: String): String {
|
||||
@ -133,4 +140,28 @@ class GalleryAdder {
|
||||
return orig
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class GalleryAddEvent {
|
||||
abstract val logMessage: String
|
||||
abstract val galleryUrl: String
|
||||
open val galleryTitle: String? = null
|
||||
|
||||
class Success(override val galleryUrl: String,
|
||||
val manga: Manga): GalleryAddEvent() {
|
||||
override val logMessage = "[OK] Added gallery: $galleryTitle"
|
||||
override val galleryTitle: String
|
||||
get() = manga.title
|
||||
}
|
||||
|
||||
sealed class Fail: GalleryAddEvent() {
|
||||
class UnknownType(override val galleryUrl: String): Fail() {
|
||||
override val logMessage = "[ERROR] Unknown gallery type for gallery: $galleryUrl"
|
||||
}
|
||||
|
||||
class Error(override val galleryUrl: String,
|
||||
val message: String): Fail() {
|
||||
override val logMessage = "[ERROR] $message"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,131 +1,145 @@
|
||||
package exh.ui.batchadd
|
||||
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.jakewharton.rxbinding.view.clicks
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseFragment
|
||||
import exh.GalleryAdder
|
||||
import exh.metadata.nullIfBlank
|
||||
import kotlinx.android.synthetic.main.eh_fragment_batch_add.*
|
||||
import timber.log.Timber
|
||||
import kotlin.concurrent.thread
|
||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||
import eu.kanade.tachiyomi.util.combineLatest
|
||||
import eu.kanade.tachiyomi.util.plusAssign
|
||||
import kotlinx.android.synthetic.main.eh_fragment_batch_add.view.*
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.subscriptions.CompositeSubscription
|
||||
|
||||
/**
|
||||
* LoginActivity
|
||||
* Batch add screen
|
||||
*/
|
||||
class BatchAddController : NucleusController<BatchAddPresenter>() {
|
||||
override fun inflateView(inflater: LayoutInflater, container: ViewGroup) =
|
||||
inflater.inflate(R.layout.eh_fragment_batch_add, container, false)!!
|
||||
|
||||
class BatchAddFragment : BaseFragment() {
|
||||
override fun getTitle() = "Batch add"
|
||||
|
||||
private val galleryAdder by lazy { GalleryAdder() }
|
||||
override fun createPresenter() = BatchAddPresenter()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedState: Bundle?)
|
||||
= inflater.inflate(R.layout.eh_fragment_batch_add, container, false)!!
|
||||
override fun onViewCreated(view: View, savedViewState: Bundle?) {
|
||||
super.onViewCreated(view, savedViewState)
|
||||
|
||||
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
|
||||
setToolbarTitle("Batch add")
|
||||
|
||||
setup()
|
||||
}
|
||||
|
||||
fun setup() {
|
||||
btn_add_galleries.setOnClickListener {
|
||||
val galleries = galleries_box.text.toString()
|
||||
//Check text box has content
|
||||
if(galleries.isNullOrBlank()) {
|
||||
noGalleriesSpecified()
|
||||
return@setOnClickListener
|
||||
with(view) {
|
||||
btn_add_galleries.clicks().subscribeUntilDestroy {
|
||||
addGalleries(galleries_box.text.toString())
|
||||
}
|
||||
|
||||
//Too lazy to actually deal with orientation changes
|
||||
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
|
||||
progress_dismiss_btn.clicks().subscribeUntilDestroy {
|
||||
presenter.currentlyAddingRelay.call(false)
|
||||
}
|
||||
|
||||
val splitGalleries = galleries.split("\n").map {
|
||||
it.trim().nullIfBlank()
|
||||
}.filterNotNull()
|
||||
val progressSubscriptions = CompositeSubscription()
|
||||
|
||||
val dialog = MaterialDialog.Builder(context)
|
||||
.title("Adding galleries...")
|
||||
.progress(false, splitGalleries.size, true)
|
||||
.cancelable(false)
|
||||
.canceledOnTouchOutside(false)
|
||||
presenter.currentlyAddingRelay
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeUntilDestroy {
|
||||
progressSubscriptions.clear()
|
||||
if(it) {
|
||||
showProgress(this)
|
||||
progressSubscriptions += presenter.progressRelay
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.combineLatest(presenter.progressTotalRelay, { progress, total ->
|
||||
//Show hide dismiss button
|
||||
progress_dismiss_btn.visibility =
|
||||
if(progress == total)
|
||||
View.VISIBLE
|
||||
else View.GONE
|
||||
|
||||
formatProgress(progress, total)
|
||||
}).subscribeUntilDestroy {
|
||||
progress_text.text = it
|
||||
}
|
||||
|
||||
progressSubscriptions += presenter.progressTotalRelay
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeUntilDestroy {
|
||||
progress_bar.max = it
|
||||
}
|
||||
|
||||
progressSubscriptions += presenter.progressRelay
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeUntilDestroy {
|
||||
progress_bar.progress = it
|
||||
}
|
||||
|
||||
presenter.eventRelay
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribeUntilDestroy {
|
||||
progress_log.append("$it\n")
|
||||
}?.let {
|
||||
progressSubscriptions += it
|
||||
}
|
||||
} else hideProgress(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val View.progressViews
|
||||
get() = listOf(
|
||||
progress_title_view,
|
||||
progress_log_wrapper,
|
||||
progress_bar,
|
||||
progress_text,
|
||||
progress_dismiss_btn
|
||||
)
|
||||
|
||||
private val View.inputViews
|
||||
get() = listOf(
|
||||
input_title_view,
|
||||
galleries_box,
|
||||
btn_add_galleries
|
||||
)
|
||||
|
||||
private var List<View>.visibility: Int
|
||||
get() = throw UnsupportedOperationException()
|
||||
set(v) { forEach { it.visibility = v } }
|
||||
|
||||
private fun showProgress(target: View? = view) {
|
||||
target?.apply {
|
||||
progressViews.visibility = View.VISIBLE
|
||||
inputViews.visibility = View.GONE
|
||||
}?.progress_log?.text = ""
|
||||
}
|
||||
|
||||
private fun hideProgress(target: View? = view) {
|
||||
target?.apply {
|
||||
progressViews.visibility = View.GONE
|
||||
inputViews.visibility = View.VISIBLE
|
||||
}?.galleries_box?.setText("", TextView.BufferType.EDITABLE)
|
||||
}
|
||||
|
||||
private fun formatProgress(progress: Int, total: Int) = "$progress/$total"
|
||||
|
||||
private fun addGalleries(galleries: String) {
|
||||
//Check text box has content
|
||||
if(galleries.isBlank()) {
|
||||
noGalleriesSpecified()
|
||||
return
|
||||
}
|
||||
|
||||
presenter.addGalleries(galleries)
|
||||
}
|
||||
|
||||
private fun noGalleriesSpecified() {
|
||||
activity?.let {
|
||||
MaterialDialog.Builder(it)
|
||||
.title("No galleries to add!")
|
||||
.content("You must specify at least one gallery to add!")
|
||||
.positiveText("Ok")
|
||||
.onPositive { materialDialog, _ -> materialDialog.dismiss() }
|
||||
.cancelable(true)
|
||||
.canceledOnTouchOutside(true)
|
||||
.show()
|
||||
|
||||
val succeeded = mutableListOf<String>()
|
||||
val failed = mutableListOf<String>()
|
||||
|
||||
thread {
|
||||
splitGalleries.forEachIndexed { i, s ->
|
||||
activity.runOnUiThread {
|
||||
dialog.setContent("Processing: $s")
|
||||
}
|
||||
if(addGallery(s)) {
|
||||
succeeded.add(s)
|
||||
} else {
|
||||
failed.add(s)
|
||||
}
|
||||
activity.runOnUiThread {
|
||||
dialog.setProgress(i + 1)
|
||||
}
|
||||
}
|
||||
|
||||
//Show report
|
||||
val succeededCount = succeeded.size
|
||||
val failedCount = failed.size
|
||||
|
||||
if(succeeded.isEmpty()) succeeded += "None"
|
||||
if(failed.isEmpty()) failed += "None"
|
||||
val succeededReport = succeeded.joinToString(separator = "\n", prefix = "Added:\n")
|
||||
val failedReport = failed.joinToString(separator = "\n", prefix = "Failed:\n")
|
||||
|
||||
val summary = "Summary:\nAdded: $succeededCount gallerie(s)\nFailed: $failedCount gallerie(s)"
|
||||
|
||||
val report = listOf(succeededReport, failedReport, summary).joinToString(separator = "\n\n")
|
||||
|
||||
activity.runOnUiThread {
|
||||
//Enable orientation changes again
|
||||
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
|
||||
|
||||
dialog.dismiss()
|
||||
|
||||
MaterialDialog.Builder(context)
|
||||
.title("Batch add report")
|
||||
.content(report)
|
||||
.positiveText("Ok")
|
||||
.cancelable(true)
|
||||
.canceledOnTouchOutside(true)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun addGallery(url: String): Boolean {
|
||||
try {
|
||||
galleryAdder.addGallery(url, true)
|
||||
} catch(t: Throwable) {
|
||||
Timber.e(t, "Could not add gallery!")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun noGalleriesSpecified() {
|
||||
MaterialDialog.Builder(context)
|
||||
.title("No galleries to add!")
|
||||
.content("You must specify at least one gallery to add!")
|
||||
.positiveText("Ok")
|
||||
.onPositive { materialDialog, _ -> materialDialog.dismiss() }
|
||||
.cancelable(true)
|
||||
.canceledOnTouchOutside(true)
|
||||
.show()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance() = BatchAddFragment()
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,51 @@
|
||||
package exh.ui.batchadd
|
||||
|
||||
/**
|
||||
* Created by nulldev on 8/23/17.
|
||||
*/
|
||||
import com.jakewharton.rxrelay.BehaviorRelay
|
||||
import com.jakewharton.rxrelay.ReplayRelay
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import exh.GalleryAddEvent
|
||||
import exh.GalleryAdder
|
||||
import exh.metadata.nullIfBlank
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class BatchAddPresenter: BasePresenter<BatchAddController>() {
|
||||
|
||||
private val galleryAdder by lazy { GalleryAdder() }
|
||||
|
||||
val progressTotalRelay = BehaviorRelay.create(0)!!
|
||||
val progressRelay = BehaviorRelay.create(0)!!
|
||||
var eventRelay: ReplayRelay<String>? = null
|
||||
val currentlyAddingRelay = BehaviorRelay.create(false)!!
|
||||
|
||||
fun addGalleries(galleries: String) {
|
||||
eventRelay = ReplayRelay.create()
|
||||
val splitGalleries = galleries.split("\n").map {
|
||||
it.trim().nullIfBlank()
|
||||
}.filterNotNull()
|
||||
|
||||
progressRelay.call(0)
|
||||
progressTotalRelay.call(splitGalleries.size)
|
||||
|
||||
currentlyAddingRelay.call(true)
|
||||
|
||||
thread {
|
||||
val succeeded = mutableListOf<String>()
|
||||
val failed = mutableListOf<String>()
|
||||
|
||||
splitGalleries.forEachIndexed { i, s ->
|
||||
val result = galleryAdder.addGallery(s, true)
|
||||
if(result is GalleryAddEvent.Success) {
|
||||
succeeded.add(s)
|
||||
} else {
|
||||
failed.add(s)
|
||||
}
|
||||
progressRelay.call(i + 1)
|
||||
eventRelay?.call(result.logMessage)
|
||||
}
|
||||
|
||||
//Show report
|
||||
val summary = "\nSummary:\nAdded: ${succeeded.size} gallerie(s)\nFailed: ${failed.size} gallerie(s)"
|
||||
eventRelay?.call(summary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,11 @@ import android.view.MenuItem
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaActivity
|
||||
import exh.GalleryAdder
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import timber.log.Timber
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
//TODO :(
|
||||
class InterceptActivity : BaseActivity() {
|
||||
|
||||
private val galleryAdder = GalleryAdder()
|
||||
@ -19,12 +18,9 @@ class InterceptActivity : BaseActivity() {
|
||||
var finished = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setAppTheme()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.eh_activity_intercept)
|
||||
|
||||
setupToolbar(toolbar, backNavigation = false)
|
||||
|
||||
if(savedInstanceState == null)
|
||||
thread { setup() }
|
||||
}
|
||||
@ -54,8 +50,9 @@ class InterceptActivity : BaseActivity() {
|
||||
if(Intent.ACTION_VIEW == intent.action) {
|
||||
val manga = galleryAdder.addGallery(intent.dataString)
|
||||
|
||||
if(!finished)
|
||||
startActivity(MangaActivity.newIntent(this, manga, true))
|
||||
//TODO
|
||||
// if(!finished)
|
||||
// startActivity(MangaActivity.newIntent(this, manga, true))
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
|
@ -1,60 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
41
app/src/main/java/exh/ui/lock/LockChangeHandler.kt
Normal file
41
app/src/main/java/exh/ui/lock/LockChangeHandler.kt
Normal file
@ -0,0 +1,41 @@
|
||||
package exh.ui.lock
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ObjectAnimator
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.changehandler.AnimatorChangeHandler
|
||||
import java.util.ArrayList
|
||||
|
||||
class LockChangeHandler : AnimatorChangeHandler {
|
||||
constructor(): super()
|
||||
|
||||
constructor(removesFromViewOnPush: Boolean) : super(removesFromViewOnPush)
|
||||
|
||||
constructor(duration: Long) : super(duration)
|
||||
|
||||
constructor(duration: Long, removesFromViewOnPush: Boolean) : super(duration, removesFromViewOnPush)
|
||||
|
||||
override fun getAnimator(container: ViewGroup, from: View?, to: View?, isPush: Boolean, toAddedToContainer: Boolean): Animator {
|
||||
val animator = AnimatorSet()
|
||||
val viewAnimators = ArrayList<Animator>()
|
||||
|
||||
if (!isPush && from != null) {
|
||||
viewAnimators.add(ObjectAnimator.ofFloat(from, View.SCALE_X, 5f))
|
||||
viewAnimators.add(ObjectAnimator.ofFloat(from, View.SCALE_Y, 5f))
|
||||
viewAnimators.add(ObjectAnimator.ofFloat(from, View.ALPHA, 0f))
|
||||
}
|
||||
|
||||
animator.playTogether(viewAnimators)
|
||||
return animator
|
||||
}
|
||||
|
||||
override fun resetFromView(from: View) {}
|
||||
|
||||
override fun copy(): ControllerChangeHandler =
|
||||
LockChangeHandler(animationDuration, removesFromViewOnPush())
|
||||
|
||||
}
|
||||
|
68
app/src/main/java/exh/ui/lock/LockController.kt
Executable file
68
app/src/main/java/exh/ui/lock/LockController.kt
Executable file
@ -0,0 +1,68 @@
|
||||
package exh.ui.lock
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
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.controller.NucleusController
|
||||
import kotlinx.android.synthetic.main.activity_lock.view.*
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class LockController : NucleusController<LockPresenter>() {
|
||||
override fun inflateView(inflater: LayoutInflater, container: ViewGroup)
|
||||
= inflater.inflate(R.layout.activity_lock, container, false)!!
|
||||
|
||||
override fun createPresenter() = LockPresenter()
|
||||
|
||||
override fun getTitle() = "Application locked"
|
||||
|
||||
val prefs: PreferencesHelper by injectLazy()
|
||||
|
||||
override fun onViewCreated(view: View, savedViewState: Bundle?) {
|
||||
super.onViewCreated(view, savedViewState)
|
||||
|
||||
if(!lockEnabled(prefs)) {
|
||||
closeLock()
|
||||
return
|
||||
}
|
||||
|
||||
with(view) {
|
||||
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!
|
||||
closeLock()
|
||||
} else {
|
||||
MaterialDialog.Builder(context)
|
||||
.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?) {}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun closeLock() {
|
||||
router.popCurrentController()
|
||||
}
|
||||
|
||||
override fun handleBack() = true
|
||||
}
|
@ -17,7 +17,7 @@ import java.security.SecureRandom
|
||||
class LockPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
Preference(context, attrs) {
|
||||
|
||||
val secureRandom by lazy { SecureRandom() }
|
||||
private val secureRandom by lazy { SecureRandom() }
|
||||
|
||||
val prefs: PreferencesHelper by injectLazy()
|
||||
|
||||
@ -26,12 +26,11 @@ class LockPreference @JvmOverloads constructor(context: Context, attrs: Attribut
|
||||
updateSummary()
|
||||
}
|
||||
|
||||
fun updateSummary() {
|
||||
if(lockEnabled(prefs)) {
|
||||
summary = "Application is locked"
|
||||
} else {
|
||||
summary = "Application is not locked, tap to lock"
|
||||
}
|
||||
private fun updateSummary() {
|
||||
summary = if(lockEnabled(prefs))
|
||||
"Application is locked"
|
||||
else
|
||||
"Application is not locked, tap to lock"
|
||||
}
|
||||
|
||||
override fun onClick() {
|
||||
@ -65,7 +64,7 @@ class LockPreference @JvmOverloads constructor(context: Context, attrs: Attribut
|
||||
}
|
||||
}
|
||||
|
||||
fun savePassword(password: String) {
|
||||
private fun savePassword(password: String) {
|
||||
val salt: String?
|
||||
val hash: String?
|
||||
val length: Int
|
||||
|
6
app/src/main/java/exh/ui/lock/LockPresenter.kt
Normal file
6
app/src/main/java/exh/ui/lock/LockPresenter.kt
Normal file
@ -0,0 +1,6 @@
|
||||
package exh.ui.lock
|
||||
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
|
||||
class LockPresenter: BasePresenter<LockController>()
|
||||
|
@ -44,13 +44,6 @@ fun lockEnabled(prefs: PreferencesHelper = Injekt.get())
|
||||
&& 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
|
||||
*
|
||||
|
@ -15,7 +15,6 @@ import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||
import exh.EXH_SOURCE_ID
|
||||
import kotlinx.android.synthetic.main.eh_activity_login.*
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
@ -34,13 +33,10 @@ class LoginActivity : BaseActivity() {
|
||||
val sourceManager: SourceManager by injectLazy()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setAppTheme()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.eh_activity_login)
|
||||
|
||||
setup()
|
||||
|
||||
setupToolbar(toolbar, backNavigation = false)
|
||||
}
|
||||
|
||||
fun setup() {
|
||||
@ -187,7 +183,7 @@ class LoginActivity : BaseActivity() {
|
||||
document.getElementsByName('submit')[0].style.visibility = 'visible';
|
||||
document.querySelector('td[width="60%"][valign="top"]').style.visibility = 'visible';
|
||||
|
||||
function hide(e) {if(e !== null && e !== undefined) e.style.display = 'none';}
|
||||
function hide(e) {if(e != null) e.style.display = 'none';}
|
||||
|
||||
hide(document.querySelector(".errorwrap"));
|
||||
hide(document.querySelector('td[width="40%"][valign="top"]'));
|
||||
@ -202,7 +198,7 @@ class LoginActivity : BaseActivity() {
|
||||
hide(fd[2]);
|
||||
hide(child.querySelector('br'));
|
||||
var error = document.querySelector(".page > div > .borderwrap");
|
||||
if(error !== null) error.style.visibility = 'visible';
|
||||
if(error != null) error.style.visibility = 'visible';
|
||||
hide(fh[0]);
|
||||
hide(fh[1]);
|
||||
hide(document.querySelector("#gfooter"));
|
||||
@ -211,7 +207,7 @@ class LoginActivity : BaseActivity() {
|
||||
e.style.color = "white";
|
||||
});
|
||||
var pc = document.querySelector(".postcolor");
|
||||
if(pc !== null) pc.style.color = "#26353F";
|
||||
if(pc != null) pc.style.color = "#26353F";
|
||||
})()
|
||||
"""
|
||||
}
|
||||
|
@ -1,29 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/backgroundDark">
|
||||
|
||||
<com.andrognito.pinlockview.PinLockView
|
||||
android:id="@+id/pin_lock_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/indicator_dots"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/indicator_dots" />
|
||||
|
||||
<com.andrognito.pinlockview.IndicatorDots
|
||||
android:id="@+id/indicator_dots"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toTopOf="@+id/pin_lock_view"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/pin_lock_view"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent" />
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
</android.support.constraint.ConstraintLayout>
|
@ -17,8 +17,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<include layout="@layout/toolbar"/>
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<FrameLayout
|
||||
|
@ -17,8 +17,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<include layout="@layout/toolbar"/>
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
|
@ -9,46 +9,121 @@
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:padding="16dp ">
|
||||
|
||||
<TextView
|
||||
android:text="Enter the galleries to add (E-Hentai/ExHentai only, separated by a new line):"
|
||||
android:id="@+id/input_title_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/textView"
|
||||
android:text="Enter the galleries to add (separated by a new line):"
|
||||
android:textAppearance="@style/TextAppearance.Medium.Title"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_marginStart="16dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:layout_marginRight="16dp" />
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/galleries_box"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:inputType="textMultiLine"
|
||||
android:ems="10"
|
||||
android:id="@+id/galleries_box"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/textView"
|
||||
app:layout_constraintRight_toRightOf="@+id/textView"
|
||||
android:hint="Example:\n\nhttp://e-hentai.org/g/12345/1a2b3c4e\nhttp://g.e-hentai.org/g/67890/6f7g8h9i\nhttp://exhentai.org/g/13579/1a3b5c7e\nhttps://exhentai.org/g/24680/2f4g6h8i\n"
|
||||
android:inputType="textMultiLine"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@+id/btn_add_galleries"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:hint="Example:\n\nhttp://e-hentai.org/g/12345/1a2b3c4e\nhttp://g.e-hentai.org/g/67890/6f7g8h9i\nhttp://exhentai.org/g/13579/1a3b5c7e\nhttps://exhentai.org/g/24680/2f4g6h8i\n" />
|
||||
app:layout_constraintLeft_toLeftOf="@+id/input_title_view"
|
||||
app:layout_constraintRight_toRightOf="@+id/input_title_view"
|
||||
app:layout_constraintTop_toBottomOf="@+id/input_title_view" />
|
||||
|
||||
<Button
|
||||
android:text="Add Galleries"
|
||||
android:id="@+id/btn_add_galleries"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/btn_add_galleries"
|
||||
android:text="Add Galleries"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/galleries_box"
|
||||
app:layout_constraintRight_toRightOf="@+id/galleries_box" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/progress_title_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginLeft="0dp"
|
||||
android:layout_marginRight="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:text="Adding galleries..."
|
||||
android:textAppearance="@style/TextAppearance.Medium.Title"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintBottom_toTopOf="@+id/progress_dismiss_btn"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/progress_log_wrapper"
|
||||
app:layout_constraintRight_toLeftOf="@+id/progress_text" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/progress_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:singleLine="true"
|
||||
android:text=""
|
||||
android:textAlignment="textEnd"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintRight_toRightOf="@+id/progress_log_wrapper"
|
||||
app:layout_constraintTop_toTopOf="@+id/progress_bar" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/progress_dismiss_btn"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="0dp"
|
||||
android:layout_marginRight="0dp"
|
||||
android:text="Finish"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/progress_log_wrapper"
|
||||
app:layout_constraintRight_toRightOf="@+id/progress_log_wrapper"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/btn_add_galleries"
|
||||
android:layout_marginBottom="0dp" />
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/progress_log_wrapper"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintBottom_toTopOf="@+id/progress_bar"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/progress_title_view"
|
||||
app:layout_constraintRight_toRightOf="@+id/progress_title_view"
|
||||
app:layout_constraintTop_toBottomOf="@+id/progress_title_view"
|
||||
app:layout_constraintVertical_bias="0.0">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/progress_log"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.Regular.Body1"
|
||||
android:visibility="visible" />
|
||||
</ScrollView>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
|
@ -101,48 +101,4 @@
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
|
||||
<!-- EH -->
|
||||
<string-array name="ehentai_quality">
|
||||
<item>Auto</item>
|
||||
<item>2400x</item>
|
||||
<item>1600x</item>
|
||||
<item>1280x</item>
|
||||
<item>980x</item>
|
||||
<item>780x</item>
|
||||
</string-array>
|
||||
<string-array name="ehentai_quality_values">
|
||||
<item>auto</item>
|
||||
<item>ovrs_2400</item>
|
||||
<item>ovrs_1600</item>
|
||||
<item>high</item>
|
||||
<item>med</item>
|
||||
<item>low</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="ehentai_search_result_count">
|
||||
<item>25 results</item>
|
||||
<item>50 results</item>
|
||||
<item>100 results</item>
|
||||
<item>200 results</item>
|
||||
</string-array>
|
||||
<string-array name="ehentai_search_result_count_values">
|
||||
<item>rc_0</item>
|
||||
<item>rc_1</item>
|
||||
<item>rc_2</item>
|
||||
<item>rc_3</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="ehentai_thumbnail_rows">
|
||||
<item>4</item>
|
||||
<item>10 (requires \'More Thumbs\' hath perk)</item>
|
||||
<item>20 (requires \'Thumbs Up\' hath perk)</item>
|
||||
<item>40 (requires \'All Thumbs\' hath perk)</item>
|
||||
</string-array>
|
||||
<string-array name="ehentai_thumbnail_rows_values">
|
||||
<item>tr_2</item>
|
||||
<item>tr_5</item>
|
||||
<item>tr_10</item>
|
||||
<item>tr_20</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
@ -1,12 +1,12 @@
|
||||
package eu.kanade.tachiyomi
|
||||
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.robolectric.manifest.AndroidManifest
|
||||
//import org.robolectric.RobolectricTestRunner
|
||||
//import org.robolectric.annotation.Config
|
||||
//import org.robolectric.manifest.AndroidManifest
|
||||
|
||||
class CustomRobolectricGradleTestRunner(klass: Class<*>) : RobolectricTestRunner(klass) {
|
||||
//class CustomRobolectricGradleTestRunner(klass: Class<*>) : RobolectricTestRunner(klass) {
|
||||
|
||||
override fun getAppManifest(config: Config): AndroidManifest {
|
||||
return super.getAppManifest(config).apply { packageName = "eu.kanade.tachiyomi" }
|
||||
}
|
||||
}
|
||||
// override fun getAppManifest(config: Config): AndroidManifest {
|
||||
// return super.getAppManifest(config).apply { packageName = "eu.kanade.tachiyomi" }
|
||||
// }
|
||||
//}
|
Loading…
x
Reference in New Issue
Block a user