Migrate to Tachiyomi 6.1

Rewrite batch add UI
Rewrite lock UI
This commit is contained in:
NerdNumber9 2017-08-24 12:28:54 -04:00
parent 3da7c47bf5
commit 32d02f9329
28 changed files with 640 additions and 532 deletions

View File

@ -42,8 +42,8 @@ android {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 25 targetSdkVersion 25
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
versionCode 5003 versionCode 6101
versionName "v5.0.3-EH" versionName "v6.1.1-EH"
buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\"" buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\""
buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\"" buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\""
@ -214,7 +214,7 @@ dependencies {
releaseCompile "com.google.firebase:firebase-crash:$firebase_version" releaseCompile "com.google.firebase:firebase-crash:$firebase_version"
//SnappyDB (EH) //SnappyDB (EH)
compile 'io.paperdb:paperdb:2.0' compile 'io.paperdb:paperdb:2.1'
//JVE (Regex) (EH) //JVE (Regex) (EH)
compile 'ru.lanwen.verbalregex:java-verbal-expressions:1.4' compile 'ru.lanwen.verbalregex:java-verbal-expressions:1.4'

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="eu.kanade.tachiyomi"> package="eu.kanade.tachiyomi">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
@ -106,8 +107,8 @@
<!-- EH --> <!-- EH -->
<activity <activity
android:name="exh.ui.login.LoginActivity" android:name="exh.ui.login.LoginActivity"
android:label="@string/label_login" android:label="@string/label_login">
android:parentActivityName=".ui.setting.SettingsActivity" > <!-- TODO parent activity -->
</activity> </activity>
<activity <activity
@ -145,17 +146,8 @@
android:scheme="https"/> android:scheme="https"/>
</intent-filter> </intent-filter>
</activity> </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"/> android:label="Application locked"/>
</application> </application>

View File

@ -67,7 +67,7 @@ class NHentai(context: Context) : HttpSource() {
= parseResultPage(response) = parseResultPage(response)
override fun mangaDetailsParse(response: 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 //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> { override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
@ -85,7 +85,7 @@ class NHentai(context: Context) : HttpSource() {
= nhGet(baseUrl + "/api/gallery/" + url.split("/").last()) = nhGet(baseUrl + "/api/gallery/" + url.split("/").last())
fun parseResultPage(response: Response): MangasPage { 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") val error = res.get("error")
if(error == null) { if(error == null) {
@ -154,7 +154,7 @@ class NHentai(context: Context) : HttpSource() {
?: client.newCall(urlToDetailsRequest(url)) ?: client.newCall(urlToDetailsRequest(url))
.asObservableSuccess() .asObservableSuccess()
.map { .map {
rawParseGallery(jsonParser.parse(it.body().string()).asJsonObject) rawParseGallery(jsonParser.parse(it.body()!!.string()).asJsonObject)
}.toBlocking().first() }.toBlocking().first()
}!! }!!

View File

@ -4,54 +4,8 @@ import android.support.v7.app.AppCompatActivity
import eu.kanade.tachiyomi.util.LocaleHelper import eu.kanade.tachiyomi.util.LocaleHelper
abstract class BaseActivity : AppCompatActivity() { abstract class BaseActivity : AppCompatActivity() {
init { init {
@Suppress("LeakingThis") @Suppress("LeakingThis")
LocaleHelper.updateConfiguration(this) 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
}
}
}
} }

View File

@ -16,12 +16,6 @@ class LibraryCategoryAdapter(view: LibraryCategoryView) :
*/ */
private var mangas: List<LibraryItem> = emptyList() 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 var asyncSearchText: String? = null
/** /**
@ -36,49 +30,6 @@ class LibraryCategoryAdapter(view: LibraryCategoryView) :
performFilter() 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. * Returns the position in the adapter for the given manga.
* *
@ -89,7 +40,9 @@ class LibraryCategoryAdapter(view: LibraryCategoryView) :
} }
fun performFilter() { fun performFilter() {
updateDataSet(mangas.filter { it.filter(searchText) }) updateDataSet(mangas.filter {
it.filter(searchText)
})
} }
} }

View File

@ -102,19 +102,6 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
fun onBind(category: Category) { fun onBind(category: Category) {
this.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()) { adapter.mode = if (controller.selectedMangas.isNotEmpty()) {
FlexibleAdapter.MODE_MULTI FlexibleAdapter.MODE_MULTI
} else { } else {

View File

@ -12,10 +12,18 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.util.inflate import eu.kanade.tachiyomi.util.inflate
import eu.kanade.tachiyomi.widget.AutofitRecyclerView 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.* import kotlinx.android.synthetic.main.catalogue_grid_item.view.*
class LibraryItem(val manga: Manga) : AbstractFlexibleItem<LibraryHolder>(), IFilterable { class LibraryItem(val manga: Manga) : AbstractFlexibleItem<LibraryHolder>(), IFilterable {
// --> EH
private val searchEngine = SearchEngine()
private val metadataHelper = MetadataHelper()
// <-- EH
override fun getLayoutRes(): Int { override fun getLayoutRes(): Int {
return R.layout.catalogue_grid_item 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. * @return true if the manga should be included, false otherwise.
*/ */
override fun filter(constraint: String): Boolean { 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) || return manga.title.contains(constraint, true) ||
(manga.author?.contains(constraint, true) ?: false) (manga.author?.contains(constraint, true) ?: false)
} }

View File

@ -1,8 +1,13 @@
package eu.kanade.tachiyomi.ui.main package eu.kanade.tachiyomi.ui.main
import android.animation.ObjectAnimator 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.content.Intent
import android.graphics.Color import android.graphics.Color
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.v4.view.GravityCompat import android.support.v4.view.GravityCompat
import android.support.v4.widget.DrawerLayout import android.support.v4.widget.DrawerLayout
@ -10,6 +15,8 @@ import android.support.v7.graphics.drawable.DrawerArrowDrawable
import android.view.ViewGroup import android.view.ViewGroup
import com.bluelinelabs.conductor.* import com.bluelinelabs.conductor.*
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler 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.Migrations
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper 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.recent_updates.RecentChaptersController
import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
import eu.kanade.tachiyomi.ui.setting.SettingsMainController 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 kotlinx.android.synthetic.main.main_activity.*
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.util.*
class MainActivity : BaseActivity() { class MainActivity : BaseActivity() {
@ -86,9 +99,8 @@ class MainActivity : BaseActivity() {
R.id.nav_drawer_recently_read -> setRoot(RecentlyReadController(), id) R.id.nav_drawer_recently_read -> setRoot(RecentlyReadController(), id)
R.id.nav_drawer_catalogues -> setRoot(CatalogueController(), id) R.id.nav_drawer_catalogues -> setRoot(CatalogueController(), id)
R.id.nav_drawer_latest_updates -> setRoot(LatestUpdatesController(), id) R.id.nav_drawer_latest_updates -> setRoot(LatestUpdatesController(), id)
//TODO
// --> EH // --> EH
R.id.nav_drawer_batch_add -> setFragment(BatchAddFragment.newInstance(), id) R.id.nav_drawer_batch_add -> setRoot(BatchAddController(), id)
// <-- EH // <-- EH
R.id.nav_drawer_downloads -> { R.id.nav_drawer_downloads -> {
router.pushController(RouterTransaction.with(DownloadController()) 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()) syncActivityViewWithController(router.backstack.lastOrNull()?.controller())
if (savedInstanceState == null) { if (savedInstanceState == null) {
@ -144,15 +167,6 @@ class MainActivity : BaseActivity() {
if (Migrations.upgrade(preferences)) { if (Migrations.upgrade(preferences)) {
ChangelogDialogController().showDialog(router) 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 { companion object {
// Shortcut actions // Shortcut actions
const val SHORTCUT_LIBRARY = "eu.kanade.tachiyomi.SHOW_LIBRARY" const val SHORTCUT_LIBRARY = "eu.kanade.tachiyomi.SHOW_LIBRARY"

View File

@ -1,19 +1,15 @@
package eu.kanade.tachiyomi.ui.setting package eu.kanade.tachiyomi.ui.setting
import android.content.Intent import android.content.Intent
import android.os.Bundle
import android.support.v7.preference.PreferenceScreen 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.migration.MetadataFetchDialog
import exh.ui.login.LoginActivity import exh.ui.login.LoginActivity
import uy.kohesive.injekt.injectLazy
/** /**
* EH Settings fragment * EH Settings fragment
*/ */
class SettingsEhFragment : SettingsController() { class SettingsEhController : SettingsController() {
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) {
title = "E-Hentai" title = "E-Hentai"
@ -27,13 +23,24 @@ class SettingsEhFragment : SettingsController() {
.asObservable().subscribeUntilDestroy { .asObservable().subscribeUntilDestroy {
isChecked = it isChecked = it
} }
onChange { newVal ->
newVal as Boolean
if(!newVal) {
preferences.enableExhentai().set(false)
true
} else {
startActivity(Intent(context, LoginActivity::class.java))
false
}
}
} }
switchPreference { switchPreference {
title = "Use Hentai@Home Network" 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" 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" key = "enable_hah"
defaultValue = "true" defaultValue = true
} }
switchPreference { 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)" 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)" summaryOff = "Currently showing English/Romanized titles in search results. Clear the chapter cache after changing this (in the Advanced section)"
key = "use_jp_title" key = "use_jp_title"
defaultValue = "false" defaultValue = false
} }
switchPreference { switchPreference {
defaultValue = "true" defaultValue = true
key = "secure_exh" key = "secure_exh"
title = "Secure ExHentai/E-Hentai" title = "Secure ExHentai/E-Hentai"
summary = "Use the HTTPS version of ExHentai/E-Hentai." summary = "Use the HTTPS version of ExHentai/E-Hentai."
@ -91,15 +98,13 @@ class SettingsEhFragment : SettingsController() {
"rc_2", "rc_2",
"rc_3" "rc_3"
) )
dependency = "enable_exhentai" }.dependency = "enable_exhentai"
}
listPreference { listPreference {
defaultValue = "tr_2" defaultValue = "tr_2"
title = "Thumbnail rows" title = "Thumbnail rows"
summary = "Affects loading speeds. It is recommended to set this to the maximum size your hath perks allow" summary = "Affects loading speeds. It is recommended to set this to the maximum size your hath perks allow"
key = "ex_thumb_rows" key = "ex_thumb_rows"
dependency = "enable_exhentai"
entries = arrayOf( entries = arrayOf(
"4", "4",
"10 (requires 'More Thumbs' hath perk)", "10 (requires 'More Thumbs' hath perk)",
@ -112,7 +117,7 @@ class SettingsEhFragment : SettingsController() {
"tr_10", "tr_10",
"tr_20" "tr_20"
) )
} }.dependency = "enable_exhentai"
preferenceCategory { preferenceCategory {
title = "Advanced" title = "Advanced"
@ -122,48 +127,14 @@ class SettingsEhFragment : SettingsController() {
title = "Migrate library metadata" title = "Migrate library metadata"
isPersistent = false isPersistent = false
key = "ex_migrate_library" 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
}
}
} }

View File

@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.util.LocaleHelper import eu.kanade.tachiyomi.util.LocaleHelper
import exh.ui.lock.LockPreference
import kotlinx.android.synthetic.main.pref_library_columns.view.* import kotlinx.android.synthetic.main.pref_library_columns.view.*
import rx.Observable import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
@ -175,6 +176,13 @@ class SettingsGeneralController : SettingsController() {
true true
} }
} }
LockPreference(context).apply {
key = "pref_app_lock"
title = "Application lock"
isPersistent = false
addPreference(this)
}
} }
class LibraryColumnsDialog : DialogController() { class LibraryColumnsDialog : DialogController() {

View File

@ -48,6 +48,12 @@ class SettingsMainController : SettingsController() {
titleRes = R.string.backup titleRes = R.string.backup
onClick { navigateTo(SettingsBackupController()) } onClick { navigateTo(SettingsBackupController()) }
} }
preference {
iconRes = R.drawable.eh_ic_ehlogo_red_24dp
iconTint = tintColor
titleRes = R.string.pref_category_eh
onClick { navigateTo(SettingsEhController()) }
}
preference { preference {
iconRes = R.drawable.ic_code_black_24dp iconRes = R.drawable.ic_code_black_24dp
iconTint = tintColor iconTint = tintColor

View File

@ -56,68 +56,75 @@ class GalleryAdder {
val outJson = JsonParser().parse(networkHelper.client.newCall(Request.Builder() val outJson = JsonParser().parse(networkHelper.client.newCall(Request.Builder()
.url(API_BASE) .url(API_BASE)
.post(RequestBody.create(JSON, json.toString())) .post(RequestBody.create(JSON, json.toString()))
.build()).execute().body().string()).obj .build()).execute().body()!!.string()).obj
val obj = outJson["tokenlist"].array.first() val obj = outJson["tokenlist"].array.first()
return "${uri.scheme}://${uri.host}/g/${obj["gid"].int}/${obj["token"].string}/" return "${uri.scheme}://${uri.host}/g/${obj["gid"].int}/${obj["token"].string}/"
} }
fun addGallery(url: String, fav: Boolean = false): Manga { fun addGallery(url: String, fav: Boolean = false): GalleryAddEvent {
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
try { try {
sourceObj.fetchChapterList(manga).map { val urlObj = Uri.parse(url)
syncChaptersWithSource(db, it, manga, sourceObj) val source = when (urlObj.host) {
}.toBlocking().first() "g.e-hentai.org", "e-hentai.org" -> EH_SOURCE_ID
} catch (e: Exception) { "exhentai.org" -> EXH_SOURCE_ID
Timber.w(e, "Failed to update chapters for gallery: ${manga.title}!") 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 { private fun getUrlWithoutDomain(orig: String): String {
@ -133,4 +140,28 @@ class GalleryAdder {
return orig 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"
}
}
} }

View File

@ -1,131 +1,145 @@
package exh.ui.batchadd package exh.ui.batchadd
import android.content.pm.ActivityInfo
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.jakewharton.rxbinding.view.clicks
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.fragment.BaseFragment import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import exh.GalleryAdder import eu.kanade.tachiyomi.util.combineLatest
import exh.metadata.nullIfBlank import eu.kanade.tachiyomi.util.plusAssign
import kotlinx.android.synthetic.main.eh_fragment_batch_add.* import kotlinx.android.synthetic.main.eh_fragment_batch_add.view.*
import timber.log.Timber import rx.android.schedulers.AndroidSchedulers
import kotlin.concurrent.thread 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?) override fun onViewCreated(view: View, savedViewState: Bundle?) {
= inflater.inflate(R.layout.eh_fragment_batch_add, container, false)!! super.onViewCreated(view, savedViewState)
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { with(view) {
setToolbarTitle("Batch add") btn_add_galleries.clicks().subscribeUntilDestroy {
addGalleries(galleries_box.text.toString())
setup()
}
fun setup() {
btn_add_galleries.setOnClickListener {
val galleries = galleries_box.text.toString()
//Check text box has content
if(galleries.isNullOrBlank()) {
noGalleriesSpecified()
return@setOnClickListener
} }
//Too lazy to actually deal with orientation changes progress_dismiss_btn.clicks().subscribeUntilDestroy {
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR presenter.currentlyAddingRelay.call(false)
}
val splitGalleries = galleries.split("\n").map { val progressSubscriptions = CompositeSubscription()
it.trim().nullIfBlank()
}.filterNotNull()
val dialog = MaterialDialog.Builder(context) presenter.currentlyAddingRelay
.title("Adding galleries...") .observeOn(AndroidSchedulers.mainThread())
.progress(false, splitGalleries.size, true) .subscribeUntilDestroy {
.cancelable(false) progressSubscriptions.clear()
.canceledOnTouchOutside(false) 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() .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()
}
} }

View File

@ -1,5 +1,51 @@
package exh.ui.batchadd package exh.ui.batchadd
/** import com.jakewharton.rxrelay.BehaviorRelay
* Created by nulldev on 8/23/17. 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)
}
}
}

View File

@ -6,12 +6,11 @@ import android.view.MenuItem
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
import eu.kanade.tachiyomi.ui.manga.MangaActivity
import exh.GalleryAdder import exh.GalleryAdder
import kotlinx.android.synthetic.main.toolbar.*
import timber.log.Timber import timber.log.Timber
import kotlin.concurrent.thread import kotlin.concurrent.thread
//TODO :(
class InterceptActivity : BaseActivity() { class InterceptActivity : BaseActivity() {
private val galleryAdder = GalleryAdder() private val galleryAdder = GalleryAdder()
@ -19,12 +18,9 @@ class InterceptActivity : BaseActivity() {
var finished = false var finished = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setAppTheme()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.eh_activity_intercept) setContentView(R.layout.eh_activity_intercept)
setupToolbar(toolbar, backNavigation = false)
if(savedInstanceState == null) if(savedInstanceState == null)
thread { setup() } thread { setup() }
} }
@ -54,8 +50,9 @@ class InterceptActivity : BaseActivity() {
if(Intent.ACTION_VIEW == intent.action) { if(Intent.ACTION_VIEW == intent.action) {
val manga = galleryAdder.addGallery(intent.dataString) val manga = galleryAdder.addGallery(intent.dataString)
if(!finished) //TODO
startActivity(MangaActivity.newIntent(this, manga, true)) // if(!finished)
// startActivity(MangaActivity.newIntent(this, manga, true))
onBackPressed() onBackPressed()
} }
} }

View File

@ -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)
}
}

View 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())
}

View 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
}

View File

@ -17,7 +17,7 @@ import java.security.SecureRandom
class LockPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class LockPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
Preference(context, attrs) { Preference(context, attrs) {
val secureRandom by lazy { SecureRandom() } private val secureRandom by lazy { SecureRandom() }
val prefs: PreferencesHelper by injectLazy() val prefs: PreferencesHelper by injectLazy()
@ -26,12 +26,11 @@ class LockPreference @JvmOverloads constructor(context: Context, attrs: Attribut
updateSummary() updateSummary()
} }
fun updateSummary() { private fun updateSummary() {
if(lockEnabled(prefs)) { summary = if(lockEnabled(prefs))
summary = "Application is locked" "Application is locked"
} else { else
summary = "Application is not locked, tap to lock" "Application is not locked, tap to lock"
}
} }
override fun onClick() { 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 salt: String?
val hash: String? val hash: String?
val length: Int val length: Int

View File

@ -0,0 +1,6 @@
package exh.ui.lock
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
class LockPresenter: BasePresenter<LockController>()

View File

@ -44,13 +44,6 @@ fun lockEnabled(prefs: PreferencesHelper = Injekt.get())
&& prefs.lockSalt().get() != null && prefs.lockSalt().get() != null
&& prefs.lockLength().getOrDefault() != -1 && 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 * Check if the lock will function properly
* *

View File

@ -15,7 +15,6 @@ import eu.kanade.tachiyomi.source.online.all.EHentai
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
import exh.EXH_SOURCE_ID import exh.EXH_SOURCE_ID
import kotlinx.android.synthetic.main.eh_activity_login.* import kotlinx.android.synthetic.main.eh_activity_login.*
import kotlinx.android.synthetic.main.toolbar.*
import rx.Observable import rx.Observable
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
@ -34,13 +33,10 @@ class LoginActivity : BaseActivity() {
val sourceManager: SourceManager by injectLazy() val sourceManager: SourceManager by injectLazy()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setAppTheme()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.eh_activity_login) setContentView(R.layout.eh_activity_login)
setup() setup()
setupToolbar(toolbar, backNavigation = false)
} }
fun setup() { fun setup() {
@ -187,7 +183,7 @@ class LoginActivity : BaseActivity() {
document.getElementsByName('submit')[0].style.visibility = 'visible'; document.getElementsByName('submit')[0].style.visibility = 'visible';
document.querySelector('td[width="60%"][valign="top"]').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(".errorwrap"));
hide(document.querySelector('td[width="40%"][valign="top"]')); hide(document.querySelector('td[width="40%"][valign="top"]'));
@ -202,7 +198,7 @@ class LoginActivity : BaseActivity() {
hide(fd[2]); hide(fd[2]);
hide(child.querySelector('br')); hide(child.querySelector('br'));
var error = document.querySelector(".page > div > .borderwrap"); 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[0]);
hide(fh[1]); hide(fh[1]);
hide(document.querySelector("#gfooter")); hide(document.querySelector("#gfooter"));
@ -211,7 +207,7 @@ class LoginActivity : BaseActivity() {
e.style.color = "white"; e.style.color = "white";
}); });
var pc = document.querySelector(".postcolor"); var pc = document.querySelector(".postcolor");
if(pc !== null) pc.style.color = "#26353F"; if(pc != null) pc.style.color = "#26353F";
})() })()
""" """
} }

View File

@ -1,29 +1,29 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:background="@color/backgroundDark">
<com.andrognito.pinlockview.PinLockView <com.andrognito.pinlockview.PinLockView
android:id="@+id/pin_lock_view" android:id="@+id/pin_lock_view"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/indicator_dots"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent" /> app:layout_constraintTop_toBottomOf="@+id/indicator_dots" />
<com.andrognito.pinlockview.IndicatorDots <com.andrognito.pinlockview.IndicatorDots
android:id="@+id/indicator_dots" android:id="@+id/indicator_dots"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="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" android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@+id/pin_lock_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="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> </android.support.constraint.ConstraintLayout>

View File

@ -17,8 +17,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<include layout="@layout/toolbar"/>
</android.support.design.widget.AppBarLayout> </android.support.design.widget.AppBarLayout>
<FrameLayout <FrameLayout

View File

@ -17,8 +17,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<include layout="@layout/toolbar"/>
</android.support.design.widget.AppBarLayout> </android.support.design.widget.AppBarLayout>
<android.support.constraint.ConstraintLayout <android.support.constraint.ConstraintLayout

View File

@ -9,46 +9,121 @@
<android.support.constraint.ConstraintLayout <android.support.constraint.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:padding="16dp ">
<TextView <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_width="0dp"
android:layout_height="wrap_content" 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:textAppearance="@style/TextAppearance.Medium.Title"
android:layout_marginTop="16dp" android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginStart="16dp"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
android:layout_marginRight="16dp" /> app:layout_constraintTop_toTopOf="parent" />
<EditText <EditText
android:id="@+id/galleries_box"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:inputType="textMultiLine"
android:ems="10" android:ems="10"
android:id="@+id/galleries_box" 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:layout_marginTop="8dp" android:inputType="textMultiLine"
app:layout_constraintTop_toBottomOf="@+id/textView" android:visibility="gone"
app:layout_constraintLeft_toLeftOf="@+id/textView"
app:layout_constraintRight_toRightOf="@+id/textView"
app:layout_constraintBottom_toTopOf="@+id/btn_add_galleries" app:layout_constraintBottom_toTopOf="@+id/btn_add_galleries"
android:layout_marginBottom="8dp" app:layout_constraintLeft_toLeftOf="@+id/input_title_view"
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_constraintRight_toRightOf="@+id/input_title_view"
app:layout_constraintTop_toBottomOf="@+id/input_title_view" />
<Button <Button
android:text="Add Galleries" android:id="@+id/btn_add_galleries"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/btn_add_galleries" android:text="Add Galleries"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="16dp"
app:layout_constraintLeft_toLeftOf="@+id/galleries_box" app:layout_constraintLeft_toLeftOf="@+id/galleries_box"
app:layout_constraintRight_toRightOf="@+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.constraint.ConstraintLayout>
</android.support.design.widget.CoordinatorLayout> </android.support.design.widget.CoordinatorLayout>

View File

@ -101,48 +101,4 @@
<item>1</item> <item>1</item>
<item>2</item> <item>2</item>
</string-array> </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> </resources>

View File

@ -1,12 +1,12 @@
package eu.kanade.tachiyomi package eu.kanade.tachiyomi
import org.robolectric.RobolectricTestRunner //import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config //import org.robolectric.annotation.Config
import org.robolectric.manifest.AndroidManifest //import org.robolectric.manifest.AndroidManifest
class CustomRobolectricGradleTestRunner(klass: Class<*>) : RobolectricTestRunner(klass) { //class CustomRobolectricGradleTestRunner(klass: Class<*>) : RobolectricTestRunner(klass) {
override fun getAppManifest(config: Config): AndroidManifest { // override fun getAppManifest(config: Config): AndroidManifest {
return super.getAppManifest(config).apply { packageName = "eu.kanade.tachiyomi" } // return super.getAppManifest(config).apply { packageName = "eu.kanade.tachiyomi" }
} // }
} //}