Compare commits

...

14 Commits
0.4.1 ... 0.4.3

Author SHA1 Message Date
Johan von Forstner
f0f6c08610 Release 0.4.3 2021-01-17 14:15:46 +01:00
Johan von Forstner
a2fe9a06c5 fix another IllegalStateException 2021-01-17 14:09:37 +01:00
Johan von Forstner
cb79f17c23 catch IllegalArgumentException 2021-01-17 14:08:28 +01:00
Johan von Forstner
0009895537 fix IllegalStateException 2021-01-17 14:07:20 +01:00
Johan von Forstner
df705670b1 fix ClassCastException 2021-01-17 14:00:35 +01:00
Johan von Forstner
c616e9fdbd README.md: Describe map backends
see also #36
2021-01-06 19:30:45 +01:00
Johan von Forstner
c70a092d99 Release 0.4.2 2021-01-03 16:47:15 +01:00
Johan von Forstner
34fee47c08 Fix incorrect linking of text (fixes #29) 2021-01-03 16:23:07 +01:00
Johan von Forstner
bf97a14fe3 add station availability in map screen (fixes #52) 2021-01-03 15:28:58 +01:00
Johan von Forstner
60d4d56f80 Fix links to Google Maps
(maps app was not found due to https://developer.android.com/training/basics/intents/package-visibility)
2021-01-03 11:00:22 +01:00
Johan von Forstner
8bf33c7384 FilterProfilesFragment: Add rename and delete buttons + undo function 2021-01-03 10:45:56 +01:00
Johan von Forstner
595e6e9a8f Welcome dialog: replace > with ≥ 2021-01-03 09:52:07 +01:00
Johan von Forstner
9efbdfc046 Fix typo in welcome page 2021-01-02 22:38:54 +01:00
Johan von Forstner
e1d4b6bcc5 welcome dialog: fix height on small screens 2021-01-02 20:09:12 +01:00
27 changed files with 349 additions and 106 deletions

View File

@@ -20,6 +20,7 @@ Features
- Favorites list, also with availability information
- No ads, fully open source
- Compatible with Android 5.0 and above
- Can use Google Maps or Mapbox (OpenStreetMap) as map backends - the version available on F-Droid only uses Mapbox.
Screenshots
-----------

View File

@@ -13,8 +13,8 @@ android {
applicationId "net.vonforst.evmap"
minSdkVersion 21
targetSdkVersion 30
versionCode 28
versionName "0.4.1"
versionCode 37
versionName "0.4.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

View File

@@ -5,6 +5,17 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="geo" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="google.navigation" />
</intent>
</queries>
<application
android:name=".EvMapApplication"
android:allowBackup="true"

View File

@@ -91,7 +91,7 @@ class MapsActivity : AppCompatActivity() {
cb.getRootView(),
R.string.no_maps_app_found,
Snackbar.LENGTH_SHORT
)
).show()
}
}

View File

@@ -48,7 +48,8 @@ fun buildDetails(
R.drawable.ic_address,
R.string.address,
loc.address.toString(),
loc.locationDescription
loc.locationDescription,
clickable = true
),
if (loc.operator != null) DetailsAdapter.Detail(
R.drawable.ic_operator,

View File

@@ -1,16 +1,23 @@
package net.vonforst.evmap.adapter
import android.annotation.SuppressLint
import android.view.MotionEvent
import android.view.animation.AccelerateInterpolator
import androidx.recyclerview.widget.ItemTouchHelper
import net.vonforst.evmap.R
import net.vonforst.evmap.databinding.ItemFilterProfileBinding
import net.vonforst.evmap.storage.FilterProfile
class FilterProfilesAdapter(val dragHelper: ItemTouchHelper) : DataBindingAdapter<FilterProfile>() {
class FilterProfilesAdapter(
val dragHelper: ItemTouchHelper,
val onDelete: (FilterProfile) -> Unit,
val onRename: (FilterProfile) -> Unit
) : DataBindingAdapter<FilterProfile>() {
init {
setHasStableIds(true)
}
@SuppressLint("ClickableViewAccessibility")
override fun bind(
holder: ViewHolder<FilterProfile>,
item: FilterProfile
@@ -24,6 +31,20 @@ class FilterProfilesAdapter(val dragHelper: ItemTouchHelper) : DataBindingAdapte
}
false
}
binding.foreground.translationX = 0f
binding.btnDelete.setOnClickListener {
binding.foreground.animate()
.translationX(binding.foreground.width.toFloat())
.setDuration(250)
.setInterpolator(AccelerateInterpolator())
.withEndAction {
onDelete(item)
}
.start()
}
binding.btnRename.setOnClickListener {
onRename(item)
}
}
override fun getItemId(position: Int): Long {

View File

@@ -97,6 +97,9 @@ data class ChargeLocation(
}
}
val totalChargepoints: Int
get() = chargepoints.sumBy { it.count }
fun formatChargepoints(): String {
return chargepointsMerged.map {
"${it.count} × ${it.type} ${it.formatPower()}"

View File

@@ -86,8 +86,9 @@ class FavoritesFragment : Fragment(), LostApiClient.ConnectionCallbacks {
}
override fun onConnected() {
val context = this.context ?: return
if (ContextCompat.checkSelfPermission(
requireContext(),
context,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {

View File

@@ -1,14 +1,7 @@
package net.vonforst.evmap.fragment
import android.content.Context
import android.content.DialogInterface
import android.os.Bundle
import android.view.*
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.FrameLayout
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.databinding.DataBindingUtil
@@ -24,6 +17,7 @@ import net.vonforst.evmap.MapsActivity
import net.vonforst.evmap.R
import net.vonforst.evmap.adapter.FiltersAdapter
import net.vonforst.evmap.databinding.FragmentFilterBinding
import net.vonforst.evmap.ui.showEditTextDialog
import net.vonforst.evmap.viewmodel.FilterViewModel
import net.vonforst.evmap.viewmodel.viewModelFactory
@@ -96,54 +90,22 @@ class FilterFragment : Fragment() {
true
}
R.id.menu_save_profile -> {
val container = FrameLayout(requireContext())
container.setPadding(
(16 * resources.displayMetrics.density).toInt(), 0,
(16 * resources.displayMetrics.density).toInt(), 0
)
val input = EditText(requireContext())
input.isSingleLine = true
vm.filterProfile.value?.let { profile ->
input.setText(profile.name)
}
container.addView(input)
val dialog = AlertDialog.Builder(requireContext())
.setTitle(R.string.save_as_profile)
.setMessage(R.string.save_profile_enter_name)
.setView(container)
.setPositiveButton(R.string.ok) { di, button ->
lifecycleScope.launch {
vm.saveAsProfile(input.text.toString())
findNavController().popBackStack()
}
showEditTextDialog(requireContext()) { dialog, input ->
vm.filterProfile.value?.let { profile ->
input.setText(profile.name)
}
.setNegativeButton(R.string.cancel) { di, button ->
}.show()
// move dialog to top
val attrs = dialog.window?.attributes?.apply {
gravity = Gravity.TOP
}
dialog.window?.attributes = attrs
// focus and show keyboard
input.requestFocus()
input.postDelayed({
val imm =
requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(input, InputMethodManager.SHOW_IMPLICIT)
}, 100)
input.setOnEditorActionListener { _, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
val text = input.text
if (text != null) {
dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
return@setOnEditorActionListener true
dialog.setTitle(R.string.save_as_profile)
.setMessage(R.string.save_profile_enter_name)
.setPositiveButton(R.string.ok) { di, button ->
lifecycleScope.launch {
vm.saveAsProfile(input.text.toString())
findNavController().popBackStack()
}
}
.setNegativeButton(R.string.cancel) { di, button ->
}
}
false
}
true
}

View File

@@ -11,18 +11,23 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.launch
import net.vonforst.evmap.MapsActivity
import net.vonforst.evmap.R
import net.vonforst.evmap.adapter.DataBindingAdapter
import net.vonforst.evmap.adapter.FilterProfilesAdapter
import net.vonforst.evmap.databinding.FragmentFilterProfilesBinding
import net.vonforst.evmap.databinding.ItemFilterProfileBinding
import net.vonforst.evmap.storage.FilterProfile
import net.vonforst.evmap.ui.showEditTextDialog
import net.vonforst.evmap.viewmodel.FilterProfilesViewModel
import net.vonforst.evmap.viewmodel.viewModelFactory
@@ -34,6 +39,7 @@ class FilterProfilesFragment : Fragment() {
FilterProfilesViewModel(requireActivity().application)
}
})
private var deleteSnackbar: Snackbar? = null
override fun onCreateView(
inflater: LayoutInflater,
@@ -86,7 +92,8 @@ class FilterProfilesFragment : Fragment() {
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
vm.delete(viewHolder.itemId)
val fp = vm.filterProfiles.value?.find { it.id == viewHolder.itemId }
fp?.let { delete(it) }
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
@@ -166,7 +173,24 @@ class FilterProfilesFragment : Fragment() {
}
})
val adapter = FilterProfilesAdapter(touchHelper)
val adapter = FilterProfilesAdapter(touchHelper, onDelete = { fp ->
delete(fp)
}, onRename = { fp ->
showEditTextDialog(requireContext()) { dialog, input ->
input.setText(fp.name)
dialog.setTitle(R.string.rename)
.setMessage(R.string.save_profile_enter_name)
.setPositiveButton(R.string.ok) { di, button ->
lifecycleScope.launch {
vm.insert(fp.copy(name = input.text.toString()))
}
}
.setNegativeButton(R.string.cancel) { di, button ->
}
}
})
binding.filterProfilesList.apply {
this.adapter = adapter
layoutManager =
@@ -184,4 +208,21 @@ class FilterProfilesFragment : Fragment() {
findNavController().popBackStack()
}
}
fun delete(fp: FilterProfile) {
vm.delete(fp.id)
deleteSnackbar?.dismiss()
view?.let {
val snackbar = Snackbar.make(
it,
getString(R.string.deleted_filterprofile, fp.name),
Snackbar.LENGTH_LONG
).setAction(R.string.undo) {
vm.insert(fp.copy(id = 0))
}
deleteSnackbar = snackbar
snackbar.show()
}
}
}

View File

@@ -224,7 +224,11 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
)
if (!PreferenceDataSource(requireContext()).welcomeDialogShown) {
navController.navigate(R.id.action_map_to_welcome)
try {
navController.navigate(R.id.action_map_to_welcome)
} catch (ignored: IllegalArgumentException) {
// when there is already another navigation going on
}
}
}
@@ -593,7 +597,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
val charger = vm.chargerDetails.value?.data
if (charger != null) {
when (it.icon) {
R.drawable.ic_location -> {
R.drawable.ic_location, R.drawable.ic_address -> {
(activity as? MapsActivity)?.showLocation(charger)
}
R.drawable.ic_fault_report -> {
@@ -1054,9 +1058,10 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac
override fun onConnected() {
val map = this.map ?: return
val context = this.context ?: return
if (vm.myLocationEnabled.value == true) {
if (ActivityCompat.checkSelfPermission(
requireContext(),
context,
ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {

View File

@@ -2,6 +2,7 @@ package net.vonforst.evmap.ui
import android.content.Context
import android.content.res.ColorStateList
import android.text.SpannableString
import android.view.View
import android.view.ViewGroup.MarginLayoutParams
import android.widget.ImageView
@@ -25,6 +26,25 @@ fun goneUnless(view: View, visible: Boolean) {
view.visibility = if (visible) View.VISIBLE else View.GONE
}
@BindingAdapter("goneUnlessAnimated")
fun goneUnlessAnimated(view: View, oldValue: Boolean, newValue: Boolean) {
if (oldValue == newValue) return
view.animate().cancel()
if (newValue) {
view.visibility = View.VISIBLE
view.alpha = 0f
view.animate().alpha(1f).withEndAction {
view.alpha = 1f
}
} else {
view.animate().alpha(0f).withEndAction {
view.alpha = 1f
view.visibility = View.GONE
}
}
}
@BindingAdapter("invisibleUnless")
fun invisibleUnless(view: View, visible: Boolean) {
view.visibility = if (visible) View.VISIBLE else View.INVISIBLE
@@ -131,6 +151,26 @@ fun setTopMargin(view: View, topMargin: Float) {
view.layoutParams = layoutParams
}
/**
* Linkify is already possible using the autoLink and linksClickable attributes, but this does not
* remove spans correctly. So we implement a new version that manually removes the spans.
*/
@BindingAdapter("linkify")
fun setLinkify(textView: TextView, oldValue: Int, newValue: Int) {
if (oldValue == newValue) return
textView.autoLinkMask = newValue
textView.linksClickable = newValue != 0
// remove spans
val text = textView.text
if (newValue == 0 && text != null && text is SpannableString) {
text.getSpans(0, text.length, Any::class.java).forEach {
text.removeSpan(it)
}
}
}
private fun availabilityColor(
status: List<ChargepointStatus>?,
context: Context
@@ -160,4 +200,8 @@ fun availabilityText(status: List<ChargepointStatus>?): String? {
return if (unknown > 0) {
if (unknown == total) "?" else "$available?"
} else available.toString()
}
fun flatten(it: Iterable<Iterable<ChargepointStatus>>?): List<ChargepointStatus>? {
return it?.flatten()
}

View File

@@ -0,0 +1,63 @@
package net.vonforst.evmap.ui
import android.content.Context
import android.content.DialogInterface
import android.view.Gravity
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.FrameLayout
import androidx.appcompat.app.AlertDialog
private fun dialogEditText(ctx: Context): Pair<View, EditText> {
val container = FrameLayout(ctx)
container.setPadding(
(16 * ctx.resources.displayMetrics.density).toInt(), 0,
(16 * ctx.resources.displayMetrics.density).toInt(), 0
)
val input = EditText(ctx)
input.isSingleLine = true
container.addView(input)
return container to input
}
fun showEditTextDialog(
ctx: Context,
customize: (AlertDialog.Builder, EditText) -> Unit
): AlertDialog {
val (container, input) = dialogEditText(ctx)
val dialogBuilder = AlertDialog.Builder(ctx)
.setView(container)
customize(dialogBuilder, input)
val dialog = dialogBuilder.show()
// move dialog to top
val attrs = dialog.window?.attributes?.apply {
gravity = Gravity.TOP
}
dialog.window?.attributes = attrs
// focus and show keyboard
input.requestFocus()
input.postDelayed({
val imm =
ctx.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(input, InputMethodManager.SHOW_IMPLICIT)
}, 100)
input.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
val text = input.text
val button = dialog.getButton(DialogInterface.BUTTON_POSITIVE)
if (text != null && button != null) {
button.performClick()
return@setOnEditorActionListener true
}
}
false
}
return dialog
}

View File

@@ -27,6 +27,12 @@ class FilterProfilesViewModel(application: Application) : AndroidViewModel(appli
}
}
fun insert(item: FilterProfile) {
viewModelScope.launch {
db.filterProfileDao().insert(item)
}
}
fun reorderProfiles(list: List<FilterProfile>) {
viewModelScope.launch {
db.filterProfileDao().update(*list.toTypedArray())

View File

@@ -21,6 +21,8 @@
<import type="net.vonforst.evmap.viewmodel.Status" />
<import type="net.vonforst.evmap.ui.BindingAdaptersKt" />
<variable
name="charger"
type="Resource&lt;ChargeLocation&gt;" />
@@ -41,6 +43,10 @@
name="filteredChargeCards"
type="java.util.Set&lt;Long&gt;" />
<variable
name="expanded"
type="Boolean" />
</data>
<androidx.cardview.widget.CardView
@@ -59,14 +65,15 @@
android:paddingBottom="8dp">
<TextView
android:id="@+id/textView"
android:id="@+id/txtName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="1"
android:text="@{charger.data.name}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintEnd_toStartOf="@+id/txtAvailability"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toTopOf="parent"
tools:text="Parkhaus" />
@@ -81,11 +88,11 @@
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toBottomOf="@+id/textView"
app:layout_constraintTop_toBottomOf="@+id/txtName"
tools:text="Beispielstraße 10, 12345 Berlin" />
<TextView
android:id="@+id/textView27"
android:id="@+id/txtDistance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
@@ -95,12 +102,33 @@
android:minWidth="50dp"
android:text="@{@string/distance_format(distance)}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
app:layout_constraintBottom_toBottomOf="@+id/topPart"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/textView3"
tools:text="10 km" />
<TextView
android:id="@+id/textView3"
android:id="@+id/txtAvailability"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="72dp"
android:background="@drawable/rounded_rect"
android:ellipsize="end"
android:gravity="end"
android:maxLines="1"
android:padding="2dp"
android:text="@{String.format(&quot;%s/%d&quot;, BindingAdaptersKt.availabilityText(BindingAdaptersKt.flatten(availability.data.status.values())), charger.data.totalChargepoints)}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
android:textColor="@android:color/white"
app:backgroundTintAvailability="@{BindingAdaptersKt.flatten(availability.data.status.values())}"
app:goneUnlessAnimated="@{availability.data != null &amp;&amp; !expanded}"
app:goneUnless="@{availability.data != null}"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/txtName"
tools:backgroundTint="@color/available"
tools:text="2/2" />
<TextView
android:id="@+id/txtConnectors"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
@@ -108,7 +136,7 @@
android:maxLines="1"
android:text="@{charger.data.formatChargepoints()}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
app:layout_constraintEnd_toStartOf="@+id/textView27"
app:layout_constraintEnd_toStartOf="@+id/txtDistance"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toBottomOf="@+id/textView2"
tools:text="2x Typ 2 22 kW" />
@@ -136,7 +164,7 @@
android:textColor="?colorPrimary"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toBottomOf="@+id/textView3" />
app:layout_constraintTop_toBottomOf="@+id/txtConnectors" />
<TextView
android:id="@+id/textView12"
@@ -263,10 +291,10 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="@+id/textView3"
app:layout_constraintBottom_toBottomOf="@+id/txtConnectors"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toTopOf="@+id/textView" />
app:layout_constraintTop_toTopOf="@+id/txtName" />
<Button
android:id="@+id/btnChargeprice"

View File

@@ -7,7 +7,8 @@
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="0dp"
android:layout_weight="1">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/linearLayout4"
@@ -91,7 +92,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="&gt;11 kW"
android:text="11 kW"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
app:layout_constraintEnd_toStartOf="@+id/iconLabel3"
app:layout_constraintHorizontal_bias="0.5"
@@ -115,7 +116,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="&gt;20 kW"
android:text="20 kW"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
app:layout_constraintEnd_toStartOf="@+id/iconLabel4"
app:layout_constraintHorizontal_bias="0.5"
@@ -139,7 +140,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="&gt;43 kW"
android:text="43 kW"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
app:layout_constraintEnd_toStartOf="@+id/iconLabel5"
app:layout_constraintHorizontal_bias="0.5"
@@ -163,7 +164,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="&gt;100 kW"
android:text="100 kW"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"

View File

@@ -9,6 +9,8 @@
<import type="net.vonforst.evmap.viewmodel.Status" />
<import type="com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike" />
<variable
name="vm"
type="net.vonforst.evmap.viewmodel.MapViewModel" />
@@ -139,7 +141,8 @@
app:availability="@{vm.availability}"
app:chargeCards="@{vm.chargeCardMap}"
app:filteredChargeCards="@{vm.filteredChargeCards}"
app:distance="@{vm.chargerDistance}" />
app:distance="@{vm.chargerDistance}"
app:expanded="@{vm.bottomSheetState != BottomSheetBehaviorGoogleMapsLike.STATE_COLLAPSED &amp;&amp; vm.bottomSheetState != BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN}" />
</androidx.core.widget.NestedScrollView>

View File

@@ -18,7 +18,7 @@
app:selectableItemBackground="@{item.clickable}">
<TextView
android:id="@+id/textView9"
android:id="@+id/txtTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
@@ -48,7 +48,7 @@
tools:srcCompat="@drawable/ic_address" />
<TextView
android:id="@+id/textView8"
android:id="@+id/txtContent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
@@ -56,13 +56,12 @@
android:layout_marginBottom="14dp"
android:text="@{item.detailText}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
android:autoLink="@{item.links ? Linkify.WEB_URLS | Linkify.PHONE_NUMBERS : 0}"
android:linksClickable="@{item.links}"
app:linkify="@{item.links ? Linkify.WEB_URLS | Linkify.PHONE_NUMBERS : 0}"
app:goneUnless="@{item.detailText != null}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/textView9"
app:layout_constraintTop_toBottomOf="@+id/textView9"
app:layout_constraintStart_toStartOf="@+id/txtTitle"
app:layout_constraintTop_toBottomOf="@+id/txtTitle"
tools:text="Lorem ipsum" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -24,7 +24,7 @@
app:selectableItemBackground="@{item.clickable}">
<TextView
android:id="@+id/textView9"
android:id="@+id/txtTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
@@ -54,21 +54,20 @@
tools:srcCompat="@drawable/ic_address" />
<TextView
android:id="@+id/textView8"
android:id="@+id/txtContent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
android:autoLink="@{item.links ? Linkify.WEB_URLS | Linkify.PHONE_NUMBERS : 0}"
android:linksClickable="@{item.links}"
app:linkify="@{item.links ? Linkify.WEB_URLS | Linkify.PHONE_NUMBERS : 0}"
android:text="@{item.detailText}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
app:goneUnless="@{item.detailText != null}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/expandToggle"
app:layout_constraintStart_toStartOf="@+id/textView9"
app:layout_constraintTop_toBottomOf="@+id/textView9"
app:layout_constraintStart_toStartOf="@+id/txtTitle"
app:layout_constraintTop_toBottomOf="@+id/txtTitle"
app:layout_constraintVertical_bias="0.0"
tools:text="Lorem ipsum" />
@@ -83,8 +82,8 @@
app:goneUnless="@{expandToggle.checked}"
app:hours="@{item.hoursDays}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/textView9"
app:layout_constraintTop_toBottomOf="@+id/textView8" />
app:layout_constraintStart_toStartOf="@+id/txtTitle"
app:layout_constraintTop_toBottomOf="@+id/txtContent" />
<include
android:id="@+id/hours_tue"
@@ -97,7 +96,7 @@
app:dayOfWeek="@{DayOfWeek.TUESDAY}"
app:hours="@{item.hoursDays}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/textView9"
app:layout_constraintStart_toStartOf="@+id/txtTitle"
app:layout_constraintTop_toBottomOf="@id/hours_mon" />
<include
@@ -111,7 +110,7 @@
app:dayOfWeek="@{DayOfWeek.WEDNESDAY}"
app:hours="@{item.hoursDays}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/textView9"
app:layout_constraintStart_toStartOf="@+id/txtTitle"
app:layout_constraintTop_toBottomOf="@id/hours_tue" />
<include
@@ -125,7 +124,7 @@
app:dayOfWeek="@{DayOfWeek.THURSDAY}"
app:hours="@{item.hoursDays}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/textView9"
app:layout_constraintStart_toStartOf="@+id/txtTitle"
app:layout_constraintTop_toBottomOf="@id/hours_wed" />
<include
@@ -139,7 +138,7 @@
app:dayOfWeek="@{DayOfWeek.FRIDAY}"
app:hours="@{item.hoursDays}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/textView9"
app:layout_constraintStart_toStartOf="@+id/txtTitle"
app:layout_constraintTop_toBottomOf="@id/hours_thu" />
<include
@@ -153,7 +152,7 @@
app:dayOfWeek="@{DayOfWeek.SATURDAY}"
app:hours="@{item.hoursDays}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/textView9"
app:layout_constraintStart_toStartOf="@+id/txtTitle"
app:layout_constraintTop_toBottomOf="@id/hours_fri" />
<include
@@ -167,7 +166,7 @@
app:dayOfWeek="@{DayOfWeek.SUNDAY}"
app:hours="@{item.hoursDays}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/textView9"
app:layout_constraintStart_toStartOf="@+id/txtTitle"
app:layout_constraintTop_toBottomOf="@id/hours_sat" />
<include
@@ -183,7 +182,7 @@
app:hours="@{item.hoursDays}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/textView9"
app:layout_constraintStart_toStartOf="@+id/txtTitle"
app:layout_constraintTop_toBottomOf="@id/hours_sun" />
<ToggleButton

View File

@@ -45,7 +45,7 @@
tools:text="Beispielstraße 10, 12345 Berlin" />
<TextView
android:id="@+id/textView3"
android:id="@+id/txtConnectors"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"

View File

@@ -29,6 +29,7 @@
android:layout_marginRight="16dp"
app:tint="@android:color/white"
app:srcCompat="@drawable/ic_delete" />
</FrameLayout>
@@ -43,17 +44,42 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginEnd="8dp"
android:text="@{item.name}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/handle"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintEnd_toStartOf="@+id/btnRename"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.511"
tools:text="Lorem ipsum" />
<ImageButton
android:id="@+id/btnDelete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:tint="?colorControlNormal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/handle"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_delete"
android:contentDescription="@string/delete" />
<ImageButton
android:id="@+id/btnRename"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:tint="?colorControlNormal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/btnDelete"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_edit"
android:contentDescription="@string/rename" />
<ImageView
android:id="@+id/handle"
android:layout_width="wrap_content"

View File

@@ -148,9 +148,12 @@
<string name="filterprofiles_empty_state">Du hast noch keine Filterprofile gespeichert.</string>
<string name="welcome_to_evmap">Willkommen bei EVMap!</string>
<string name="welcome_1">Mit EVMap kannst du Ladestationen für Elektroautos in deiner Nähe finden. EVMap nutzt dafür die Community-gepflegte Datenbank von GoingElectric.de, die sich vor allem auf Europa und den deutschsprachigen Raum konzentriert. Über die Website GoingElectric.de kannst du selbst zum Verzeichnis beitragen.\n\nDie Ladestationen werden auf der Karte mit verschiedenen Farben angezeigt, die die maximale Ladeleistung angeben:</string>
<string name="welcome_2">EVMap ist kostenlos und Open Source. Du kannst bei GitHub zur Weiterentwicklung beitragen oder die Entwicklung mit Spenden unterstützen. Die entsprechenden Links findest du unter Über EVMap” im Menü.</string>
<string name="welcome_2">EVMap ist kostenlos und Open Source. Du kannst bei GitHub zur Weiterentwicklung beitragen oder die Entwicklung mit Spenden unterstützen. Die entsprechenden Links findest du unter Über EVMap” im Menü.</string>
<string name="deleted_filterprofile">„%s” gelöscht</string>
<string name="undo">Rückgängig</string>
<string name="rename">Umbenennen</string>
<plurals name="charge_cards_compatible_num">
<item quantity="one">%d kompatibler Ladetarif</item>
<item quantity="other">%d kompatible Ladetarife</item>
</plurals>
</resources>
</resources>

View File

@@ -148,6 +148,9 @@
<string name="welcome_to_evmap">Welcome to EVMap!</string>
<string name="welcome_1">Using EVMap, you can find electric vehicle chargers around you. EVMap uses the community-maintained database from GoingElectric.de, which focuses on chargers in Europe and the German-speaking countries. You can contribute to this database on the GoingElectric.de website.\n\nChargers are shown on the map in different colors, which correspond to their maximum charging power:</string>
<string name="welcome_2">EVMap is free and Open Source software. You can contribute to the development on GitHub or support me through donations. The corresponding links can be found under “About EVMap” in the menu.</string>
<string name="deleted_filterprofile">Deleted “%s”</string>
<string name="undo">Undo</string>
<string name="rename">Rename</string>
<plurals name="charge_cards_compatible_num">
<item quantity="one">%d compatible payment method</item>
<item quantity="other">%d compatible payment methods</item>

View File

@@ -0,0 +1,9 @@
Neue Funktionen:
- Filterprofile verwalten: Möglichkeit zum Löschen und Umbennenen der angelegten Filterprofile
- Verfügbarkeit der Ladestationen wird auch direkt in der Schnellansicht angezeigt
Verbesserungen:
- Fehlender OK-Button beim Willkommensdialog auf kleinen Bildschirmen behoben
- Nicht funktionierender Navigationsbutton auf Android 11 behoben
- Tippfehler behoben
- Fehlerhafte Links bei den Koordinaten behoben

View File

@@ -0,0 +1,2 @@
Verbesserungen:
- Verschiedene Abstürze behoben

View File

@@ -0,0 +1,9 @@
New features:
- Manage filter profiles: Buttons to delete and rename filter profiles
- Real-time availability also displayed in quick view
Improvements:
- Fixed missing OK button of welcome dialog on small screens
- Fixed not working navigation button on Android 11
- Fixed typos
- Removed incorrect links on charger coordinates

View File

@@ -0,0 +1,2 @@
Improvements:
- Fixed various crashes