Skip to content
This repository has been archived by the owner on Aug 13, 2021. It is now read-only.

22 add resources #150

Open
wants to merge 36 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
01d2b27
feat(material): add Material model
Oct 8, 2018
0953d74
feat(material): add MaterialDao, ViewModel and Repository, add URL
Oct 8, 2018
c9680e8
feat(material): add MaterialListFragment with adapter and layout files
Oct 9, 2018
4644072
feat(learnstore): add fragment to navigation, rename MaterialListFrag…
Oct 15, 2018
0975b7b
feat(learnstore): display tags as text
Oct 16, 2018
aca4a70
feat(learnstore): display thumbnail images
Oct 16, 2018
d09f09c
feat(learnstore): display current material and popular material
Oct 22, 2018
1d4d496
feat(learnstore): improve headings and card margins in layout
Oct 22, 2018
2ad8971
feat(learnstore): add tags as chips in layout, still some bugs
Oct 25, 2018
0237c12
fix(learnstore): remove empty function from adapter
Oct 26, 2018
9f0f404
fix(learnstore): tag chips are now displayed correctly
Oct 28, 2018
a435e77
fix(learnstore): display correct tags for every material
Oct 28, 2018
9a45eda
refactor(views): remove unnecessary LayoutManager subclass
Oct 28, 2018
ea73a41
feat(learnstore): improve design, especially appearance of tags
Oct 29, 2018
5e8205b
fix(learnstore): change layout attributes for backward compatibility
Nov 1, 2018
f7bb93c
fix(learnstore): correct urls and realm queries
Nov 5, 2018
7810910
feat(user): sync materials on login
Nov 5, 2018
500826a
feat(learnstore): open tab when material is clicked, materials are no…
Nov 6, 2018
a0c27d6
fix(learnstore): correct realm queries for deleting deprecated Materi…
Nov 8, 2018
546bf53
refactor(MaterialListAdapter): move TagAdapter handling into Material…
Nov 15, 2018
92bcd80
refactor(learnstore): remove unused code
Nov 15, 2018
6c324e4
refactor(materials): rename methods
Dec 3, 2018
9d493f2
Merge branch '38-fix-ui-backward-compatibility' into 22-add-resources
Dec 4, 2018
5792968
fix(learnstore): set CardView foreground transparent for API 16
Dec 11, 2018
44f39fc
fix: update danger gem
Dec 11, 2018
814d771
fix: update danger gem dependencies
Dec 11, 2018
baa3c1a
fix: update gem dependencies
Dec 11, 2018
7a91169
fix: revert gem changes
Dec 13, 2018
cfc2403
refactor: fix lint errors
Dec 17, 2018
5fcd96c
Merge branch 'dev' of https://github.com/schul-cloud/schulcloud-mobil…
May 27, 2019
96febd5
fix(learnstore): small fixes and cleanup
May 27, 2019
357775b
fix: images can be loaded from url on API 28 now
May 27, 2019
cfaf301
fix(learnstore): reduce vertical distance between tags
May 27, 2019
20a3e8a
fix(learnstore): adapt menu label and fragment title to naming on web…
May 27, 2019
7905076
fix(learnstore): change tag layout margins
May 28, 2019
0a7e7bd
refactor(learnstore): remove unused import
May 28, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {
implementation "com.github.bumptech.glide:glide:${glideVersion}"
implementation "com.github.bumptech.glide:okhttp3-integration:${glideVersion}"
kapt "com.github.bumptech.glide:compiler:${glideVersion}"
implementation "com.squareup.picasso:picasso:2.71828"

// date and time
implementation 'net.danlew:android.joda:2.9.9.4'
Expand Down Expand Up @@ -58,6 +59,7 @@ dependencies {
implementation "androidx.recyclerview:recyclerview:1.0.0"
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation "androidx.preference:preference:1.1.0-alpha04"
implementation 'com.google.android:flexbox:1.1.0'
// other
implementation "androidx.core:core-ktx:1.0.1"
androidTestImplementation "androidx.annotation:annotation:1.0.2"
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
android:label="@string/brand_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">

<activity android:name=".controllers.login.LoginActivity" />
<!-- Link autofill with website -->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package org.schulcloud.mobile.controllers.learnstore


import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.fragment_learnstore.*
import org.schulcloud.mobile.R
import org.schulcloud.mobile.controllers.main.FragmentType
import org.schulcloud.mobile.controllers.main.MainFragment
import org.schulcloud.mobile.controllers.main.MainFragmentConfig
import org.schulcloud.mobile.models.material.Material
import org.schulcloud.mobile.models.material.MaterialRepository
import org.schulcloud.mobile.utils.asLiveData
import org.schulcloud.mobile.viewmodels.MaterialListViewModel
import org.schulcloud.mobile.views.ItemOffsetDecoration

class LearnstoreFragment : MainFragment<MaterialListViewModel>() {

companion object {
val TAG: String = LearnstoreFragment::class.java.simpleName
}

private val currentMaterialAdapter: MaterialListAdapter by lazy {
MaterialListAdapter()
}

private val popularMaterialAdapter: MaterialListAdapter by lazy {
MaterialListAdapter()
}

override var url: String? = "/content"
override fun provideConfig(): LiveData<MainFragmentConfig> = MainFragmentConfig(
fragmentType = FragmentType.PRIMARY,
title = getString(R.string.learnstore_title)
).asLiveData()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(MaterialListViewModel::class.java)
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_learnstore, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

setUpRecyclerView(recyclerViewPopular, popularMaterialAdapter, viewModel.popularMaterials, popularEmpty)
setUpRecyclerView(recyclerViewCurrent, currentMaterialAdapter, viewModel.currentMaterials, currentEmpty)
}

override suspend fun refresh() {
MaterialRepository.syncMaterials()
}

private fun setUpRecyclerView(recyclerView: RecyclerView,
adapter: MaterialListAdapter,
materials: LiveData<List<Material>>,
empty: View) {
adapter.emptyIndicator = empty
materials.observe(this, Observer {
adapter.update(it ?: emptyList())
})
recyclerView.apply {
layoutManager = LinearLayoutManager(activity)
this.adapter = adapter
addItemDecoration(ItemOffsetDecoration(context, R.dimen.grid_spacing))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.schulcloud.mobile.controllers.learnstore

import android.view.LayoutInflater
import android.view.ViewGroup
import com.google.android.flexbox.*
import org.schulcloud.mobile.controllers.base.BaseAdapter
import org.schulcloud.mobile.controllers.base.BaseViewHolder
import org.schulcloud.mobile.databinding.ItemMaterialBinding
import org.schulcloud.mobile.models.material.Material
import org.schulcloud.mobile.utils.asUri
import org.schulcloud.mobile.utils.openUrl

class MaterialListAdapter
: BaseAdapter<Material, MaterialListAdapter.MaterialViewHolder, ItemMaterialBinding>() {

fun update(materials: List<Material>) {
items = materials
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MaterialViewHolder {
val binding = ItemMaterialBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MaterialViewHolder(binding)
}

class MaterialViewHolder(binding: ItemMaterialBinding) : BaseViewHolder<Material, ItemMaterialBinding>(binding) {
private val tagAdapter: TagAdapter = TagAdapter()

init {
binding.recyclerViewTags.apply {
layoutManager = FlexboxLayoutManager(this.context).apply {
flexWrap = FlexWrap.WRAP
flexDirection = FlexDirection.ROW
alignItems = AlignItems.STRETCH
}
adapter = tagAdapter
}
}

override fun onItemSet() {
binding.material = item
binding.viewHolder = this

tagAdapter.update(item.tags ?: emptyList())
}

fun openExternal() {
context.openUrl(item.url.asUri())
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.schulcloud.mobile.controllers.learnstore

import android.view.LayoutInflater
import android.view.ViewGroup
import org.schulcloud.mobile.controllers.base.BaseAdapter
import org.schulcloud.mobile.controllers.base.BaseViewHolder
import org.schulcloud.mobile.databinding.ItemTagBinding

class TagAdapter
: BaseAdapter<String, TagAdapter.TagViewHolder, ItemTagBinding>() {

fun update(tags: List<String>) {
items = tags
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TagViewHolder {
val binding = ItemTagBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return TagViewHolder(binding)
}

class TagViewHolder(binding: ItemTagBinding) : BaseViewHolder<String, ItemTagBinding>(binding) {
override fun onItemSet() {
binding.tagText = item
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.schulcloud.mobile.models.material

import com.google.gson.annotations.SerializedName
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
import org.schulcloud.mobile.models.base.HasId
import java.util.*

open class Material : RealmObject(), HasId {
@PrimaryKey
@SerializedName("_id")
override var id: String = ""

var providerName: String? = null
var url: String? = null
var title: String? = null
var description: String? = null
var thumbnail: String? = null
var featuredUntil: Date? = null
var clickCount: Int? = null
var createdAt: String? = null

var tags: RealmList<String>? = null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.schulcloud.mobile.models.material

import androidx.lifecycle.LiveData
import io.realm.Realm
import io.realm.Sort
import org.joda.time.LocalDate
import org.schulcloud.mobile.utils.allAsLiveData
import org.schulcloud.mobile.utils.map

class MaterialDao(private val realm: Realm) {

fun currentMaterials(): LiveData<List<Material>> {
val currentDate = LocalDate.now().toDate()
return realm.where(Material::class.java)
.greaterThanOrEqualTo("featuredUntil", currentDate)
.sort("title", Sort.ASCENDING)
.allAsLiveData()
}

fun popularMaterials(limitPopular: Int): LiveData<List<Material>> {
return realm.where(Material::class.java)
.sort("clickCount", Sort.DESCENDING)
.allAsLiveData()
.map { materialList ->
materialList.take(limitPopular)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.schulcloud.mobile.models.material

import androidx.lifecycle.LiveData
import io.realm.Realm
import org.joda.time.LocalDate
import org.joda.time.format.DateTimeFormat
import org.schulcloud.mobile.jobs.base.RequestJob
import org.schulcloud.mobile.utils.materialDao

object MaterialRepository {
const val LIMIT_POPULAR = 3

fun currentMaterials(realm: Realm): LiveData<List<Material>> {
return realm.materialDao().currentMaterials()
}

fun popularMaterials(realm: Realm): LiveData<List<Material>> {
return realm.materialDao().popularMaterials(LIMIT_POPULAR)
}

suspend fun syncMaterials() {
val currentDate = LocalDate.now()
val dateString = currentDate.toString(DateTimeFormat.forPattern("yyyy-MM-dd"))
RequestJob.Data.with({ listCurrentMaterials(dateString) }, {
greaterThanOrEqualTo("featuredUntil", currentDate.toDate())
}).run()

RequestJob.Data.with({ listPopularMaterials(LIMIT_POPULAR) }, {
beginGroup()
.isNull("featuredUntil")
.or()
.lessThan("featuredUntil", currentDate.toDate())
.endGroup()
}).run()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.schulcloud.mobile.models.base.Repository
import org.schulcloud.mobile.models.course.CourseRepository
import org.schulcloud.mobile.models.event.EventRepository
import org.schulcloud.mobile.models.homework.HomeworkRepository
import org.schulcloud.mobile.models.material.MaterialRepository
import org.schulcloud.mobile.models.news.NewsRepository
import org.schulcloud.mobile.storages.UserStorage
import org.schulcloud.mobile.utils.userDao
Expand Down Expand Up @@ -57,6 +58,7 @@ object UserRepository : Repository() {
HomeworkRepository.syncHomeworkList()
CourseRepository.syncCourses()
NewsRepository.syncNews()
MaterialRepository.syncMaterials()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.schulcloud.mobile.models.file.SignedUrlRequest
import org.schulcloud.mobile.models.file.SignedUrlResponse
import org.schulcloud.mobile.models.homework.Homework
import org.schulcloud.mobile.models.homework.submission.Submission
import org.schulcloud.mobile.models.material.Material
import org.schulcloud.mobile.models.news.News
import org.schulcloud.mobile.models.topic.Topic
import org.schulcloud.mobile.models.user.User
Expand Down Expand Up @@ -67,4 +68,10 @@ interface ApiServiceInterface {
fun generateSignedUrl(@Body signedUrlRequest: SignedUrlRequest): Call<SignedUrlResponse>
@GET
fun downloadFile(@Url fileUrl: String): Call<ResponseBody>

// Material
@GET("content/resources?\$sort[clickCount]=-1")
fun listPopularMaterials(@Query("\$limit") limitPopular: Int): Call<FeathersResponse<List<Material>>>
@GET("content/resources")
fun listCurrentMaterials(@Query("featuredUntil[\$gte]={date}") date: String): Call<FeathersResponse<List<Material>>>
}
3 changes: 3 additions & 0 deletions app/src/main/java/org/schulcloud/mobile/utils/RealmUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.schulcloud.mobile.models.event.EventDao
import org.schulcloud.mobile.models.file.FileDao
import org.schulcloud.mobile.models.homework.HomeworkDao
import org.schulcloud.mobile.models.homework.submission.SubmissionDao
import org.schulcloud.mobile.models.material.MaterialDao
import org.schulcloud.mobile.models.news.NewsDao
import org.schulcloud.mobile.models.topic.TopicDao
import org.schulcloud.mobile.models.user.UserDao
Expand All @@ -40,3 +41,5 @@ fun Realm.homeworkDao(): HomeworkDao = HomeworkDao(this)
fun Realm.submissionDao(): SubmissionDao = SubmissionDao(this)

fun Realm.fileDao(): FileDao = FileDao(this)

fun Realm.materialDao(): MaterialDao = MaterialDao(this)
16 changes: 16 additions & 0 deletions app/src/main/java/org/schulcloud/mobile/utils/ViewUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ import android.content.Context
import android.content.res.ColorStateList
import android.content.res.Resources
import android.graphics.Color
import android.graphics.Point
import android.graphics.drawable.ColorDrawable
import android.text.TextUtils
import android.text.format.DateUtils
import android.view.View
import android.view.WindowManager
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import androidx.databinding.BindingAdapter
import androidx.databinding.BindingConversion
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.squareup.picasso.Picasso
import com.google.android.material.bottomappbar.BottomAppBar
import org.schulcloud.mobile.R

Expand Down Expand Up @@ -50,6 +55,17 @@ fun showDate(view: TextView, date: String?) {
} ?: ""
}

@BindingAdapter("fromUrl")
fun getImageFromUrl(view: ImageView, url: String?) {
if (!TextUtils.isEmpty(url)) {
val wm: WindowManager = view.context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val size = Point()
wm.defaultDisplay.getSize(size)
val width = size.x
Picasso.get().load(url).resize(width, 0).centerInside().into(view)
}
}

fun Int.dpToPx(): Int = Math.round(this * Resources.getSystem().displayMetrics.density)


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.schulcloud.mobile.viewmodels

import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import io.realm.Realm
import org.schulcloud.mobile.models.material.Material
import org.schulcloud.mobile.models.material.MaterialRepository

class MaterialListViewModel : ViewModel() {
private val realm: Realm by lazy {
Realm.getDefaultInstance()
}

val currentMaterials: LiveData<List<Material>> = MaterialRepository.currentMaterials(realm)
val popularMaterials: LiveData<List<Material>> = MaterialRepository.popularMaterials(realm)
}
Loading