1
0

Change app theme (#330)

This commit is contained in:
Rafał Borcz
2019-08-26 20:54:20 +02:00
committed by Mikołaj Pich
parent b0033af048
commit c6d9dfa0c9
231 changed files with 2225 additions and 1511 deletions

View File

@ -43,6 +43,7 @@
android:name=".ui.modules.message.send.SendMessageActivity"
android:configChanges="orientation|screenSize"
android:label="@string/send_message_title"
android:windowSoftInputMode="adjustResize"
android:theme="@style/WulkanowyTheme.NoActionBar" />
<activity
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"

View File

@ -13,6 +13,7 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.utils.Log
import io.github.wulkanowy.di.DaggerAppComponent
import io.github.wulkanowy.services.sync.SyncWorkerFactory
import io.github.wulkanowy.ui.base.ThemeManager
import io.github.wulkanowy.utils.ActivityLifecycleLogger
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.CrashlyticsTree
@ -29,6 +30,9 @@ class WulkanowyApp : DaggerApplication() {
@Inject
lateinit var workerFactory: SyncWorkerFactory
@Inject
lateinit var themeManager: ThemeManager
@Inject
lateinit var appInfo: AppInfo
@ -41,6 +45,7 @@ class WulkanowyApp : DaggerApplication() {
super.onCreate()
AndroidThreeTen.init(this)
RxJavaPlugins.setErrorHandler(::onError)
themeManager.applyDefaultTheme()
initWorkManager()
initLogging()

View File

@ -6,14 +6,15 @@ import io.github.wulkanowy.di.scopes.PerActivity
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.login.LoginModule
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainModule
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureActivity
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider
@Suppress("unused")
@Module
internal abstract class BuilderModule {

View File

@ -28,6 +28,7 @@ import io.github.wulkanowy.services.sync.works.Work
import io.github.wulkanowy.services.widgets.TimetableWidgetService
import javax.inject.Singleton
@Suppress("unused")
@AssistedModule
@Module(includes = [AssistedInject_ServicesModule::class])
abstract class ServicesModule {

View File

@ -64,7 +64,7 @@ class SyncWorker @AssistedInject constructor(
private fun notify(result: Result) {
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(applicationContext, DebugChannel.CHANNEL_ID)
.setContentTitle("Debug notification")
.setSmallIcon(R.drawable.ic_more_settings_24dp)
.setSmallIcon(R.drawable.ic_more_settings)
.setAutoCancel(true)
.setColor(applicationContext.getCompatColor(R.color.colorPrimary))
.setStyle(BigTextStyle().bigText("${SyncWorker::class.java.simpleName} result: $result"))

View File

@ -15,7 +15,7 @@ import io.github.wulkanowy.data.repositories.grade.GradeRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import javax.inject.Inject
@ -41,14 +41,14 @@ class GradeWork @Inject constructor(
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
.setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items, grades.size, grades.size))
.setContentText(context.resources.getQuantityString(R.plurals.grade_notify_new_items, grades.size, grades.size))
.setSmallIcon(R.drawable.ic_stat_notify_grade)
.setSmallIcon(R.drawable.ic_stat_grade)
.setAutoCancel(true)
.setPriority(PRIORITY_HIGH)
.setDefaults(DEFAULT_ALL)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(context, MenuView.GRADE.id,
MainActivity.getStartIntent(context, MenuView.GRADE, true), FLAG_UPDATE_CURRENT))
PendingIntent.getActivity(context, MainView.Section.GRADE.id,
MainActivity.getStartIntent(context, MainView.Section.GRADE, true), FLAG_UPDATE_CURRENT))
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, grades.size, grades.size))
grades.forEach { addLine("${it.subject}: ${it.entry}") }

View File

@ -15,7 +15,7 @@ import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import javax.inject.Inject
@ -41,14 +41,14 @@ class LuckyNumberWork @Inject constructor(
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
.setContentTitle(context.getString(R.string.lucky_number_notify_new_item_title))
.setContentText(context.getString(R.string.lucky_number_notify_new_item, luckyNumber.luckyNumber))
.setSmallIcon(R.drawable.ic_stat_notify_lucky_number)
.setSmallIcon(R.drawable.ic_stat_luckynumber)
.setAutoCancel(true)
.setDefaults(DEFAULT_ALL)
.setPriority(PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(context, MenuView.MESSAGE.id,
MainActivity.getStartIntent(context, MenuView.LUCKY_NUMBER, true), FLAG_UPDATE_CURRENT))
PendingIntent.getActivity(context, MainView.Section.MESSAGE.id,
MainActivity.getStartIntent(context, MainView.Section.LUCKY_NUMBER, true), FLAG_UPDATE_CURRENT))
.build())
}
}

View File

@ -16,7 +16,7 @@ import io.github.wulkanowy.data.repositories.message.MessageRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import javax.inject.Inject
@ -42,14 +42,14 @@ class MessageWork @Inject constructor(
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
.setContentTitle(context.resources.getQuantityString(R.plurals.message_new_items, messages.size, messages.size))
.setContentText(context.resources.getQuantityString(R.plurals.message_notify_new_items, messages.size, messages.size))
.setSmallIcon(R.drawable.ic_stat_notify_message)
.setSmallIcon(R.drawable.ic_stat_message)
.setAutoCancel(true)
.setDefaults(DEFAULT_ALL)
.setPriority(PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(context, MenuView.MESSAGE.id,
MainActivity.getStartIntent(context, MenuView.MESSAGE, true), FLAG_UPDATE_CURRENT)
PendingIntent.getActivity(context, MainView.Section.MESSAGE.id,
MainActivity.getStartIntent(context, MainView.Section.MESSAGE, true), FLAG_UPDATE_CURRENT)
)
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.message_number_item, messages.size, messages.size))

View File

@ -15,7 +15,7 @@ import io.github.wulkanowy.data.repositories.note.NoteRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import javax.inject.Inject
@ -41,14 +41,14 @@ class NoteWork @Inject constructor(
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
.setContentTitle(context.resources.getQuantityString(R.plurals.note_new_items, notes.size, notes.size))
.setContentText(context.resources.getQuantityString(R.plurals.note_notify_new_items, notes.size, notes.size))
.setSmallIcon(R.drawable.ic_stat_notify_note)
.setSmallIcon(R.drawable.ic_stat_note)
.setAutoCancel(true)
.setDefaults(DEFAULT_ALL)
.setPriority(PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(context, MenuView.NOTE.id,
MainActivity.getStartIntent(context, MenuView.NOTE, true), FLAG_UPDATE_CURRENT))
PendingIntent.getActivity(context, MainView.Section.NOTE.id,
MainActivity.getStartIntent(context, MainView.Section.NOTE, true), FLAG_UPDATE_CURRENT))
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size))
notes.forEach { addLine("${it.teacher}: ${it.category}") }

View File

@ -1,5 +1,8 @@
package io.github.wulkanowy.ui.base
import android.app.ActivityManager
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.LOLLIPOP
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
@ -16,6 +19,7 @@ import dagger.android.HasAndroidInjector
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.utils.FragmentLifecycleLogger
import io.github.wulkanowy.utils.getThemeAttrColor
import javax.inject.Inject
abstract class BaseActivity<T : BasePresenter<out BaseView>> : AppCompatActivity(), BaseView, HasAndroidInjector {
@ -35,10 +39,15 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>> : AppCompatActivity
public override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
themeManager.applyTheme(this)
themeManager.applyActivityTheme(this)
super.onCreate(savedInstanceState)
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true)
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
if (SDK_INT >= LOLLIPOP) {
@Suppress("DEPRECATION")
setTaskDescription(ActivityManager.TaskDescription(null, null, getThemeAttrColor(R.attr.colorSurface)))
}
}
override fun showError(text: String, error: Throwable) {

View File

@ -2,29 +2,31 @@ package io.github.wulkanowy.ui.base
import android.content.pm.PackageManager.GET_ACTIVITIES
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import io.github.wulkanowy.R
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ThemeManager @Inject constructor(private val preferencesRepository: PreferencesRepository) {
fun applyTheme(activity: AppCompatActivity) {
fun applyActivityTheme(activity: AppCompatActivity) {
if (isThemeApplicable(activity)) {
activity.delegate.apply {
when (preferencesRepository.appTheme) {
"light" -> setLocalNightMode(MODE_NIGHT_NO)
"dark" -> setLocalNightMode(MODE_NIGHT_YES)
"black" -> {
setLocalNightMode(MODE_NIGHT_YES)
activity.setTheme(R.style.WulkanowyTheme_Black)
}
}
}
applyDefaultTheme()
if (preferencesRepository.appTheme == "black") activity.setTheme(R.style.WulkanowyTheme_Black)
}
}
fun applyDefaultTheme() {
AppCompatDelegate.setDefaultNightMode(
if (preferencesRepository.appTheme == "light") MODE_NIGHT_NO
else MODE_NIGHT_YES
)
}
private fun isThemeApplicable(activity: AppCompatActivity): Boolean {
return activity.packageManager.getPackageInfo(activity.packageName, GET_ACTIVITIES)
.activities.singleOrNull { it.name == activity::class.java.canonicalName }?.theme

View File

@ -1,19 +1,29 @@
package io.github.wulkanowy.ui.modules.about
import android.content.Intent
import android.content.Intent.ACTION_SENDTO
import android.content.Intent.EXTRA_EMAIL
import android.content.Intent.EXTRA_SUBJECT
import android.content.Intent.EXTRA_TEXT
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.mikepenz.aboutlibraries.LibsBuilder
import com.mikepenz.aboutlibraries.LibsFragmentCompat
import eu.davidea.flexibleadapter.FlexibleAdapter
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.license.LicenseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.getCompatDrawable
import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.withOnExtraListener
import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_about.*
import javax.inject.Inject
class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
@ -22,63 +32,94 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
lateinit var presenter: AboutPresenter
@Inject
lateinit var fragmentCompat: LibsFragmentCompat
lateinit var aboutAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
@Inject
lateinit var appInfo: AppInfo
override val versionRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_version), "${appInfo.versionName} (${appInfo.versionCode})", getCompatDrawable(R.drawable.ic_all_about))
}
override val feedbackRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_feedback), getString(R.string.about_feedback_summary), getCompatDrawable(R.drawable.ic_about_feedback))
}
override val discordRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_discord), getString(R.string.about_discord_summary), getCompatDrawable(R.drawable.ic_about_discord))
}
override val homepageRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_homepage), getString(R.string.about_homepage_summary), getCompatDrawable(R.drawable.ic_about_homepage))
}
override val licensesRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_licenses), getString(R.string.about_licenses_summary), getCompatDrawable(R.drawable.ic_about_licenses))
}
override val privacyRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_privacy), getString(R.string.about_privacy_summary), getCompatDrawable(R.drawable.ic_about_privacy))
}
override val titleStringId get() = R.string.about_title
companion object {
fun newInstance() = AboutFragment()
}
override val titleStringId: Int
get() = R.string.about_title
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
presenter.onAttachView(this)
return Bundle().apply {
putSerializable("data", LibsBuilder()
.withAboutAppName(getString(R.string.app_name))
.withAboutVersionShown(true)
.withAboutIconShown(true)
.withLicenseShown(true)
.withAboutSpecial1(getString(R.string.about_discord_invite))
.withAboutSpecial2(getString(R.string.about_homepage))
.withAboutSpecial3(getString(R.string.about_feedback))
.withFields(R.string::class.java.fields)
.withCheckCachedDetection(false)
.withExcludedLibraries("fastadapter", "AndroidIconics", "Jsoup", "Retrofit", "okio",
"Butterknife", "CircleImageView")
.withOnExtraListener { presenter.onExtraSelect(it) })
}.let {
fragmentCompat.onCreateView(inflater.context, inflater, container, savedInstanceState, it)
}
return inflater.inflate(R.layout.fragment_about, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
fragmentCompat.onViewCreated(view, savedInstanceState)
presenter.onAttachView(this)
}
override fun openDiscordInviteView() {
override fun initView() {
aboutAdapter.setOnItemClickListener(presenter::onItemSelected)
with(aboutRecycler) {
layoutManager = SmoothScrollLinearLayoutManager(context)
adapter = aboutAdapter
}
}
override fun updateData(header: AboutScrollableHeader, items: List<AboutItem>) {
with(aboutAdapter) {
removeAllScrollableHeaders()
addScrollableHeader(header)
updateDataSet(items)
}
}
override fun openDiscordInvite() {
context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
}
override fun openHomepageWebView() {
override fun openHomepage() {
context?.openInternetBrowser("https://wulkanowy.github.io/", ::showMessage)
}
override fun openEmailClientView() {
val intent = Intent(Intent.ACTION_SENDTO).apply {
data = Uri.parse("mailto:")
putExtra(Intent.EXTRA_EMAIL, Array(1) { "wulkanowyinc@gmail.com" })
putExtra(Intent.EXTRA_SUBJECT, "Zgłoszenie błędu")
putExtra(Intent.EXTRA_TEXT, "Tu umieść treść zgłoszenia\n\n" + "-".repeat(40) + "\n" + """
Build: ${appInfo.versionCode}
SDK: ${appInfo.systemVersion}
Device: ${appInfo.systemManufacturer} ${appInfo.systemModel}
""".trimIndent())
}
override fun openEmailClient() {
val intent = Intent(ACTION_SENDTO)
.apply {
data = Uri.parse("mailto:")
putExtra(EXTRA_EMAIL, arrayOf("wulkanowyinc@gmail.com"))
putExtra(EXTRA_SUBJECT, "Zgłoszenie błędu")
putExtra(EXTRA_TEXT, "Tu umieść treść zgłoszenia\n\n${"-".repeat(40)}\n " +
"""
Build: ${appInfo.versionCode}
SDK: ${appInfo.systemVersion}
Device: ${appInfo.systemManufacturer} ${appInfo.systemModel}
""".trimIndent())
}
context?.let {
if (intent.resolveActivity(it.packageManager) != null) {
@ -89,8 +130,15 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
}
}
override fun openLicenses() {
(activity as? MainActivity)?.pushView(LicenseFragment.newInstance())
}
override fun openPrivacyPolicy() {
context?.openInternetBrowser("https://wulkanowy.github.io/polityka-prywatnosci.html", ::showMessage)
}
override fun onDestroyView() {
fragmentCompat.onDestroyView()
presenter.onDetachView()
super.onDestroyView()
}

View File

@ -0,0 +1,56 @@
package io.github.wulkanowy.ui.modules.about
import android.graphics.drawable.Drawable
import android.view.View
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 kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_about.*
class AboutItem(
val title: String,
private val summary: String,
private val image: Drawable?
) : AbstractFlexibleItem<AboutItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_about
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
with(holder) {
aboutItemImage.setImageDrawable(image)
aboutItemTitle.text = title
aboutItemSummary.text = summary
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as AboutItem
if (title != other.title) return false
if (summary != other.summary) return false
if (image != other.image) return false
return true
}
override fun hashCode(): Int {
var result = title.hashCode()
result = 31 * result + summary.hashCode()
result = 31 * result + (image?.hashCode() ?: 0)
return result
}
class ViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View? get() = contentView
}
}

View File

@ -1,14 +0,0 @@
package io.github.wulkanowy.ui.modules.about
import com.mikepenz.aboutlibraries.LibsFragmentCompat
import dagger.Module
import dagger.Provides
import io.github.wulkanowy.di.scopes.PerFragment
@Module
class AboutModule {
@PerFragment
@Provides
fun provideLibsFragmentCompat() = LibsFragmentCompat()
}

View File

@ -1,9 +1,6 @@
package io.github.wulkanowy.ui.modules.about
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL1
import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL2
import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL3
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
@ -21,28 +18,53 @@ class AboutPresenter @Inject constructor(
override fun onAttachView(view: AboutView) {
super.onAttachView(view)
view.initView()
Timber.i("About view was initialized")
loadData()
}
fun onExtraSelect(type: Libs.SpecialButton?) {
fun onItemSelected(item: AbstractFlexibleItem<*>) {
if (item !is AboutItem) return
view?.run {
when (type) {
SPECIAL1 -> {
Timber.i("Opening discord invide page")
analytics.logEvent("open_page", "name" to "discord")
openDiscordInviteView()
when (item.title) {
feedbackRes?.first -> {
Timber.i("Opening email client ")
openEmailClient()
analytics.logEvent("about_open", "name" to "feedback")
}
SPECIAL2 -> {
Timber.i("Opening home page")
analytics.logEvent("open_page", "name" to "home")
openHomepageWebView()
discordRes?.first -> {
Timber.i("Opening discord")
openDiscordInvite()
analytics.logEvent("about_open", "name" to "discord")
}
SPECIAL3 -> {
Timber.i("Opening email client")
analytics.logEvent("open_page", "name" to "email")
openEmailClientView()
homepageRes?.first -> {
Timber.i("Opening homepage")
openHomepage()
analytics.logEvent("about_open", "name" to "homepage")
}
licensesRes?.first -> {
Timber.i("Opening licenses view")
openLicenses()
analytics.logEvent("about_open", "name" to "licenses")
}
privacyRes?.first -> {
Timber.i("Opening privacy page ")
openPrivacyPolicy()
analytics.logEvent("about_open", "name" to "privacy")
}
}
}
}
private fun loadData() {
view?.run {
updateData(AboutScrollableHeader(), listOfNotNull(
versionRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
feedbackRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
discordRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
homepageRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
licensesRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
privacyRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }))
}
}
}

View File

@ -0,0 +1,41 @@
package io.github.wulkanowy.ui.modules.about
import android.view.View
import androidx.core.content.res.ResourcesCompat
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 kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.scrollable_header_about.*
class AboutScrollableHeader : AbstractFlexibleItem<AboutScrollableHeader.ViewHolder>() {
override fun getLayoutRes() = R.layout.scrollable_header_about
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
with(holder) {
val context = contentView.context
val drawable = ResourcesCompat.getDrawableForDensity(context.resources, context.applicationInfo.icon, 640, null)
aboutScrollableHeaderIcon.setImageDrawable(drawable)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
return true
}
override fun hashCode() = javaClass.hashCode()
class ViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View? get() = contentView
}
}

View File

@ -1,12 +1,33 @@
package io.github.wulkanowy.ui.modules.about
import android.graphics.drawable.Drawable
import io.github.wulkanowy.ui.base.BaseView
interface AboutView : BaseView {
fun openDiscordInviteView()
val versionRes: Triple<String, String, Drawable?>?
fun openEmailClientView()
val feedbackRes: Triple<String, String, Drawable?>?
fun openHomepageWebView()
val discordRes: Triple<String, String, Drawable?>?
val homepageRes: Triple<String, String, Drawable?>?
val licensesRes: Triple<String, String, Drawable?>?
val privacyRes: Triple<String, String, Drawable?>?
fun initView()
fun updateData(header: AboutScrollableHeader, items: List<AboutItem>)
fun openDiscordInvite()
fun openEmailClient()
fun openHomepage()
fun openLicenses()
fun openPrivacyPolicy()
}

View File

@ -0,0 +1,87 @@
package io.github.wulkanowy.ui.modules.about.license
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 androidx.appcompat.app.AlertDialog
import androidx.core.text.HtmlCompat
import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.entity.Library
import dagger.Lazy
import eu.davidea.flexibleadapter.FlexibleAdapter
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.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_license.*
import javax.inject.Inject
class LicenseFragment : BaseFragment(), LicenseView, MainView.TitledView {
@Inject
lateinit var presenter: LicensePresenter
@Inject
lateinit var licenseAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
@Inject
lateinit var libs: Lazy<Libs>
override val titleStringId get() = R.string.license_title
override val appLibraries: ArrayList<Library>?
get() = context?.let {
libs.get().prepareLibraries(it, emptyArray(), emptyArray(), autoDetect = true, checkCachedDetection = true, sort = true)
}
companion object {
fun newInstance() = LicenseFragment()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_license, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
presenter.onAttachView(this)
}
override fun initView() {
with(licenseRecycler) {
layoutManager = SmoothScrollLinearLayoutManager(context)
adapter = licenseAdapter
}
licenseAdapter.setOnItemClickListener(presenter::onItemSelected)
}
override fun updateData(data: List<LicenseItem>) {
licenseAdapter.updateDataSet(data)
}
override fun openLicense(licenseHtml: String) {
context?.let {
AlertDialog.Builder(it).apply {
setTitle(R.string.license_dialog_title)
setMessage(HtmlCompat.fromHtml(licenseHtml, FROM_HTML_MODE_LEGACY))
setPositiveButton(android.R.string.ok) { _, _ -> }
show()
}
}
}
override fun showProgress(show: Boolean) {
licenseProgress.visibility = if (show) VISIBLE else GONE
}
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
}
}

View File

@ -0,0 +1,44 @@
package io.github.wulkanowy.ui.modules.about.license
import android.view.View
import com.mikepenz.aboutlibraries.entity.Library
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 kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_license.*
class LicenseItem(val library: Library) : AbstractFlexibleItem<LicenseItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_license
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
with(holder) {
licenseItemName.text = library.libraryName
licenseItemSummary.text = library.license?.licenseName
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as LicenseItem
if (library != other.library) return false
return true
}
override fun hashCode() = library.hashCode()
class ViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View? get() = contentView
}
}

View File

@ -0,0 +1,15 @@
package io.github.wulkanowy.ui.modules.about.license
import android.content.Context
import com.mikepenz.aboutlibraries.Libs
import dagger.Module
import dagger.Provides
import io.github.wulkanowy.di.scopes.PerFragment
@Module
class LicenseModule {
@PerFragment
@Provides
fun provideLibs(context: Context) = Libs(context)
}

View File

@ -0,0 +1,40 @@
package io.github.wulkanowy.ui.modules.about.license
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
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 LicensePresenter @Inject constructor(
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository
) : BasePresenter<LicenseView>(errorHandler, studentRepository, schedulers) {
override fun onAttachView(view: LicenseView) {
super.onAttachView(view)
view.initView()
loadData()
}
fun onItemSelected(item: AbstractFlexibleItem<*>) {
if (item !is LicenseItem) return
view?.run { item.library.license?.licenseDescription?.let { openLicense(it) } }
}
private fun loadData() {
disposable.add(Single.fromCallable { view?.appLibraries }
.map {
val exclude = listOf("Android-Iconics", "CircleImageView", "FastAdapter", "Jsoup", "okio", "Retrofit")
it.filter { library -> !exclude.contains(library.libraryName) }
}
.map { it.map { library -> LicenseItem(library) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnEvent { _, _ -> view?.showProgress(false) }
.subscribe({ view?.run { updateData(it) } }, { errorHandler.dispatch(it) }))
}
}

View File

@ -0,0 +1,17 @@
package io.github.wulkanowy.ui.modules.about.license
import com.mikepenz.aboutlibraries.entity.Library
import io.github.wulkanowy.ui.base.BaseView
interface LicenseView : BaseView {
val appLibraries: ArrayList<Library>?
fun initView()
fun updateData(data: List<LicenseItem>)
fun openLicense(licenseHtml: String)
fun showProgress(show: Boolean)
}

View File

@ -60,9 +60,7 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
}
override fun initView() {
attendanceAdapter.apply {
setOnItemClickListener { presenter.onAttendanceItemSelected(it) }
}
attendanceAdapter.setOnItemClickListener { presenter.onAttendanceItemSelected(it) }
attendanceRecycler.run {
layoutManager = SmoothScrollLinearLayoutManager(context)

View File

@ -78,6 +78,7 @@ class GradeFragment : BaseFragment(), GradeView, MainView.MainChildView, MainVie
offscreenPageLimit = 3
setOnSelectPageListener { presenter.onPageSelected(it) }
}
gradeTabLayout.setupWithViewPager(gradeViewPager)
gradeSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
}

View File

@ -10,6 +10,7 @@ import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment
import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment
import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment
@Suppress("unused")
@Module
abstract class GradeModule {

View File

@ -81,9 +81,7 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary))
animateXY(1000, 1000)
minAngleForSlices = 25f
legend.apply {
textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary)
}
legend.textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary)
}
context?.let {

View File

@ -10,6 +10,7 @@ import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment
import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment
import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
@Suppress("unused")
@Module
internal abstract class LoginModule {

View File

@ -17,7 +17,6 @@ import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.hideSoftInput
import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.setOnItemSelectedListener
import io.github.wulkanowy.utils.setOnTextChangedListener
import io.github.wulkanowy.utils.showSoftInput
import kotlinx.android.synthetic.main.fragment_login_form.*
@ -42,7 +41,11 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
get() = loginFormPass.text.toString()
override val formHostValue: String?
get() = resources.getStringArray(R.array.endpoints_values)[loginFormHost.selectedItemPosition]
get() = hostValues.getOrNull(hostKeys.indexOf(loginFormHost.text.toString()))
private lateinit var hostKeys: Array<String>
private lateinit var hostValues: Array<String>
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_login_form, container, false)
@ -54,9 +57,12 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
}
override fun initView() {
hostKeys = resources.getStringArray(R.array.endpoints_keys)
hostValues = resources.getStringArray(R.array.endpoints_values)
loginFormName.setOnTextChangedListener { presenter.onNameTextChanged() }
loginFormPass.setOnTextChangedListener { presenter.onPassTextChanged() }
loginFormHost.setOnItemSelectedListener { presenter.onHostSelected() }
loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() }
loginFormSignIn.setOnClickListener { presenter.onSignInClick() }
loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() }
@ -64,13 +70,14 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
if (id == IME_ACTION_DONE || id == IME_NULL) loginFormSignIn.callOnClick() else false
}
context?.let {
loginFormHost.adapter = ArrayAdapter.createFromResource(it, R.array.endpoints_keys, android.R.layout.simple_spinner_item)
.apply { setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) }
with(loginFormHost) {
setText(hostKeys.getOrElse(0) { "" })
setAdapter(ArrayAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys))
keyListener = null
}
}
override fun setDefaultCredentials(name: String, pass: String) {
override fun setCredentials(name: String, pass: String) {
loginFormName.setText(name)
loginFormPass.setText(pass)
}
@ -143,7 +150,7 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
(activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple(
loginFormName.text.toString(),
loginFormPass.text.toString(),
resources.getStringArray(R.array.endpoints_values)[loginFormHost.selectedItemPosition]
resources.getStringArray(R.array.endpoints_values)[1]
))
}

View File

@ -40,7 +40,7 @@ class LoginFormPresenter @Inject constructor(
view?.apply {
clearPassError()
clearNameError()
if (formHostValue?.contains("fakelog") == true) setDefaultCredentials("jan@fakelog.cf", "jan123")
if (formHostValue?.contains("fakelog") == true) setCredentials("jan@fakelog.cf", "jan123")
}
}

View File

@ -13,7 +13,7 @@ interface LoginFormView : BaseView {
val formHostValue: String?
fun setDefaultCredentials(name: String, pass: String)
fun setCredentials(name: String, pass: String)
fun setErrorNameRequired()

View File

@ -48,7 +48,7 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginS
get() = itemView
init {
loginItemCheck.setOnClickListener { super.onClick(loginItemContainer) }
loginItemCheck.keyListener = null
}
override fun onClick(view: View?) {

View File

@ -27,7 +27,7 @@ import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Maybe
import timber.log.Timber
@ -74,8 +74,8 @@ class LuckyNumberWidgetProvider : BroadcastReceiver() {
getLuckyNumber(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId)?.luckyNumber?.toString() ?: "#"
)
setOnClickPendingIntent(R.id.luckyNumberWidgetContainer,
PendingIntent.getActivity(context, MenuView.LUCKY_NUMBER.id,
MainActivity.getStartIntent(context, MenuView.LUCKY_NUMBER, true), FLAG_UPDATE_CURRENT))
PendingIntent.getActivity(context, MainView.Section.LUCKY_NUMBER.id,
MainActivity.getStartIntent(context, MainView.Section.LUCKY_NUMBER, true), FLAG_UPDATE_CURRENT))
}.also {
setStyles(it, intent)
appWidgetManager.updateAppWidget(appWidgetId, it)

View File

@ -4,16 +4,21 @@ import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.LOLLIPOP
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.core.view.ViewCompat
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import com.aurelhubert.ahbottomnavigation.AHBottomNavigation.TitleState.ALWAYS_SHOW
import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem
import com.google.android.material.elevation.ElevationOverlayProvider
import com.ncapdevi.fragnav.FragNavController
import com.ncapdevi.fragnav.FragNavController.Companion.HIDE
import dagger.Lazy
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.modules.account.AccountDialog
@ -26,8 +31,9 @@ import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.more.MoreFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.safelyPopFragment
import io.github.wulkanowy.utils.safelyPopFragments
import io.github.wulkanowy.utils.setOnViewChangeListener
import kotlinx.android.synthetic.main.activity_main.*
import javax.inject.Inject
@ -40,10 +46,13 @@ class MainActivity : BaseActivity<MainPresenter>(), MainView {
@Inject
lateinit var navController: FragNavController
@Inject
lateinit var overlayProvider: Lazy<ElevationOverlayProvider>
companion object {
const val EXTRA_START_MENU = "extraStartMenu"
fun getStartIntent(context: Context, startMenu: MainView.MenuView? = null, clear: Boolean = false): Intent {
fun getStartIntent(context: Context, startMenu: MainView.Section? = null, clear: Boolean = false): Intent {
return Intent(context, MainActivity::class.java)
.apply {
if (clear) flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
@ -52,24 +61,21 @@ class MainActivity : BaseActivity<MainPresenter>(), MainView {
}
}
override val isRootView: Boolean
get() = navController.isRootFragment
override val isRootView get() = navController.isRootFragment
override val currentViewTitle: String?
get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId?.let { getString(it) }
override val currentStackSize get() = navController.currentStack?.size
override val currentStackSize: Int?
get() = navController.currentStack?.size
override val currentViewTitle get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId?.let { getString(it) }
override var startMenuIndex = 0
override var startMenuMoreIndex = -1
private val moreMenuFragments = listOf<Fragment>(
MessageFragment.newInstance(),
HomeworkFragment.newInstance(),
NoteFragment.newInstance(),
LuckyNumberFragment.newInstance()
private val moreMenuFragments = mapOf<Int, Fragment>(
MainView.Section.MESSAGE.id to MessageFragment.newInstance(),
MainView.Section.HOMEWORK.id to HomeworkFragment.newInstance(),
MainView.Section.NOTE.id to NoteFragment.newInstance(),
MainView.Section.LUCKY_NUMBER.id to LuckyNumberFragment.newInstance()
)
override fun onCreate(savedInstanceState: Bundle?) {
@ -78,11 +84,11 @@ class MainActivity : BaseActivity<MainPresenter>(), MainView {
setSupportActionBar(mainToolbar)
messageContainer = mainFragmentContainer
presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_START_MENU) as? MainView.MenuView)
presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_START_MENU) as? MainView.Section)
navController.run {
with(navController) {
initialize(startMenuIndex, savedInstanceState)
pushFragment(moreMenuFragments.getOrNull(startMenuMoreIndex))
pushFragment(moreMenuFragments[startMenuMoreIndex])
}
}
@ -92,30 +98,31 @@ class MainActivity : BaseActivity<MainPresenter>(), MainView {
}
override fun initView() {
mainBottomNav.run {
addItems(
listOf(
AHBottomNavigationItem(R.string.grade_title, R.drawable.ic_menu_main_grade_26dp, 0),
AHBottomNavigationItem(R.string.attendance_title, R.drawable.ic_menu_main_attendance_24dp, 0),
AHBottomNavigationItem(R.string.exam_title, R.drawable.ic_menu_main_exam_24dp, 0),
AHBottomNavigationItem(R.string.timetable_title, R.drawable.ic_menu_main_timetable_24dp, 0),
AHBottomNavigationItem(R.string.more_title, R.drawable.ic_menu_main_more_24dp, 0)
)
)
accentColor = ContextCompat.getColor(context, R.color.colorPrimary)
inactiveColor = getThemeAttrColor(android.R.attr.textColorSecondary)
defaultBackgroundColor = getThemeAttrColor(R.attr.bottomNavBackground)
with(mainToolbar) {
if (SDK_INT >= LOLLIPOP) stateListAnimator = null
setBackgroundColor(overlayProvider.get().getSurfaceColorWithOverlayIfNeeded(dpToPx(4f)))
}
with(mainBottomNav) {
addItems(listOf(
AHBottomNavigationItem(R.string.grade_title, R.drawable.ic_main_grade, 0),
AHBottomNavigationItem(R.string.attendance_title, R.drawable.ic_main_attendance, 0),
AHBottomNavigationItem(R.string.exam_title, R.drawable.ic_main_exam, 0),
AHBottomNavigationItem(R.string.timetable_title, R.drawable.ic_main_timetable, 0),
AHBottomNavigationItem(R.string.more_title, R.drawable.ic_main_more, 0)
))
accentColor = getThemeAttrColor(R.attr.colorPrimary)
inactiveColor = ColorUtils.setAlphaComponent(getThemeAttrColor(R.attr.colorOnSurface), 153)
defaultBackgroundColor = overlayProvider.get().getSurfaceColorWithOverlayIfNeeded(dpToPx(8f))
titleState = ALWAYS_SHOW
currentItem = startMenuIndex
isBehaviorTranslationEnabled = false
setTitleTextSizeInSp(10f, 10f)
setOnTabSelectedListener { position, wasSelected ->
presenter.onTabSelected(position, wasSelected)
}
setOnTabSelectedListener(presenter::onTabSelected)
}
navController.run {
setOnViewChangeListener { presenter.onViewChange() }
with(navController) {
setOnViewChangeListener(presenter::onViewChange)
fragmentHideStrategy = HIDE
rootFragments = listOf(
GradeFragment.newInstance(),
@ -152,6 +159,10 @@ class MainActivity : BaseActivity<MainPresenter>(), MainView {
navController.showDialogFragment(AccountDialog.newInstance())
}
override fun showActionBarElevation(show: Boolean) {
ViewCompat.setElevation(mainToolbar, if (show) dpToPx(4f) else 0f)
}
override fun notifyMenuViewReselected() {
(navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentReselected()
}
@ -164,8 +175,8 @@ class MainActivity : BaseActivity<MainPresenter>(), MainView {
navController.pushFragment(fragment)
}
override fun popView() {
navController.safelyPopFragment()
override fun popView(depth: Int) {
navController.safelyPopFragments(depth)
}
override fun onBackPressed() {

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.main
import com.google.android.material.elevation.ElevationOverlayProvider
import com.ncapdevi.fragnav.FragNavController
import dagger.Module
import dagger.Provides
@ -7,7 +8,8 @@ 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.AboutModule
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
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment
@ -19,15 +21,16 @@ import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.message.MessageModule
import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment
import io.github.wulkanowy.ui.modules.mobiledevice.token.MobileDeviceTokenDialog
import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment
import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceModule
import io.github.wulkanowy.ui.modules.mobiledevice.token.MobileDeviceTokenDialog
import io.github.wulkanowy.ui.modules.more.MoreFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.settings.SettingsFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment
@Suppress("unused")
@Module
abstract class MainModule {
@ -39,6 +42,11 @@ abstract class MainModule {
fun provideFragNavController(activity: MainActivity): FragNavController {
return FragNavController(activity.supportFragmentManager, R.id.mainFragmentContainer)
}
//In activities must be injected as Lazy
@JvmStatic
@Provides
fun provideElevationOverlayProvider(activity: MainActivity) = ElevationOverlayProvider(activity)
}
@PerFragment
@ -74,7 +82,7 @@ abstract class MainModule {
abstract fun bindTimetableFragment(): TimetableFragment
@PerFragment
@ContributesAndroidInjector(modules = [AboutModule::class])
@ContributesAndroidInjector
abstract fun bindAboutFragment(): AboutFragment
@PerFragment
@ -108,4 +116,8 @@ abstract class MainModule {
@PerFragment
@ContributesAndroidInjector
abstract fun bindMobileDeviceDialog(): MobileDeviceTokenDialog
@PerFragment
@ContributesAndroidInjector(modules = [LicenseModule::class])
abstract fun bindLicenseFragment(): LicenseFragment
}

View File

@ -5,6 +5,8 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.main.MainView.Section.GRADE
import io.github.wulkanowy.ui.modules.main.MainView.Section.MESSAGE
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber
@ -19,7 +21,7 @@ class MainPresenter @Inject constructor(
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<MainView>(errorHandler, studentRepository, schedulers) {
fun onAttachView(view: MainView, initMenu: MainView.MenuView?) {
fun onAttachView(view: MainView, initMenu: MainView.Section?) {
super.onAttachView(view)
view.apply {
getProperViewIndexes(initMenu).let { (main, more) ->
@ -34,8 +36,9 @@ class MainPresenter @Inject constructor(
analytics.logEvent("app_open", "destination" to initMenu?.name)
}
fun onViewChange() {
fun onViewChange(section: MainView.Section?) {
view?.apply {
showActionBarElevation(section != GRADE && section != MESSAGE)
currentViewTitle?.let { setViewTitle(it) }
currentStackSize?.let {
if (it > 1) showHomeArrow(true)
@ -77,10 +80,10 @@ class MainPresenter @Inject constructor(
} == true
}
private fun getProperViewIndexes(initMenu: MainView.MenuView?): Pair<Int, Int> {
return when {
initMenu?.id in 0..3 -> initMenu!!.id to -1
(initMenu?.id ?: 0) > 3 -> 4 to initMenu!!.id - 4
private fun getProperViewIndexes(initMenu: MainView.Section?): Pair<Int, Int> {
return when (initMenu?.id) {
in 0..3 -> initMenu!!.id to -1
in 4..10 -> 4 to initMenu!!.id
else -> prefRepository.startMenuIndex to -1
}
}

View File

@ -22,11 +22,13 @@ interface MainView : BaseView {
fun showAccountPicker()
fun showActionBarElevation(show: Boolean)
fun notifyMenuViewReselected()
fun setViewTitle(title: String)
fun popView()
fun popView(depth: Int = 1)
interface MainChildView {
@ -38,14 +40,17 @@ interface MainView : BaseView {
val titleStringId: Int
}
enum class MenuView(val id: Int) {
enum class Section(val id: Int) {
GRADE(0),
ATTENDANCE(1),
EXAM(2),
TIMETABLE(3),
MESSAGE(4),
HOMEWORK(5),
NOTE(6),
LUCKY_NUMBER(7),
MORE(4),
MESSAGE(5),
HOMEWORK(6),
NOTE(7),
LUCKY_NUMBER(8),
SETTINGS(9),
ABOUT(10)
}
}

View File

@ -8,6 +8,7 @@ import io.github.wulkanowy.di.scopes.PerFragment
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment
@Suppress("unused")
@Module
abstract class MessageModule {

View File

@ -1,30 +0,0 @@
package io.github.wulkanowy.ui.modules.message.send
import android.graphics.drawable.Drawable
import android.net.Uri
import com.pchmn.materialchips.model.ChipInterface
import io.github.wulkanowy.data.db.entities.Recipient
class RecipientChip(var recipient: Recipient) : ChipInterface {
override fun getAvatarDrawable(): Drawable? = null
override fun getAvatarUri(): Uri? = null
override fun getId(): Any = recipient.id
override fun getLabel(): String = recipient.name
override fun getInfo(): String {
return recipient.realName.run {
substringBeforeLast("-").let { sub ->
when {
(sub == this) -> this
(sub.indexOf('(') != -1) -> indexOf("(").let { substring(if (it != -1) it else 0) }
(sub.indexOf('[') != -1) -> indexOf("[").let { substring(if (it != -1) it else 0) }
else -> substringAfter('-')
}
}.trim()
}
}
}

View File

@ -0,0 +1,14 @@
package io.github.wulkanowy.ui.modules.message.send
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.materialchipsinput.ChipItem
data class RecipientChipItem(
override val title: String,
override val summary: String,
val recipient: Recipient
) : ChipItem

View File

@ -2,18 +2,20 @@ package io.github.wulkanowy.ui.modules.message.send
import android.content.Context
import android.content.Intent
import android.graphics.Rect
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.TouchDelegate
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.hideSoftInput
import io.github.wulkanowy.utils.showSoftInput
import kotlinx.android.synthetic.main.activity_send_message.*
@ -38,14 +40,18 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter>(), SendMessageVie
}
}
override val formRecipientsData: List<Recipient>
get() = (sendMessageRecipientsInput.selectedChipList).map { (it as RecipientChip).recipient }
override val isDropdownListVisible: Boolean
get() = sendMessageTo.isDropdownListVisible
@Suppress("UNCHECKED_CAST")
override val formRecipientsData: List<RecipientChipItem>
get() = sendMessageTo.addedChipItems as List<RecipientChipItem>
override val formSubjectValue: String
get() = sendMessageSubjectInput.text.toString()
get() = sendMessageSubject.text.toString()
override val formContentValue: String
get() = sendMessageContentInput.text.toString()
get() = sendMessageMessageContent.text.toString()
override val messageRequiredRecipients: String
get() = getString(R.string.message_required_recipients)
@ -66,6 +72,12 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter>(), SendMessageVie
presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, intent.getSerializableExtra(EXTRA_REPLY) as? Boolean)
}
override fun initView() {
setUpExtendedHitArea()
sendMessageScroll.setOnTouchListener { _, _ -> presenter.onTouchScroll() }
sendMessageTo.onTextChangeListener = presenter::onRecipientsTextChange
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.action_menu_send_message, menu)
return true
@ -81,15 +93,15 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter>(), SendMessageVie
}
override fun setReportingUnit(unit: ReportingUnit) {
sendMessageFromTextView.setText(unit.senderName)
sendMessageFrom.text = unit.senderName
}
override fun setRecipients(recipients: List<Recipient>) {
sendMessageRecipientsInput.filterableList = recipients.map { RecipientChip(it) }
override fun setRecipients(recipients: List<RecipientChipItem>) {
sendMessageTo.filterableChipItems = recipients
}
override fun setSelectedRecipients(recipients: List<Recipient>) {
recipients.map { sendMessageRecipientsInput.addChip(RecipientChip(it)) }
override fun setSelectedRecipients(recipients: List<RecipientChipItem>) {
sendMessageTo.addChips(recipients)
}
override fun showProgress(show: Boolean) {
@ -109,11 +121,11 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter>(), SendMessageVie
}
override fun setSubject(subject: String) {
sendMessageSubjectInput.setText(subject)
sendMessageSubject.setText(subject)
}
override fun setContent(content: String) {
sendMessageContentInput.setText(content)
sendMessageMessageContent.setText(content)
}
override fun showMessage(text: String) {
@ -124,7 +136,41 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter>(), SendMessageVie
if (show) showSoftInput() else hideSoftInput()
}
override fun hideDropdownList() {
sendMessageTo.hideDropdownList()
}
override fun scrollToRecipients() {
sendMessageScroll.post {
sendMessageScroll.scrollTo(0, sendMessageTo.bottom - dpToPx(53f).toInt())
}
}
override fun popView() {
onBackPressed()
}
private fun setUpExtendedHitArea() {
fun extendHitArea() {
val containerHitRect = Rect().apply {
sendMessageContent.getHitRect(this)
}
val contentHitRect = Rect().apply {
sendMessageMessageContent.getHitRect(this)
}
contentHitRect.top = contentHitRect.bottom
contentHitRect.bottom = containerHitRect.bottom
sendMessageContent.touchDelegate = TouchDelegate(contentHitRect, sendMessageMessageContent)
}
sendMessageMessageContent.post {
sendMessageMessageContent.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
extendHitArea()
}
extendHitArea()
}
}
}

View File

@ -32,6 +32,7 @@ class SendMessagePresenter @Inject constructor(
fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean?) {
super.onAttachView(view)
view.initView()
Timber.i("Send message view was initialized")
loadData(message, reply)
view.apply {
@ -54,15 +55,47 @@ class SendMessagePresenter @Inject constructor(
}
}
fun onTouchScroll(): Boolean {
return view?.run {
if (isDropdownListVisible) {
hideDropdownList()
true
} else false
} == true
}
fun onRecipientsTextChange(text: String) {
if (text.isBlank()) return
view?.scrollToRecipients()
}
fun onUpNavigate(): Boolean {
view?.popView()
return true
}
fun onSend(): Boolean {
view?.run {
when {
formRecipientsData.isEmpty() -> showMessage(messageRequiredRecipients)
formContentValue.length < 3 -> showMessage(messageContentMinLength)
else -> {
sendMessage(
subject = formSubjectValue,
content = formContentValue,
recipients = formRecipientsData.map { it.recipient }
)
return true
}
}
}
return false
}
private fun loadData(message: Message?, reply: Boolean?) {
var reportingUnit: ReportingUnit? = null
var recipients: List<Recipient> = emptyList()
var selectedRecipient: List<Recipient> = emptyList()
var recipientChips: List<RecipientChipItem> = emptyList()
var selectedRecipientChips: List<RecipientChipItem> = emptyList()
Timber.i("Loading recipients started")
disposable.add(studentRepository.getCurrentStudent()
@ -73,14 +106,14 @@ class SendMessagePresenter @Inject constructor(
.flatMap { recipientRepository.getRecipients(student, 2, it).toMaybe() }
.doOnSuccess {
Timber.i("Loading recipients result: Success, fetched %d recipients", it.size)
recipients = it
recipientChips = createChips(it)
}
.flatMapCompletable {
if (message == null || reply != true) Completable.complete()
else recipientRepository.getMessageRecipients(student, message)
.doOnSuccess {
Timber.i("Loaded message recipients to reply result: Success, fetched %d recipients", it.size)
selectedRecipient = it
selectedRecipientChips = createChips(it)
}
.ignoreElement()
}
@ -95,11 +128,11 @@ class SendMessagePresenter @Inject constructor(
}
.doFinally { view?.run { showProgress(false) } }
.subscribe({
view?.apply {
view?.run {
if (reportingUnit !== null) {
reportingUnit?.let { setReportingUnit(it) }
setRecipients(recipients)
if (selectedRecipient.isNotEmpty()) setSelectedRecipients(selectedRecipient)
setRecipients(recipientChips)
if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients(selectedRecipientChips)
showContent(true)
} else {
Timber.e("Loading recipients result: Can't find the reporting unit")
@ -145,21 +178,29 @@ class SendMessagePresenter @Inject constructor(
)
}
fun onSend(): Boolean {
view?.run {
when {
formRecipientsData.isEmpty() -> showMessage(messageRequiredRecipients)
formContentValue.length < 3 -> showMessage(messageContentMinLength)
else -> {
sendMessage(
subject = formSubjectValue,
content = formContentValue,
recipients = formRecipientsData
)
return true
private fun createChips(recipients: List<Recipient>): List<RecipientChipItem> {
fun generateCorrectSummary(recipientRealName: String): String {
val substring = recipientRealName.substringBeforeLast("-")
return when {
substring == recipientRealName -> recipientRealName
substring.indexOf("(") != -1 -> {
recipientRealName.indexOf("(")
.let { recipientRealName.substring(if (it != -1) it else 0) }
}
}
substring.indexOf("[") != -1 -> {
recipientRealName.indexOf("[")
.let { recipientRealName.substring(if (it != -1) it else 0) }
}
else -> recipientRealName.substringAfter("-")
}.trim()
}
return recipients.map {
RecipientChipItem(
title = it.name,
summary = generateCorrectSummary(it.realName),
recipient = it
)
}
return false
}
}

View File

@ -1,12 +1,13 @@
package io.github.wulkanowy.ui.modules.message.send
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.ui.base.BaseView
interface SendMessageView : BaseView {
val formRecipientsData: List<Recipient>
val isDropdownListVisible: Boolean
val formRecipientsData: List<RecipientChipItem>
val formSubjectValue: String
@ -18,11 +19,13 @@ interface SendMessageView : BaseView {
val messageSuccess: String
fun initView()
fun setReportingUnit(unit: ReportingUnit)
fun setRecipients(recipients: List<Recipient>)
fun setRecipients(recipients: List<RecipientChipItem>)
fun setSelectedRecipients(recipients: List<Recipient>)
fun setSelectedRecipients(recipients: List<RecipientChipItem>)
fun showProgress(show: Boolean)
@ -38,5 +41,9 @@ interface SendMessageView : BaseView {
fun showSoftInput(show: Boolean)
fun hideDropdownList()
fun scrollToRecipients()
fun popView()
}

View File

@ -5,7 +5,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
@ -20,6 +19,7 @@ import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.settings.SettingsFragment
import io.github.wulkanowy.utils.getCompatDrawable
import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_more.*
import javax.inject.Inject
@ -39,60 +39,26 @@ class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.Mai
override val titleStringId: Int
get() = R.string.more_title
override val messagesRes: Pair<String, Drawable?>?
get() {
return context?.run {
getString(R.string.message_title) to
ContextCompat.getDrawable(this, R.drawable.ic_more_messages_24dp)
}
}
get() = context?.run { getString(R.string.message_title) to getCompatDrawable(R.drawable.ic_more_messages) }
override val homeworkRes: Pair<String, Drawable?>?
get() {
return context?.run {
getString(R.string.homework_title) to ContextCompat.getDrawable(this, R.drawable.ic_menu_main_homework_24dp)
}
}
get() = context?.run { getString(R.string.homework_title) to getCompatDrawable(R.drawable.ic_more_homework) }
override val noteRes: Pair<String, Drawable?>?
get() {
return context?.run {
getString(R.string.note_title) to ContextCompat.getDrawable(this, R.drawable.ic_menu_main_note_24dp)
}
}
get() = context?.run { getString(R.string.note_title) to getCompatDrawable(R.drawable.ic_more_note) }
override val luckyNumberRes: Pair<String, Drawable?>?
get() {
return context?.run {
getString(R.string.lucky_number_title) to
ContextCompat.getDrawable(this, R.drawable.ic_more_lucky_number_24dp)
}
}
get() = context?.run { getString(R.string.lucky_number_title) to getCompatDrawable(R.drawable.ic_more_lucky_number) }
override val mobileDevicesRes: Pair<String, Drawable?>?
get() {
return context?.run {
getString(R.string.mobile_devices_title) to
ContextCompat.getDrawable(this, R.drawable.ic_menu_main_mobile_devices_24dp)
}
}
get() = context?.run { getString(R.string.mobile_devices_title) to getCompatDrawable(R.drawable.ic_more_mobile_devices) }
override val settingsRes: Pair<String, Drawable?>?
get() {
return context?.run {
getString(R.string.settings_title) to
ContextCompat.getDrawable(this, R.drawable.ic_more_settings_24dp)
}
}
get() = context?.run { getString(R.string.settings_title) to getCompatDrawable(R.drawable.ic_more_settings) }
override val aboutRes: Pair<String, Drawable?>?
get() {
return context?.run {
getString(R.string.about_title) to
ContextCompat.getDrawable(this, R.drawable.ic_all_about_24dp)
}
}
get() = context?.run { getString(R.string.about_title) to getCompatDrawable(R.drawable.ic_all_about) }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_more, container, false)
@ -104,7 +70,7 @@ class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.Mai
}
override fun initView() {
moreAdapter.run { setOnItemClickListener { presenter.onItemSelected(it) } }
moreAdapter.setOnItemClickListener { presenter.onItemSelected(it) }
moreRecycler.apply {
layoutManager = SmoothScrollLinearLayoutManager(context)
@ -148,8 +114,8 @@ class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.Mai
(activity as? MainActivity)?.pushView(AboutFragment.newInstance())
}
override fun popView() {
(activity as? MainActivity)?.popView()
override fun popView(depth: Int) {
(activity as? MainActivity)?.popView(depth)
}
override fun onDestroyView() {

View File

@ -22,25 +22,24 @@ class MorePresenter @Inject constructor(
}
fun onItemSelected(item: AbstractFlexibleItem<*>?) {
if (item is MoreItem) {
Timber.i("Select more item \"${item.title}\"")
view?.run {
when (item.title) {
messagesRes?.first -> openMessagesView()
homeworkRes?.first -> openHomeworkView()
noteRes?.first -> openNoteView()
luckyNumberRes?.first -> openLuckyNumberView()
mobileDevicesRes?.first -> openMobileDevicesView()
settingsRes?.first -> openSettingsView()
aboutRes?.first -> openAboutView()
}
if (item !is MoreItem) return
Timber.i("Select more item \"${item.title}\"")
view?.run {
when (item.title) {
messagesRes?.first -> openMessagesView()
homeworkRes?.first -> openHomeworkView()
noteRes?.first -> openNoteView()
luckyNumberRes?.first -> openLuckyNumberView()
mobileDevicesRes?.first -> openMobileDevicesView()
settingsRes?.first -> openSettingsView()
aboutRes?.first -> openAboutView()
}
}
}
fun onViewReselected() {
Timber.i("More view is reselected")
view?.popView()
view?.popView(2)
}
private fun loadData() {

View File

@ -27,7 +27,7 @@ interface MoreView : BaseView {
fun openAboutView()
fun popView()
fun popView(depth: Int)
fun openMessagesView()

View File

@ -3,7 +3,8 @@ package io.github.wulkanowy.ui.modules.settings
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import com.takisoft.preferencex.PreferenceFragmentCompat
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import dagger.android.support.AndroidSupportInjection
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseActivity
@ -24,8 +25,7 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
fun newInstance() = SettingsFragment()
}
override val titleStringId: Int
get() = R.string.settings_title
override val titleStringId get() = R.string.settings_title
override fun onAttach(context: Context) {
AndroidSupportInjection.inject(this)
@ -37,9 +37,9 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
presenter.onAttachView(this)
}
override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.scheme_preferences)
findPreference(getString(R.string.pref_key_notification_debug)).isVisible = appInfo.isDebug
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.scheme_preferences, rootKey)
findPreference<Preference>(getString(R.string.pref_key_notification_debug))?.isVisible = appInfo.isDebug
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
@ -51,7 +51,7 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
}
override fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) {
findPreference(serviceEnablesKey).run {
findPreference<Preference>(serviceEnablesKey)?.apply {
summary = if (isHolidays) getString(R.string.pref_services_suspended) else ""
isEnabled = !isHolidays
}

View File

@ -31,12 +31,14 @@ class SettingsPresenter @Inject constructor(
fun onSharedPreferenceChanged(key: String) {
Timber.i("Change settings $key")
preferencesRepository.apply {
with(preferencesRepository) {
when (key) {
serviceEnableKey -> with(syncManager) { if (isServiceEnabled) startSyncWorker() else stopSyncWorker() }
servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startSyncWorker(true)
isDebugNotificationEnableKey -> chuckCollector.showNotification(isDebugNotificationEnable)
appThemeKey -> view?.recreateView()
else -> Unit
}
}
analytics.logEvent("setting_changed", "name" to key)

View File

@ -75,7 +75,7 @@ class TimetablePresenter @Inject constructor(
fun onTimetableItemSelected(item: AbstractFlexibleItem<*>?) {
if (item is TimetableItem) {
Timber.i("Select exam item ${item.lesson.id}")
Timber.i("Select timetable item ${item.lesson.id}")
view?.showTimetableDialog(item.lesson)
}
}

View File

@ -4,7 +4,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
@ -13,6 +12,7 @@ import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatDrawable
import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_timetable_completed.*
import javax.inject.Inject
@ -84,7 +84,7 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView.
override fun showFeatureDisabled() {
context?.let {
completedLessonsInfo.text = getString(R.string.error_feature_disabled)
completedLessonsInfoImage.setImageDrawable(ContextCompat.getDrawable(it, R.drawable.ic_all_close_circle_24dp))
completedLessonsInfoImage.setImageDrawable(it.getCompatDrawable(R.drawable.ic_all_close_circle))
}
}

View File

@ -20,7 +20,7 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.services.widgets.TimetableWidgetService
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.nextOrSameSchoolDay
@ -130,8 +130,8 @@ class TimetableWidgetProvider : BroadcastReceiver() {
putExtra(EXTRA_FROM_PROVIDER, true)
}, FLAG_UPDATE_CURRENT))
setPendingIntentTemplate(R.id.timetableWidgetList,
PendingIntent.getActivity(context, MenuView.TIMETABLE.id,
MainActivity.getStartIntent(context, MenuView.TIMETABLE, true), FLAG_UPDATE_CURRENT))
PendingIntent.getActivity(context, MainView.Section.TIMETABLE.id,
MainActivity.getStartIntent(context, MainView.Section.TIMETABLE, true), FLAG_UPDATE_CURRENT))
}.also {
sharedPref.putLong(getDateWidgetKey(appWidgetId), date.toEpochDay(), true)
appWidgetManager.apply {

View File

@ -0,0 +1,31 @@
package io.github.wulkanowy.ui.widgets
import android.content.Context
import android.os.Build.VERSION_CODES.LOLLIPOP
import android.util.AttributeSet
import android.widget.LinearLayout
import androidx.annotation.RequiresApi
import androidx.core.view.ViewCompat
import com.google.android.material.shape.MaterialShapeDrawable
class MaterialLinearLayout : LinearLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attr: AttributeSet) : super(context, attr)
constructor(context: Context, attr: AttributeSet, defStyleAttr: Int) : super(context, attr, defStyleAttr)
init {
val drawable = MaterialShapeDrawable.createWithElevationOverlay(context, ViewCompat.getElevation(this))
ViewCompat.setBackground(this, drawable)
}
@RequiresApi(LOLLIPOP)
override fun setElevation(elevation: Float) {
super.setElevation(elevation)
if (background is MaterialShapeDrawable) {
(background as MaterialShapeDrawable).elevation = elevation
}
}
}

View File

@ -2,9 +2,11 @@ package io.github.wulkanowy.utils
import android.content.Context
import android.content.Intent
import android.util.DisplayMetrics.DENSITY_DEFAULT
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
@ColorInt
@ -20,9 +22,13 @@ fun Context.getThemeAttrColor(@AttrRes colorAttr: Int): Int {
@ColorInt
fun Context.getCompatColor(@ColorRes colorRes: Int) = ContextCompat.getColor(this, colorRes)
fun Context.getCompatDrawable(@DrawableRes drawableRes: Int) = ContextCompat.getDrawable(this, drawableRes)
fun Context.openInternetBrowser(uri: String, onActivityNotFound: (uri: String) -> Unit) {
Intent.parseUri(uri, 0).let {
if (it.resolveActivity(packageManager) != null) startActivity(it)
else onActivityNotFound(uri)
}
}
fun Context.dpToPx(dp: Float) = dp * resources.displayMetrics.densityDpi / DENSITY_DEFAULT

View File

@ -2,19 +2,20 @@ package io.github.wulkanowy.utils
import androidx.fragment.app.Fragment
import com.ncapdevi.fragnav.FragNavController
import io.github.wulkanowy.ui.modules.main.MainView
inline fun FragNavController.setOnViewChangeListener(crossinline listener: (fragment: Fragment?) -> Unit) {
inline fun FragNavController.setOnViewChangeListener(crossinline listener: (section: MainView.Section?) -> Unit) {
transactionListener = object : FragNavController.TransactionListener {
override fun onFragmentTransaction(fragment: Fragment?, transactionType: FragNavController.TransactionType) {
listener(fragment)
listener(fragment?.toSection())
}
override fun onTabTransaction(fragment: Fragment?, index: Int) {
listener(fragment)
listener(fragment?.toSection())
}
}
}
fun FragNavController.safelyPopFragment() {
if (!isRootFragment) popFragment()
fun FragNavController.safelyPopFragments(depth: Int) {
if (!isRootFragment) popFragments(depth)
}

View File

@ -0,0 +1,32 @@
package io.github.wulkanowy.utils
import androidx.fragment.app.Fragment
import io.github.wulkanowy.ui.modules.about.AboutFragment
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
import io.github.wulkanowy.ui.modules.exam.ExamFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.more.MoreFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.settings.SettingsFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
fun Fragment.toSection(): MainView.Section? {
return when (this) {
is GradeFragment -> MainView.Section.GRADE
is AttendanceFragment -> MainView.Section.ATTENDANCE
is ExamFragment -> MainView.Section.EXAM
is TimetableFragment -> MainView.Section.TIMETABLE
is MoreFragment -> MainView.Section.MORE
is MessageFragment -> MainView.Section.MESSAGE
is HomeworkFragment -> MainView.Section.HOMEWORK
is NoteFragment -> MainView.Section.NOTE
is LuckyNumberFragment -> MainView.Section.LUCKY_NUMBER
is SettingsFragment -> MainView.Section.SETTINGS
is AboutFragment -> MainView.Section.ABOUT
else -> null
}
}

View File

@ -1,16 +0,0 @@
package io.github.wulkanowy.utils
import android.view.View
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.LibsBuilder
import com.mikepenz.aboutlibraries.LibsConfiguration
inline fun LibsBuilder.withOnExtraListener(crossinline listener: (Libs.SpecialButton?) -> Unit): LibsBuilder {
withListener(object : LibsConfiguration.LibsListenerImpl() {
override fun onExtraClicked(v: View?, specialButton: Libs.SpecialButton?): Boolean {
listener(specialButton)
return true
}
})
return this
}

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="28.26087"
android:viewportHeight="28.26087">
<group
android:translateX="1.1304348"
android:translateY="1.1304348">
<path
android:fillColor="#FFF"
android:pathData="M13 11h2v2h-2m0 2h2a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2h-2V7h4V5h-4a2 2 0 0 0-2 2v6c0 1.1 0.9 2 2 2m8 2H7V3h14m0-2H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2M3 5H1v16a2 2 0 0 0 2 2h16v-2H3V5z" />
</group>
</vector>

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="26.086956"
android:viewportHeight="26.086956">
<group
android:translateX="1.0434783"
android:translateY="1.0434783">
<path
android:fillColor="#FFF"
android:pathData="M12,11.18C15.3,8.18 17,6.64 17,4.69C17,3.19 15.75,2 14.25,2C13.39,2 12.57,2.36 12,3C11.43,2.36 10.61,2 9.69,2C8.19,2 7,3.25 7,4.75C7,6.64 8.7,8.18 12,11.18M11.18,12C8.18,8.7 6.64,7 4.69,7C3.19,7 2,8.25 2, 9.75C2,10.61 2.36,11.43 3,12C2.36,12.57 2,13.39 2,14.31C2,15.81 3.25,17 4.75,17C6.64,17 8.18,15.3 11.18,12M12.83, 12C15.82,15.3 17.36,17 19.31,17C20.81,17 22,15.75 22,14.25C22,13.39 21.64,12.57 21,12C21.64,11.43 22,10.61 22,9.69C22, 8.19 20.75,7 19.25,7C17.36,7 15.82,8.7 12.83,12M12,12.82C8.7,15.82 7,17.36 7,19.31C7,20.81 8.25,22 9.75,22C10.61,22 11.43,21.64 12,21C12.57,21.64 13.39,22 14.31,22C15.81,22 17,20.75 17,19.25C17,17.36 15.3,15.82 12,12.82Z" />
</group>
</vector>

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="26.086956"
android:viewportHeight="26.086956">
<group
android:translateX="1.0434783"
android:translateY="1.0434783">
<path
android:fillColor="#FFF"
android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM18,14L6,14v-2h12v2zM18,11L6,11L6,9h12v2zM18,8L6,8L6,6h12v2z" />
</group>
</vector>

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="26.086956"
android:viewportHeight="26.086956">
<group
android:translateX="1.0434783"
android:translateY="1.0434783">
<path
android:fillColor="#FFF"
android:pathData="M20.2,2H19.5H18C17.1,2 16,3 16,4H8C8,3 6.9,2 6,2H4.5H3.8H2V11C2,12 3,13 4,13H6.2C6.6,15 7.9,16.7 11,17V19.1C8.8,19.3 8,20.4 8,21.7V22H16V21.7C16,20.4 15.2,19.3 13,19.1V17C16.1,16.7 17.4,15 17.8,13H20C21,13 22,12 22,11V2H20.2M4,11V4H6V6V11C5.1,11 4.3,11 4,11M20,11C19.7,11 18.9,11 18,11V6V4H20V11Z" />
</group>
</vector>

View File

@ -1,14 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="26.086956"
android:viewportHeight="26.086956"
android:tint="#FFFFFF">
<group
android:translateX="1.0434783"
android:translateY="1.0434783">
<path
android:fillColor="#FFFFFF"
android:pathData="M12,11.18C15.3,8.18 17,6.64 17,4.69C17,3.19 15.75,2 14.25,2C13.39,2 12.57,2.36 12,3C11.43,2.36 10.61,2 9.69,2C8.19,2 7,3.25 7,4.75C7,6.64 8.7,8.18 12,11.18M11.18,12C8.18,8.7 6.64,7 4.69,7C3.19,7 2,8.25 2,9.75C2,10.61 2.36,11.43 3,12C2.36,12.57 2,13.39 2,14.31C2,15.81 3.25,17 4.75,17C6.64,17 8.18,15.3 11.18,12M12.83,12C15.82,15.3 17.36,17 19.31,17C20.81,17 22,15.75 22,14.25C22,13.39 21.64,12.57 21,12C21.64,11.43 22,10.61 22,9.69C22,8.19 20.75,7 19.25,7C17.36,7 15.82,8.7 12.83,12M12,12.82C8.7,15.82 7,17.36 7,19.31C7,20.81 8.25,22 9.75,22C10.61,22 11.43,21.64 12,21C12.57,21.64 13.39,22 14.31,22C15.81,22 17,20.75 17,19.25C17,17.36 15.3,15.82 12,12.82Z" />
</group>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 B

View File

Before

Width:  |  Height:  |  Size: 473 B

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 B

View File

Before

Width:  |  Height:  |  Size: 343 B

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 B

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@android:color/transparent" />
<stroke
android:width="3dp"
android:color="@color/colorPrimaryLight" />
</shape>

View File

@ -5,5 +5,5 @@
android:angle="270"
android:centerColor="@android:color/transparent"
android:centerX="0.01"
android:startColor="#494949" />
android:startColor="@color/colorDividerInverse" />
</shape>

View File

@ -1,12 +1,12 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="#9a0007" />
<solid android:color="@color/colorPrimaryDark" />
</shape>
</item>
<item
android:width="200dp"
android:height="200dp"
android:gravity="center"
android:drawable="@drawable/img_splash_logo" />
android:drawable="@drawable/img_splash_logo"
android:gravity="center" />
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

View File

Before

Width:  |  Height:  |  Size: 713 B

After

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 624 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 801 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#ffffffff" />
<solid android:color="#FFF" />
<corners android:radius="5dp" />
</shape>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFF"
android:pathData="M19.95,5.67c-1.9,-1.53 -4.91,-1.79 -5.04,-1.8 -0.2,-0.02 -0.4,0.1 -0.48,0.28 0,0.01 -0.07,0.16 -0.14,0.4 1.26,0.21 2.8,0.64 4.2,1.5a0.48,0.48 0,0 1,-0.5 0.82C15.59,5.38 12.58,5.3 12,5.3s-3.59,0.08 -5.99,1.57a0.48,0.48 0,1 1,-0.5 -0.81c1.4,-0.87 2.94,-1.3 4.2,-1.51 -0.07,-0.24 -0.14,-0.39 -0.14,-0.4a0.47,0.47 0,0 0,-0.48 -0.28c-0.12,0.01 -3.14,0.27 -5.07,1.82C3.02,6.62 1,12.07 1,16.8c0,0.08 0.02,0.16 0.06,0.23 1.4,2.44 5.19,3.08 6.05,3.11h0.02c0.15,0 0.3,-0.07 0.38,-0.2l0.88,-1.2a9.33,9.33 0,0 1,-3.63 -1.7,0.48 0.48,0 0,1 0.63, -0.72c0.03,0.02 2.25,1.9 6.61,1.9 4.37,0 6.6,-1.88 6.61,-1.9a0.48,0.48 0,0 1,0.63 0.71,9.31 9.31,0 0,1 -3.63, 1.71l0.88,1.2c0.09,0.13 0.23,0.2 0.38,0.2h0.02c0.86,-0.03 4.66,-0.67 6.05,-3.11a0.49,0.49 0,0 0,0.06 -0.24c0, -4.7 -2.02,-10.16 -3.05,-11.1zM8.9,14.87c-0.92,0 -1.67,-0.86 -1.67,-1.91s0.75,-1.92 1.67,-1.92 1.67,0.86 1.67, 1.92 -0.74,1.91 -1.67,1.91zM15.12,14.87c-0.92,0 -1.67,-0.86 -1.67,-1.91s0.74,-1.92 1.67,-1.92c0.92,0 1.67,0.86 1.67,1.92s-0.75,1.91 -1.67,1.91z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFF"
android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19, -3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14, 16h-4v-2h4v2zM14,12h-4v-2h4v2z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFF"
android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFF"
android:pathData="M12,3a3,3 0,0 0,-2.8 2H3v2h2l-3,7c-0.5,2 1,3 3.5,3s4,-1 3.5,-3L6,7h3.2c0.3,0.8 1,1.5 1.8,1.8V20H2v2h20v-2h-9V8.8A3,3 0,0 0,14.8 7H18L15,14c-0.5,2 1,3 3.5,3s4,-1 3.5,-3l-3,-7h2V5h-6.2A3, 3 0,0 0,12 3m0,2a1,1 0,0 1,1 1,1 1,0 0,1 -1,1 1,1 0,0 1,-1 -1,1 1,0 0,1 1,-1m-6.5,5.3L7,14H4l1.5,-3.8m13,0L20, 14h-3l1.5,-3.8z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFF"
android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z" />
</vector>

View File

@ -1,12 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:fillColor="#FFF"
android:pathData="M11,7h2v2h-2zM11,11h2v6h-2z" />
<path
android:fillColor="#FFFFFFFF"
android:fillColor="#FFF"
android:pathData="M12,2a10,10 0,1 0,0 20,10 10,0 0,0 0,-20zM12,20a8,8 0,1 1,0 -16,8 8,0 0,1 0,16z" />
</vector>

Some files were not shown because too many files have changed in this diff Show More