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

36 support file uploads for submissions #151

Open
wants to merge 41 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
8d79df4
refactor(homework): cleanup tab handling
JonasWanke Sep 5, 2018
cabbe7b
feat(main): read configs of tabs
JonasWanke Sep 5, 2018
9299b02
feat(main): merge nested configs
JonasWanke Sep 10, 2018
c04a4b1
fix(file): remove old files, directories
JonasWanke Sep 10, 2018
62e8021
fix(file): cache text color in BreadcrumbsView
JonasWanke Sep 10, 2018
eb5c406
feat(file): support uploads
JonasWanke Sep 10, 2018
1f5bd76
ui(main): always center FAB; minor refactoring
JonasWanke Sep 10, 2018
eb843d8
ui(homework): rename overview tab to assignment
JonasWanke Sep 10, 2018
25e0a9c
build: update dependencies
JonasWanke Sep 10, 2018
0c082a2
ui(homework/submission): add attachment UI
JonasWanke Sep 10, 2018
0ff5bae
refactor(file): cleanup upload code; add more error messages
JonasWanke Sep 10, 2018
f567e7a
fix(main): fix refresh in nested fragments
JonasWanke Sep 11, 2018
b62ab7f
feat(homework/submission): add attachment bottom sheet
JonasWanke Sep 11, 2018
fc5e87f
refactor(homework): cleanup
JonasWanke Sep 11, 2018
fd74184
feat(homework/attachment): support taking photo as attachment
JonasWanke Sep 11, 2018
a2dcc67
fix(base): fix permission requests
JonasWanke Sep 13, 2018
a0efd35
ui(main): enable FAB animation
JonasWanke Sep 13, 2018
d22fdfa
fix(file): use real file ID
JonasWanke Sep 14, 2018
b38425d
feat(homework/submission): show attachments
JonasWanke Sep 14, 2018
d82a35d
feat(homework/attachment): add to submission
JonasWanke Sep 14, 2018
2743f85
refactor(homework/attachment); add file from ViewModel
JonasWanke Sep 17, 2018
2dd47bd
fix(file): filenames w/ URI-encoded entities
JonasWanke Sep 18, 2018
abcec54
fix: use correct thread for dialogs
JonasWanke Sep 18, 2018
2a3ec92
feat(homework/attachment): give teacher permission to view attachments
JonasWanke Sep 18, 2018
4a4ee6b
build: update build tools
JonasWanke Sep 18, 2018
9e7e5f1
feat(homework/submission): check for edit permission
JonasWanke Sep 18, 2018
818d2a4
feat(main): sync all tabs on first visibility
JonasWanke Sep 18, 2018
303206d
ui(homework): fix clipping issue
JonasWanke Sep 18, 2018
3c8a584
feat(homework/attachment): include seconds in photo file name
JonasWanke Sep 18, 2018
bde4c8a
feat(homework/attachment): add i18n; disable drawings
JonasWanke Sep 18, 2018
897cc7a
feat(base): add BaseViewModel to close realm instances
JonasWanke Sep 21, 2018
7a399ab
feat(main): show sheet for received files
JonasWanke Sep 21, 2018
c78b29b
ui: fix spacings
JonasWanke Sep 21, 2018
726e293
fix: fix string res escaping
JonasWanke Oct 1, 2018
a1ce6ca
revert(main): remove receive file intent
JonasWanke Dec 16, 2018
f8bf6f2
ui(main): fix homework icon in drawer
JonasWanke Dec 16, 2018
af4a463
fix(file): use correct text size in breadcrumbs
JonasWanke Dec 16, 2018
abf7363
Merge branch 'dev' into 36-support-file-uploads-for-submissions
JonasWanke Dec 16, 2018
7e2c64c
fix(file): show course name in breadcrumbs after nav
JonasWanke Dec 16, 2018
2fd446a
fix(main): slide BottomAppBar back into view after tab change
JonasWanke Dec 17, 2018
4d81852
refactor: fix lint errors
JonasWanke Dec 17, 2018
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
18 changes: 9 additions & 9 deletions app/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,25 @@ dependencies {
// AndroidX
// architecture
implementation "androidx.multidex:multidex:2.0.0"
implementation "androidx.appcompat:appcompat:1.0.0-rc01"
implementation "androidx.appcompat:appcompat:1.0.0-rc02"
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0-rc01"
kapt "androidx.lifecycle:lifecycle-compiler:2.0.0-rc01"
implementation 'android.arch.navigation:navigation-ui-ktx:1.0.0-alpha05'
implementation 'android.arch.navigation:navigation-fragment-ktx:1.0.0-alpha05'
implementation "androidx.browser:browser:1.0.0-rc01"
implementation "androidx.annotation:annotation:1.0.0-rc01"
implementation "androidx.browser:browser:1.0.0-rc02"
implementation "androidx.annotation:annotation:1.0.0-rc02"
// ui
implementation "com.google.android.material:material:1.0.0-rc01"
implementation "androidx.cardview:cardview:1.0.0-rc01"
implementation "androidx.recyclerview:recyclerview:1.0.0-rc01"
implementation "androidx.constraintlayout:constraintlayout:1.1.2"
implementation "com.google.android.material:material:1.0.0-rc02"
implementation "androidx.cardview:cardview:1.0.0-rc02"
implementation "androidx.recyclerview:recyclerview:1.0.0-rc02"
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
// other
implementation "androidx.core:core-ktx:1.0.0-rc01"
implementation "androidx.core:core-ktx:1.0.0-rc02"

// unit tests
testImplementation "junit:junit:4.12"

// instrumented tests
androidTestImplementation "androidx.annotation:annotation:1.0.0-rc01"
androidTestImplementation "androidx.annotation:annotation:1.0.0-rc02"
androidTestImplementation "androidx.test:runner:1.1.0-alpha4"
}
11 changes: 11 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@

<activity android:name=".controllers.login.LoginActivity" />


<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>

</application>

</manifest>
4 changes: 4 additions & 0 deletions app/src/main/java/org/schulcloud/mobile/config/Config.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package org.schulcloud.mobile.config

import org.schulcloud.mobile.BuildConfig

object Config {
val HEADER_AUTH = "Authorization"
val HEADER_AUTH_VALUE_PREFIX = "Bearer "

val REALM_SCHEMA_VERSION = 1L

const val FILE_PROVIDER = "${BuildConfig.APPLICATION_ID}.fileprovider"
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package org.schulcloud.mobile.controllers.base

import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProviders
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
Expand All @@ -14,39 +18,36 @@ import org.schulcloud.mobile.utils.asUri
import org.schulcloud.mobile.utils.openUrl
import org.schulcloud.mobile.utils.setup
import org.schulcloud.mobile.utils.shareLink
import java.util.*
import kotlin.coroutines.experimental.Continuation
import org.schulcloud.mobile.viewmodels.BaseActivityViewModel
import kotlin.coroutines.experimental.suspendCoroutine
import kotlin.properties.Delegates


abstract class BaseActivity : AppCompatActivity() {
abstract class BaseActivity : AppCompatActivity(), ContextAware {
override val baseActivity: BaseActivity? get() = this
override val currentContext: Context get() = this

open var url: String? = null
var swipeRefreshLayout by Delegates.observable<SwipeRefreshLayout?>(null) { _, _, new ->
new?.setup()
new?.setOnRefreshListener { performRefresh() }
}

private val permissionRequests: MutableList<Continuation<Boolean>>
by lazy { LinkedList<Continuation<Boolean>>() }
private val viewModel: BaseActivityViewModel by lazy {
ViewModelProviders.of(this).get(BaseActivityViewModel::class.java)
}

override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.base_action_share -> shareLink(url!!, supportActionBar?.title)
R.id.base_action_refresh -> performRefresh()
// TODO: Remove when deep linking is readded
// TODO: Remove when deep linking is readded

This comment was marked as off-topic.

R.id.base_action_openInBrowser -> openUrl(url.asUri())
else -> return super.onOptionsItemSelected(item)
}
return true
}

protected fun setupActionBar() {
supportActionBar?.apply {
setDisplayHomeAsUpEnabled(true)
}
}

protected open suspend fun refresh() {}
protected fun performRefresh() {
swipeRefreshLayout?.isRefreshing = true
Expand All @@ -56,29 +57,36 @@ abstract class BaseActivity : AppCompatActivity() {
}
}

suspend fun requestPermission(permission: String): Boolean = suspendCoroutine { cont ->

override suspend fun requestPermission(permission: String): Boolean = suspendCoroutine { cont ->
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
cont.resume(true)
return@suspendCoroutine
}

permissionRequests.add(cont)
ActivityCompat.requestPermissions(this, arrayOf(permission), permissionRequests.size - 1)
ActivityCompat.requestPermissions(this, arrayOf(permission), viewModel.addPermissionRequest(cont))
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
// Request not from this class
if (requestCode >= permissionRequests.size) {
if (!viewModel.onPermissionResult(requestCode, permissions, grantResults)) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
return
}
}

//The request was interrupted
if (permissions.isEmpty()) {
permissionRequests[requestCode].resume(false)
return

override suspend fun startActivityForResult(intent: Intent, options: Bundle?): StartActivityResult {
return suspendCoroutine { cont ->
startActivityForResult(intent, viewModel.addActivityRequest(cont), options)
}
}

permissionRequests[requestCode].resume(grantResults[0] == PackageManager.PERMISSION_GRANTED)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
// Request not from this class
if (!viewModel.onActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data)
return
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
package org.schulcloud.mobile.controllers.base

import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment

abstract class BaseFragment : Fragment() {
val baseActivity: BaseActivity? get() = activity as? BaseActivity

suspend fun requestPermission(permission: String): Boolean = baseActivity?.requestPermission(permission) ?: false
abstract class BaseFragment : Fragment(), ContextAware {
override val baseActivity: BaseActivity? get() = activity as? BaseActivity
override val currentContext: Context get() = context!!

override suspend fun requestPermission(permission: String): Boolean {
return baseActivity?.requestPermission(permission) ?: false
}

override suspend fun startActivityForResult(intent: Intent, options: Bundle?): StartActivityResult {
return baseActivity?.startActivityForResult(intent, options) ?: StartActivityResult.error()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.schulcloud.mobile.controllers.base

import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.FragmentManager
import com.google.android.material.bottomsheet.BottomSheetDialogFragment


abstract class BaseSheet : BottomSheetDialogFragment(), ContextAware {
override val baseActivity: BaseActivity? get() = activity as? BaseActivity
override val currentContext: Context get() = context!!

override suspend fun requestPermission(permission: String): Boolean {
return baseActivity?.requestPermission(permission) ?: false
}

override suspend fun startActivityForResult(intent: Intent, options: Bundle?): StartActivityResult {
return baseActivity?.startActivityForResult(intent, options) ?: StartActivityResult.error()
}


fun show(manager: FragmentManager?) = super.show(manager, tag)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.schulcloud.mobile.controllers.base

import android.content.Context
import android.content.Intent
import android.os.Bundle

interface ContextAware {

val baseActivity: BaseActivity?
val currentContext: Context

suspend fun requestPermission(permission: String): Boolean
suspend fun startActivityForResult(intent: Intent, options: Bundle? = null): StartActivityResult

}

data class StartActivityResult(
val success: Boolean,
val data: Intent?
) {
companion object {
fun success(data: Intent?): StartActivityResult = StartActivityResult(true, data)
fun error(): StartActivityResult = StartActivityResult(false, null)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import org.schulcloud.mobile.viewmodels.CourseViewModel
import org.schulcloud.mobile.viewmodels.IdViewModelFactory
import org.schulcloud.mobile.views.DividerItemDecoration

class CourseFragment : MainFragment<CourseViewModel>() {
class CourseFragment : MainFragment<CourseFragment, CourseViewModel>() {
companion object {
val TAG: String = CourseFragment::class.java.simpleName
}
Expand All @@ -40,11 +40,11 @@ class CourseFragment : MainFragment<CourseViewModel>() {
override var url: String? = null
get() = "/courses/${viewModel.course.value?.id}"

override fun provideConfig() = viewModel.course.map { course ->
override fun provideSelfConfig() = viewModel.course.map { course ->
MainFragmentConfig(
title = course?.name ?: getString(R.string.general_error_notFound),
toolbarColor = if (course != null) Color.parseColor(course.color) else null,
menuBottomRes = R.menu.fragment_course_bottom
menuBottomRes = listOf(R.menu.fragment_course_bottom)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import org.schulcloud.mobile.utils.asLiveData
import org.schulcloud.mobile.viewmodels.CourseListViewModel
import org.schulcloud.mobile.views.ItemOffsetDecoration

class CourseListFragment : MainFragment<CourseListViewModel>() {

class CourseListFragment : MainFragment<CourseListFragment, CourseListViewModel>() {
companion object {
val TAG: String = CourseListFragment::class.java.simpleName
}
Expand All @@ -31,7 +32,7 @@ class CourseListFragment : MainFragment<CourseListViewModel>() {


override var url: String? = "/courses"
override fun provideConfig() = MainFragmentConfig(
override fun provideSelfConfig() = MainFragmentConfig(
fragmentType = FragmentType.PRIMARY,
title = getString(R.string.course_title)
).asLiveData()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import org.schulcloud.mobile.controllers.main.MainFragment
import org.schulcloud.mobile.controllers.main.MainFragmentConfig
import org.schulcloud.mobile.utils.asLiveData

class DashboardFragment : MainFragment<ViewModel>() {

class DashboardFragment : MainFragment<DashboardFragment, ViewModel>() {
companion object {
val TAG: String = DashboardFragment::class.java.simpleName
}
Expand All @@ -20,7 +21,7 @@ class DashboardFragment : MainFragment<ViewModel>() {


override var url: String? = "/dashboard"
override fun provideConfig() = MainFragmentConfig(
override fun provideSelfConfig() = MainFragmentConfig(
fragmentType = FragmentType.PRIMARY,
title = getString(R.string.dashboard_title)
).asLiveData()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package org.schulcloud.mobile.controllers.file

import android.content.Context
import android.graphics.Color
import android.graphics.PorterDuff
import android.os.Build
import android.util.AttributeSet
import android.util.TypedValue
import android.widget.TextView
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.appcompat.widget.LinearLayoutCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.content.res.getDimensionOrThrow
Expand All @@ -20,6 +20,7 @@ import org.schulcloud.mobile.utils.combinePath
import org.schulcloud.mobile.utils.getPathParts
import org.schulcloud.mobile.utils.limit
import org.schulcloud.mobile.views.CompatTextView
import kotlin.properties.Delegates

open class BreadcrumbsView @JvmOverloads constructor(
context: Context,
Expand All @@ -32,6 +33,12 @@ open class BreadcrumbsView @JvmOverloads constructor(

var onPathSelected: ((String) -> Unit)? = null

var textColor: Int by Delegates.observable(Color.WHITE) { _, _, new ->
for (child in children)
(child as? TextView)?.setTextColor(new)
dividerDrawable.setColorFilter(new, PorterDuff.Mode.SRC_ATOP)
}

private var textSize: Float = 0f

init {
Expand Down Expand Up @@ -64,16 +71,11 @@ open class BreadcrumbsView @JvmOverloads constructor(
addPartView(parts.limit(i + 1).combinePath(), parts[i])
}

fun setTextColor(@ColorInt color: Int) {
for (child in children)
(child as? TextView)?.setTextColor(color)
dividerDrawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
}

private fun addPartView(path: String, title: String) {
addView(CompatTextView(context).also {
it.textSize = textSize
it.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
it.text = title
it.setTextColor(textColor)
it.setOnClickListener { onPathSelected?.invoke(path) }

with(TypedValue()) {
Expand Down
Loading