diff --git a/app/build.gradle b/app/build.gradle index 0e86fc591..e17731d40 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -176,6 +176,8 @@ dependencies { implementation "com.mikepenz:aboutlibraries-core:7.1.0" implementation 'com.wdullaer:materialdatetimepicker:4.2.3' + implementation("io.coil-kt:coil:0.9.2") + playImplementation "com.google.firebase:firebase-core:17.2.2" playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1" diff --git a/app/src/main/assets/creators.json b/app/src/main/assets/creators.json new file mode 100644 index 000000000..b99f51e21 --- /dev/null +++ b/app/src/main/assets/creators.json @@ -0,0 +1,34 @@ +[ + { + "displayName": "Mikołaj Pich", + "githubUsername": "mklkj" + }, + { + "displayName": "Rafał Borcz", + "githubUsername": "Faierbel" + }, + { + "displayName": "Dominik Korsa", + "githubUsername": "dominik-korsa" + }, + { + "displayName": "Kacper Ziubryniewicz", + "githubUsername": "kapi2289" + }, + { + "displayName": "doteq", + "githubUsername": "doteq" + }, + { + "displayName": "Pavuloff", + "githubUsername": "pavuloff" + }, + { + "displayName": "Piotr Romanowski", + "githubUsername": "v0idzz" + }, + { + "displayName": "Dinolek", + "githubUsername": "Dinolek" + } +] diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt index 1bc4eb616..48e51e3ce 100644 --- a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt +++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.data import android.content.Context import android.content.SharedPreferences +import android.content.res.AssetManager import android.content.res.Resources import androidx.preference.PreferenceManager import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings @@ -58,6 +59,10 @@ internal class RepositoryModule { @Provides fun provideResources(context: Context): Resources = context.resources + @Singleton + @Provides + fun provideAssets(context: Context): AssetManager = context.assets + @Singleton @Provides fun provideSharedPref(context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/AppCreator.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/AppCreator.kt new file mode 100644 index 000000000..d67aa2a7f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/AppCreator.kt @@ -0,0 +1,3 @@ +package io.github.wulkanowy.data.pojos + +class AppCreator(val displayName: String, val githubUsername: String) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt new file mode 100644 index 000000000..095b37166 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt @@ -0,0 +1,20 @@ +package io.github.wulkanowy.data.repositories.appcreator + +import android.content.res.AssetManager +import com.google.gson.Gson +import io.github.wulkanowy.data.pojos.AppCreator +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AppCreatorRepository @Inject constructor(private val assets: AssetManager) { + fun getAppCreators(): Single> { + return Single.fromCallable> { + Gson().fromJson( + assets.open("creators.json").bufferedReader().use { it.readText() }, + Array::class.java + ).toList() + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt index 22276bdf9..d9430a47a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt @@ -16,6 +16,7 @@ import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import io.github.wulkanowy.R import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment import io.github.wulkanowy.ui.modules.about.license.LicenseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView @@ -42,6 +43,11 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView { Triple(getString(R.string.about_version), "${appInfo.versionName} (${appInfo.versionCode})", getCompatDrawable(R.drawable.ic_all_about)) } + override val creatorsRes: Triple? + get() = context?.run { + Triple(getString(R.string.about_creator), getString(R.string.about_creator_summary), getCompatDrawable(R.drawable.ic_about_creator)) + } + override val feedbackRes: Triple? get() = context?.run { Triple(getString(R.string.about_feedback), getString(R.string.about_feedback_summary), getCompatDrawable(R.drawable.ic_about_feedback)) @@ -143,6 +149,10 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView { (activity as? MainActivity)?.pushView(LicenseFragment.newInstance()) } + override fun openCreators() { + (activity as? MainActivity)?.pushView(CreatorFragment.newInstance()) + } + override fun openPrivacyPolicy() { context?.openInternetBrowser("https://wulkanowy.github.io/polityka-prywatnosci.html", ::showMessage) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt index 8087c9556..662939832 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt @@ -52,6 +52,11 @@ class AboutPresenter @Inject constructor( openLicenses() analytics.logEvent("about_open", "name" to "licenses") } + creatorsRes?.first -> { + Timber.i("Opening creators view") + openCreators() + analytics.logEvent("about_open", "name" to "creators") + } privacyRes?.first -> { Timber.i("Opening privacy page ") openPrivacyPolicy() @@ -65,6 +70,7 @@ class AboutPresenter @Inject constructor( view?.run { updateData(AboutScrollableHeader(), listOfNotNull( versionRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, + creatorsRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, feedbackRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, faqRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, discordRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt index 34850bae4..228225ec6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt @@ -7,6 +7,8 @@ interface AboutView : BaseView { val versionRes: Triple? + val creatorsRes: Triple? + val feedbackRes: Triple? val faqRes: Triple? @@ -33,5 +35,7 @@ interface AboutView : BaseView { fun openLicenses() + fun openCreators() + fun openPrivacyPolicy() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorFragment.kt new file mode 100644 index 000000000..299657ba6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorFragment.kt @@ -0,0 +1,76 @@ +package io.github.wulkanowy.ui.modules.about.creator + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.common.FlexibleItemDecoration +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.openInternetBrowser +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.fragment_creator.* +import javax.inject.Inject + +class CreatorFragment : BaseFragment(), CreatorView, MainView.TitledView { + + @Inject + lateinit var presenter: CreatorPresenter + + @Inject + lateinit var creatorsAdapter: FlexibleAdapter> + + override val titleStringId get() = R.string.creators_title + + companion object { + fun newInstance() = CreatorFragment() + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_creator, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.onAttachView(this) + } + + override fun initView() { + with(creatorRecycler) { + layoutManager = SmoothScrollLinearLayoutManager(context) + adapter = creatorsAdapter + addItemDecoration(FlexibleItemDecoration(context) + .withDefaultDivider() + .withDrawDividerOnLastItem(false)) + } + creatorsAdapter.setOnItemClickListener(presenter::onItemSelected) + creatorSeeMore.setOnClickListener { presenter.onSeeMoreClick() } + } + + override fun updateData(data: List) { + creatorsAdapter.updateDataSet(data) + } + + override fun openUserGithubPage(username: String) { + context?.openInternetBrowser("https://github.com/${username}", ::showMessage) + } + + override fun openGithubContributorsPage() { + context?.openInternetBrowser("https://github.com/wulkanowy/wulkanowy/graphs/contributors", ::showMessage) + } + + override fun showProgress(show: Boolean) { + creatorProgress.visibility = if (show) VISIBLE else GONE + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorItem.kt new file mode 100644 index 000000000..909919bbc --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorItem.kt @@ -0,0 +1,50 @@ +package io.github.wulkanowy.ui.modules.about.creator + +import android.view.View +import coil.api.load +import coil.transform.RoundedCornersTransformation +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.FlexibleViewHolder +import io.github.wulkanowy.R +import io.github.wulkanowy.data.pojos.AppCreator +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_creator.* + +class CreatorItem(val creator: AppCreator) : AbstractFlexibleItem() { + + override fun getLayoutRes() = R.layout.item_creator + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>) = ViewHolder(view, adapter) + + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList) { + with(holder) { + creatorItemName.text = creator.displayName + + creatorItemAvatar.load("https://github.com/${creator.githubUsername}.png") { + transformations(RoundedCornersTransformation(8f)) + crossfade(true) + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as CreatorItem + + if (creator != other.creator) return false + + return true + } + + override fun hashCode() = creator.hashCode() + + class ViewHolder(view: View, adapter: FlexibleAdapter>) : FlexibleViewHolder(view, adapter), + LayoutContainer { + + override val containerView: View? get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorPresenter.kt new file mode 100644 index 000000000..bc8034ab5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorPresenter.kt @@ -0,0 +1,43 @@ +package io.github.wulkanowy.ui.modules.about.creator + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.pojos.AppCreator +import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.SchedulersProvider +import io.reactivex.Single +import javax.inject.Inject + +class CreatorPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val appCreatorRepository: AppCreatorRepository +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: CreatorView) { + super.onAttachView(view) + view.initView() + loadData() + } + + fun onItemSelected(item: AbstractFlexibleItem<*>) { + if (item !is CreatorItem) return + view?.openUserGithubPage(item.creator.githubUsername) + } + + fun onSeeMoreClick() { + view?.openGithubContributorsPage() + } + + private fun loadData() { + disposable.add(appCreatorRepository.getAppCreators() + .map { it.map { creator -> CreatorItem(creator) } } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { view?.showProgress(false) } + .subscribe({ view?.run { updateData(it) } }, { errorHandler.dispatch(it) })) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorView.kt new file mode 100644 index 000000000..2a6a43039 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorView.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.ui.modules.about.creator + +import io.github.wulkanowy.ui.base.BaseView + +interface CreatorView : BaseView { + + fun initView() + + fun updateData(data: List) + + fun openUserGithubPage(username: String) + + fun openGithubContributorsPage() + + fun showProgress(show: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt index bbaea51ce..41f4d5f2c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt @@ -8,6 +8,7 @@ import dagger.android.ContributesAndroidInjector import io.github.wulkanowy.R import io.github.wulkanowy.di.scopes.PerFragment import io.github.wulkanowy.ui.modules.about.AboutFragment +import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment import io.github.wulkanowy.ui.modules.about.license.LicenseFragment import io.github.wulkanowy.ui.modules.about.license.LicenseModule import io.github.wulkanowy.ui.modules.account.AccountDialog @@ -121,6 +122,10 @@ abstract class MainModule { @ContributesAndroidInjector(modules = [LicenseModule::class]) abstract fun bindLicenseFragment(): LicenseFragment + @PerFragment + @ContributesAndroidInjector() + abstract fun bindCreatorsFragment(): CreatorFragment + @PerFragment @ContributesAndroidInjector(modules = [SchoolAndTeachersModule::class]) abstract fun bindSchoolAndTeachersFragment(): SchoolAndTeachersFragment diff --git a/app/src/main/res/drawable/ic_about_creator.xml b/app/src/main/res/drawable/ic_about_creator.xml new file mode 100644 index 000000000..c3daf609d --- /dev/null +++ b/app/src/main/res/drawable/ic_about_creator.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/fragment_creator.xml b/app/src/main/res/layout/fragment_creator.xml new file mode 100644 index 000000000..a0f34cffe --- /dev/null +++ b/app/src/main/res/layout/fragment_creator.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_creator.xml b/app/src/main/res/layout/item_creator.xml new file mode 100644 index 000000000..1a8d0285e --- /dev/null +++ b/app/src/main/res/layout/item_creator.xml @@ -0,0 +1,34 @@ + + + + + + + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index a59146974..458f64b45 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -12,6 +12,7 @@ Ustawienia Więcej O aplikacji + Twórcy Licencje Wiadomości Nowa wiadomość @@ -269,6 +270,8 @@ Wersja aplikacji + Twórcy + Lista programistów Wulkanowego Zgłoś błąd Wyślij zgłoszenie o błędzie poprzez e-maila FAQ @@ -287,6 +290,11 @@ Licencja + + Awatar + Zobacz więcej na GitHub + + Treść Ponów diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3ad60ee2c..8bdc54105 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,6 +12,7 @@ Settings More About + Creators Licenses Messages New message @@ -250,6 +251,8 @@ App version + Creators + List of Wulkanowy developers Report a bug Send a bug report via e-mail FAQ @@ -268,6 +271,11 @@ License + + Avatar + See more on GitHub + + Content Retry