Nareshkumar Rao
4 years ago
committed by
GitHub
22 changed files with 675 additions and 54 deletions
Binary file not shown.
@ -0,0 +1,113 @@ |
|||||
|
package com.nareshkumarrao.eiweblog |
||||
|
|
||||
|
import android.app.AlertDialog |
||||
|
import android.app.Dialog |
||||
|
import android.content.Context |
||||
|
import android.os.Bundle |
||||
|
import android.view.View |
||||
|
import android.widget.EditText |
||||
|
import android.widget.ProgressBar |
||||
|
import android.widget.TextView |
||||
|
import androidx.appcompat.app.AppCompatActivity |
||||
|
import androidx.appcompat.widget.Toolbar |
||||
|
import androidx.fragment.app.DialogFragment |
||||
|
import androidx.recyclerview.widget.DividerItemDecoration |
||||
|
import androidx.recyclerview.widget.LinearLayoutManager |
||||
|
import androidx.recyclerview.widget.RecyclerView |
||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout |
||||
|
import com.nareshkumarrao.eiweblog.ui.main.ItemArticleAdapter |
||||
|
import com.nareshkumarrao.eiweblog.ui.main.ItemGradesAdapter |
||||
|
|
||||
|
|
||||
|
class GradesActivity : AppCompatActivity() { |
||||
|
override fun onCreate(savedInstanceState: Bundle?) { |
||||
|
super.onCreate(savedInstanceState) |
||||
|
setContentView(R.layout.activity_grades) |
||||
|
|
||||
|
|
||||
|
val myToolbar = findViewById<View>(R.id.grades_toolbar) as Toolbar |
||||
|
setSupportActionBar(myToolbar) |
||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true) |
||||
|
supportActionBar?.setDisplayShowTitleEnabled(false) |
||||
|
|
||||
|
findViewById<RecyclerView>(R.id.grades_recycler).apply { |
||||
|
layoutManager = LinearLayoutManager(this@GradesActivity) |
||||
|
adapter = ItemArticleAdapter(listOf()) |
||||
|
} |
||||
|
findViewById<RecyclerView>(R.id.grades_recycler).addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL)) |
||||
|
val swipeRefreshLayout = findViewById<SwipeRefreshLayout>(R.id.grades_swipe_refresh) |
||||
|
swipeRefreshLayout?.setOnRefreshListener { |
||||
|
HISUtility.fetchExamRows(this, ::updateExamRows) |
||||
|
} |
||||
|
|
||||
|
val sharedPref = getSharedPreferences( |
||||
|
getString(R.string.preference_file_key), |
||||
|
Context.MODE_PRIVATE |
||||
|
) |
||||
|
|
||||
|
val username = sharedPref?.getString(getString(R.string.username_key), null) |
||||
|
if( username == null ){ |
||||
|
val loginDialog = LoginDialogFragment(this, false) { |
||||
|
HISUtility.fetchExamRows(this, ::updateExamRows) |
||||
|
} |
||||
|
loginDialog.show(supportFragmentManager, "loginDialog") |
||||
|
}else{ |
||||
|
val savedRows = HISUtility.getSavedExamRows(this) |
||||
|
if(savedRows == null){ |
||||
|
HISUtility.fetchExamRows(this, ::updateExamRows) |
||||
|
}else{ |
||||
|
updateExamRows(savedRows) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private fun updateExamRows(examRows: List<ExamRow>?){ |
||||
|
examRows ?: run { |
||||
|
val loginDialog = LoginDialogFragment(this, true){ |
||||
|
HISUtility.fetchExamRows(this, ::updateExamRows) |
||||
|
} |
||||
|
loginDialog.show(supportFragmentManager, "loginDialog") |
||||
|
return |
||||
|
} |
||||
|
this@GradesActivity.runOnUiThread { |
||||
|
findViewById<RecyclerView>(R.id.grades_recycler)?.apply { |
||||
|
layoutManager = LinearLayoutManager(this@GradesActivity) |
||||
|
adapter = ItemGradesAdapter(examRows) |
||||
|
} |
||||
|
findViewById<SwipeRefreshLayout>(R.id.grades_swipe_refresh).isRefreshing=false |
||||
|
findViewById<ProgressBar>(R.id.gradesProgressBar).visibility=View.GONE |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class LoginDialogFragment(val context: GradesActivity, private val isError: Boolean, val loginCallback: () -> Unit?) : DialogFragment() { |
||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { |
||||
|
return activity?.let { |
||||
|
val builder = AlertDialog.Builder(it) |
||||
|
val inflater = requireActivity().layoutInflater |
||||
|
val dialogView = inflater.inflate(R.layout.dialog_login, null) |
||||
|
if(isError){ |
||||
|
dialogView.findViewById<TextView>(R.id.errorText).visibility=View.VISIBLE |
||||
|
}else{ |
||||
|
dialogView.findViewById<TextView>(R.id.errorText).visibility=View.INVISIBLE |
||||
|
} |
||||
|
builder.setView(dialogView) |
||||
|
.setPositiveButton( |
||||
|
R.string.login |
||||
|
) { _, _ -> |
||||
|
val username = dialogView.findViewById<EditText>(R.id.loginUsername).text.toString() |
||||
|
val password = dialogView.findViewById<EditText>(R.id.loginPassword).text.toString() |
||||
|
HISUtility.setUsernamePassword(context, username, password) |
||||
|
loginCallback() |
||||
|
} |
||||
|
.setNegativeButton( |
||||
|
R.string.cancel |
||||
|
) { _, _ -> |
||||
|
dialog?.cancel() |
||||
|
context.finish() |
||||
|
} |
||||
|
builder.create() |
||||
|
} ?: throw IllegalStateException("Activity cannot be null") |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,173 @@ |
|||||
|
package com.nareshkumarrao.eiweblog |
||||
|
|
||||
|
import android.app.NotificationChannel |
||||
|
import android.app.NotificationManager |
||||
|
import android.app.PendingIntent |
||||
|
import android.content.Context |
||||
|
import android.content.Intent |
||||
|
import android.os.Build |
||||
|
import androidx.core.app.NotificationCompat |
||||
|
import androidx.core.app.NotificationManagerCompat |
||||
|
import com.google.gson.Gson |
||||
|
import com.google.gson.reflect.TypeToken |
||||
|
import org.jsoup.Connection |
||||
|
import org.jsoup.Jsoup |
||||
|
|
||||
|
data class ExamRow(val name: String, val grade: String, val attempt: String, val date: String) |
||||
|
|
||||
|
internal object HISUtility { |
||||
|
|
||||
|
fun setUsernamePassword(context: Context?, username: String, password: String) { |
||||
|
val sharedPref = context?.getSharedPreferences(context.getString(R.string.preference_file_key), Context.MODE_PRIVATE) |
||||
|
if (sharedPref != null) { |
||||
|
with(sharedPref.edit()) { |
||||
|
putString(context.getString(R.string.username_key), username) |
||||
|
putString(context.getString(R.string.password_key), password) |
||||
|
apply() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fun checkForUpdates(context: Context?, callback: (examRows: List<ExamRow>?) -> Unit) { |
||||
|
val savedRows = getSavedExamRows(context) ?: run { |
||||
|
callback(null) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
val newRows: MutableList<ExamRow> = mutableListOf() |
||||
|
fetchExamRows(context) { examRows -> |
||||
|
if (examRows != null) { |
||||
|
for (examRow in examRows) { |
||||
|
if (!savedRows.contains(examRow)) { |
||||
|
newRows.add(examRow) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
callback(newRows) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
fun getSavedExamRows(context: Context?): List<ExamRow>? { |
||||
|
val sharedPref = context?.getSharedPreferences(context.getString(R.string.preference_file_key), Context.MODE_PRIVATE) |
||||
|
val examRowsJson = sharedPref?.getString(context.getString(R.string.exam_rows_key), null) |
||||
|
?: return null |
||||
|
val examRowsType = object : TypeToken<List<ExamRow>>() {}.type |
||||
|
return Gson().fromJson(examRowsJson, examRowsType) |
||||
|
} |
||||
|
|
||||
|
private fun saveExamRows(context: Context?, examRows: List<ExamRow>) { |
||||
|
val examRowsJson = Gson().toJson(examRows) |
||||
|
val sharedPref = context?.getSharedPreferences(context.getString(R.string.preference_file_key), Context.MODE_PRIVATE) |
||||
|
if (sharedPref != null) { |
||||
|
with(sharedPref.edit()) { |
||||
|
putString(context.getString(R.string.exam_rows_key), examRowsJson) |
||||
|
apply() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Throws(LoginFailedException::class) |
||||
|
fun fetchExamRows(context: Context?, callback: (examRows: List<ExamRow>?) -> Unit): Unit? { |
||||
|
val sharedPref = context?.getSharedPreferences(context.getString(R.string.preference_file_key), Context.MODE_PRIVATE) |
||||
|
val username = sharedPref?.getString(context.getString(R.string.username_key), null) |
||||
|
?: return null |
||||
|
val password = sharedPref.getString(context.getString(R.string.password_key), null) |
||||
|
?: return null |
||||
|
|
||||
|
val runnable = Runnable { |
||||
|
val postData: MutableMap<String, String> = mutableMapOf() |
||||
|
postData["asdf"] = username |
||||
|
postData["fdsa"] = password |
||||
|
|
||||
|
val loginPage = Jsoup.connect(context.getString(R.string.ossc_login_post)) |
||||
|
.method(Connection.Method.POST) |
||||
|
.userAgent("Mozilla") |
||||
|
.data(postData) |
||||
|
.execute() |
||||
|
|
||||
|
val selectNotenspiegel = Jsoup.connect(context.getString(R.string.ossc_select_noten)) |
||||
|
.userAgent("Mozilla") |
||||
|
.cookies(loginPage.cookies()) |
||||
|
.get() |
||||
|
|
||||
|
val notenspiegelURL = selectNotenspiegel.select("a[href]:containsOwn(Notenspiegel)").first()?.attr("href") |
||||
|
?: kotlin.run { |
||||
|
callback(null) |
||||
|
return@Runnable |
||||
|
} |
||||
|
|
||||
|
val selectStudiengangUnhide = Jsoup.connect(notenspiegelURL) |
||||
|
.userAgent("Mozilla") |
||||
|
.cookies(loginPage.cookies()) |
||||
|
.get() |
||||
|
val selectStudiengangUnhideURL = selectStudiengangUnhide.select("a[href]:containsOwn(Abschluss)").first().attr("href") |
||||
|
|
||||
|
val selectStudiengang = Jsoup.connect(selectStudiengangUnhideURL) |
||||
|
.userAgent("Mozilla") |
||||
|
.cookies(loginPage.cookies()) |
||||
|
.get() |
||||
|
val studiengangURL = selectStudiengang.select("a[href]:containsOwn(Leistungen anzeigen)").first().attr("href") |
||||
|
|
||||
|
|
||||
|
val notenSpiegelPage = Jsoup.connect(studiengangURL) |
||||
|
.userAgent("Mozilla") |
||||
|
.cookies(loginPage.cookies()) |
||||
|
.get() |
||||
|
|
||||
|
val allGradesRows = notenSpiegelPage.select("div.fixedContainer > table > tbody > tr") |
||||
|
val examRows: MutableList<ExamRow> = mutableListOf() |
||||
|
for (row in allGradesRows) { |
||||
|
if (row.select("td.tabelle1_alignleft").size < 1) { |
||||
|
continue |
||||
|
} |
||||
|
val columns = row.select("td") |
||||
|
if (columns.size < 1) { |
||||
|
continue |
||||
|
} |
||||
|
val examRow = ExamRow(columns[1].text(), columns[3].text(), columns[6].text(), columns[7].text()) |
||||
|
examRows.add(examRow) |
||||
|
} |
||||
|
saveExamRows(context, examRows) |
||||
|
callback(examRows) |
||||
|
} |
||||
|
|
||||
|
return Thread(runnable).start() |
||||
|
} |
||||
|
fun sendNotification(context: Context?, examRow: ExamRow, id:Int) { |
||||
|
val intent = Intent(context, NotificationSettingsActivity::class.java).apply { |
||||
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK |
||||
|
} |
||||
|
val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, 0) |
||||
|
|
||||
|
val builder = NotificationCompat.Builder(context!!, context.getString(R.string.grades_notification_channel_id)) |
||||
|
.setSmallIcon(R.drawable.ic_stat_name) |
||||
|
.setContentTitle(context.getString(R.string.exam_results_notification)) |
||||
|
.setStyle(NotificationCompat.BigTextStyle() |
||||
|
.bigText("${examRow.name}: ${examRow.grade}")) |
||||
|
.setContentIntent(pendingIntent) |
||||
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT) |
||||
|
.setAutoCancel(true) |
||||
|
with(NotificationManagerCompat.from(context)) { |
||||
|
notify(id, builder.build()) |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
fun createNotificationChannel(context: Context?){ |
||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { |
||||
|
val name = context?.getString(R.string.grades_notification_channel_name) |
||||
|
val descriptionText = context?.getString(R.string.grades_notification_channel_description) |
||||
|
val importance = NotificationManager.IMPORTANCE_DEFAULT |
||||
|
val channel = NotificationChannel(context?.getString(R.string.grades_notification_channel_id), name, importance).apply { |
||||
|
description = descriptionText |
||||
|
} |
||||
|
val notificationManager: NotificationManager = context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager |
||||
|
notificationManager.createNotificationChannel(channel) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
class LoginFailedException : Throwable() |
@ -0,0 +1,46 @@ |
|||||
|
package com.nareshkumarrao.eiweblog.ui.main |
||||
|
|
||||
|
import android.view.LayoutInflater |
||||
|
import android.view.ViewGroup |
||||
|
import android.widget.TextView |
||||
|
import androidx.recyclerview.widget.RecyclerView |
||||
|
import com.nareshkumarrao.eiweblog.ExamRow |
||||
|
import com.nareshkumarrao.eiweblog.R |
||||
|
|
||||
|
class ItemGradesAdapter(private val examRows: List<ExamRow>) : RecyclerView.Adapter<ItemGradesAdapter.ViewHolder>() { |
||||
|
inner class ViewHolder(inflater: LayoutInflater, parent: ViewGroup) : RecyclerView.ViewHolder(inflater.inflate(R.layout.item_grades, parent, false)) { |
||||
|
private var name: TextView? = null |
||||
|
private var attempt: TextView? = null |
||||
|
private var date: TextView? = null |
||||
|
private var grade: TextView? = null |
||||
|
|
||||
|
init { |
||||
|
name = itemView.findViewById(R.id.examNameText) |
||||
|
attempt = itemView.findViewById(R.id.versuchText) |
||||
|
date = itemView.findViewById(R.id.examDateText) |
||||
|
grade = itemView.findViewById(R.id.gradeText) |
||||
|
} |
||||
|
|
||||
|
fun bind(examRow: ExamRow) { |
||||
|
name?.text = examRow.name |
||||
|
attempt?.text = examRow.attempt |
||||
|
grade?.text = examRow.grade |
||||
|
date?.text = examRow.date |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemGradesAdapter.ViewHolder { |
||||
|
val inflater = LayoutInflater.from(parent.context) |
||||
|
return ViewHolder(inflater, parent) |
||||
|
} |
||||
|
|
||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) { |
||||
|
val examRow = examRows[position] |
||||
|
holder.bind(examRow) |
||||
|
} |
||||
|
|
||||
|
override fun getItemCount(): Int { |
||||
|
return examRows.size |
||||
|
} |
||||
|
} |
@ -0,0 +1,54 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
tools:context=".GradesActivity" |
||||
|
android:orientation="vertical"> |
||||
|
|
||||
|
<com.google.android.material.appbar.AppBarLayout |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:background="@color/white" |
||||
|
android:theme="@style/Theme.EIWeblog.AppBarOverlay"> |
||||
|
|
||||
|
<androidx.appcompat.widget.Toolbar |
||||
|
android:id="@+id/grades_toolbar" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
app:contentInsetStart="0px"> |
||||
|
|
||||
|
<TextView |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:fontFamily="@font/hsdsans_elektro" |
||||
|
android:minHeight="?actionBarSize" |
||||
|
android:padding="@dimen/appbar_padding" |
||||
|
android:paddingStart="0px" |
||||
|
android:paddingLeft="0px" |
||||
|
android:text="@string/grades_title" |
||||
|
android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title" |
||||
|
android:textColor="@color/black" |
||||
|
android:textSize="25sp" |
||||
|
tools:ignore="RtlSymmetry" /> |
||||
|
</androidx.appcompat.widget.Toolbar> |
||||
|
</com.google.android.material.appbar.AppBarLayout> |
||||
|
|
||||
|
<ProgressBar |
||||
|
android:id="@+id/gradesProgressBar" |
||||
|
style="?android:attr/progressBarStyle" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" /> |
||||
|
|
||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout |
||||
|
android:id="@+id/grades_swipe_refresh" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent"> |
||||
|
<androidx.recyclerview.widget.RecyclerView |
||||
|
android:id="@+id/grades_recycler" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" /> |
||||
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> |
||||
|
|
||||
|
</LinearLayout> |
@ -0,0 +1,64 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:orientation="vertical"> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/loginTitle" |
||||
|
android:layout_width="0dp" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginStart="16dp" |
||||
|
android:layout_marginLeft="16dp" |
||||
|
android:layout_marginTop="16dp" |
||||
|
android:fontFamily="@font/hsdsans_regular" |
||||
|
android:text="@string/login_dialog_title" |
||||
|
android:textColor="@color/black" |
||||
|
android:textSize="18sp" |
||||
|
app:layout_constraintStart_toStartOf="parent" |
||||
|
app:layout_constraintTop_toTopOf="parent" /> |
||||
|
|
||||
|
<EditText |
||||
|
android:id="@+id/loginUsername" |
||||
|
android:layout_width="0dp" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginTop="8dp" |
||||
|
android:hint="@string/username" |
||||
|
android:inputType="text" |
||||
|
android:autofillHints="username" |
||||
|
app:layout_constraintEnd_toEndOf="@+id/errorText" |
||||
|
app:layout_constraintStart_toStartOf="@+id/loginTitle" |
||||
|
app:layout_constraintTop_toBottomOf="@+id/loginTitle" /> |
||||
|
|
||||
|
<EditText |
||||
|
android:id="@+id/loginPassword" |
||||
|
android:layout_width="0dp" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginTop="8dp" |
||||
|
android:layout_marginBottom="16dp" |
||||
|
android:hint="@string/password" |
||||
|
android:autofillHints="current-password" |
||||
|
android:inputType="textPassword" |
||||
|
app:layout_constraintBottom_toBottomOf="parent" |
||||
|
app:layout_constraintEnd_toEndOf="@+id/loginUsername" |
||||
|
app:layout_constraintStart_toStartOf="@+id/loginUsername" |
||||
|
app:layout_constraintTop_toBottomOf="@+id/loginUsername" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/errorText" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginEnd="16dp" |
||||
|
android:layout_marginRight="16dp" |
||||
|
android:fontFamily="@font/hsdsans_regular" |
||||
|
android:text="@string/error" |
||||
|
android:textColor="@color/fh_red" |
||||
|
android:textSize="18sp" |
||||
|
android:textStyle="bold" |
||||
|
android:visibility="invisible" |
||||
|
app:layout_constraintEnd_toEndOf="parent" |
||||
|
app:layout_constraintTop_toTopOf="@+id/loginTitle" /> |
||||
|
|
||||
|
</androidx.constraintlayout.widget.ConstraintLayout> |
@ -0,0 +1,79 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content"> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/examNameText" |
||||
|
android:layout_width="0dp" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginStart="16dp" |
||||
|
android:layout_marginLeft="16dp" |
||||
|
android:layout_marginTop="16dp" |
||||
|
android:text="G 19 Höhere Mathematik " |
||||
|
android:textColor="@color/black" |
||||
|
android:textSize="18sp" |
||||
|
android:textStyle="bold" |
||||
|
app:layout_constraintEnd_toEndOf="@+id/gradeText" |
||||
|
app:layout_constraintStart_toStartOf="parent" |
||||
|
app:layout_constraintTop_toTopOf="parent" |
||||
|
tools:ignore="HardcodedText" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/textView5" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginTop="8dp" |
||||
|
android:text="@string/versuch" |
||||
|
android:textColor="@color/black" |
||||
|
app:layout_constraintStart_toStartOf="@+id/examNameText" |
||||
|
app:layout_constraintTop_toBottomOf="@+id/examNameText" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/textView6" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginTop="8dp" |
||||
|
android:layout_marginBottom="16dp" |
||||
|
android:text="@string/pruefungsdatum" |
||||
|
android:textColor="@color/black" |
||||
|
app:layout_constraintBottom_toBottomOf="parent" |
||||
|
app:layout_constraintStart_toStartOf="@+id/textView5" |
||||
|
app:layout_constraintTop_toBottomOf="@+id/textView5" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/gradeText" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginEnd="32dp" |
||||
|
android:layout_marginRight="32dp" |
||||
|
android:text="4.0" |
||||
|
android:textColor="@color/black" |
||||
|
android:textSize="30sp" |
||||
|
app:layout_constraintBottom_toBottomOf="@+id/textView6" |
||||
|
app:layout_constraintEnd_toEndOf="parent" |
||||
|
app:layout_constraintTop_toTopOf="@+id/versuchText" |
||||
|
tools:ignore="HardcodedText" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/versuchText" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginEnd="32dp" |
||||
|
android:layout_marginRight="32dp" |
||||
|
android:text="1" |
||||
|
app:layout_constraintEnd_toStartOf="@+id/gradeText" |
||||
|
app:layout_constraintTop_toTopOf="@+id/textView5" |
||||
|
tools:ignore="HardcodedText" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/examDateText" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:text="05.02.2020" |
||||
|
app:layout_constraintEnd_toEndOf="@+id/versuchText" |
||||
|
app:layout_constraintTop_toTopOf="@+id/textView6" |
||||
|
tools:ignore="HardcodedText" /> |
||||
|
</androidx.constraintlayout.widget.ConstraintLayout> |
Loading…
Reference in new issue