mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +01:00 
			
		
		
		
	Migrate to Tachiyomi 6.1
Rewrite batch add UI Rewrite lock UI
This commit is contained in:
		| @@ -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"; | ||||
|                     })() | ||||
|                     """ | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user