Add dialog instead of activity

This commit is contained in:
Philipp Heckel 2021-10-27 22:25:02 -04:00
parent 0c3a14d528
commit 170bdc2485
10 changed files with 141 additions and 124 deletions

View file

@ -63,4 +63,5 @@ dependencies {
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$rootProject.liveDataVersion"
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
}

View file

@ -1,20 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.heckel.ntfy">
@ -36,5 +20,4 @@
<activity android:name="io.heckel.ntfy.add.AddTopicActivity" />
<activity android:name="io.heckel.ntfy.detail.DetailActivity" />
</application>
</manifest>

View file

@ -0,0 +1,80 @@
package io.heckel.ntfy
import android.app.AlertDialog
import android.app.Dialog
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import android.widget.CheckBox
import androidx.fragment.app.DialogFragment
import com.google.android.material.textfield.TextInputEditText
class AddFragment(private val listener: Listener) : DialogFragment() {
interface Listener {
fun onAddClicked(topic: String, baseUrl: String)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
// Build root view
val view = requireActivity().layoutInflater.inflate(R.layout.fragment_add, null)
val topicNameText = view.findViewById(R.id.add_dialog_topic_text) as TextInputEditText
val baseUrlText = view.findViewById(R.id.add_dialog_base_url_text) as TextInputEditText
val useAnotherServerCheckbox = view.findViewById(R.id.add_dialog_use_another_server_checkbox) as CheckBox
useAnotherServerCheckbox.setOnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) baseUrlText.visibility = View.VISIBLE
else baseUrlText.visibility = View.GONE
}
// Build dialog
val alert = AlertDialog.Builder(it)
.setView(view)
.setPositiveButton(R.string.add_dialog_button_subscribe) { _, _ ->
val topic = topicNameText.text.toString()
val baseUrl = if (useAnotherServerCheckbox.isChecked) {
baseUrlText.text.toString()
} else {
getString(R.string.add_dialog_base_url_default)
}
listener.onAddClicked(topic, baseUrl)
}
.setNegativeButton(R.string.add_dialog_button_cancel) { _, _ ->
dialog?.cancel()
}
.create()
// Add logic to disable "Subscribe" button on invalid input
alert.setOnShowListener {
val dialog = it as AlertDialog
val subscribeButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE)
subscribeButton.isEnabled = false
val textWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
if (useAnotherServerCheckbox.isChecked) {
subscribeButton.isEnabled = topicNameText.text.toString().isNotBlank()
&& "[-_A-Za-z0-9]+".toRegex().matches(topicNameText.text.toString())
&& baseUrlText.text.toString().isNotBlank()
&& "^https?://.+".toRegex().matches(baseUrlText.text.toString())
} else {
subscribeButton.isEnabled = topicNameText.text.toString().isNotBlank()
&& "[-_A-Za-z0-9]+".toRegex().matches(topicNameText.text.toString())
}
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
// Nothing
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
// Nothing
}
}
topicNameText.addTextChangedListener(textWatcher)
baseUrlText.addTextChangedListener(textWatcher)
}
alert
} ?: throw IllegalStateException("Activity cannot be null")
}
}

View file

@ -1,6 +1,5 @@
package io.heckel.ntfy
import android.app.Activity
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
@ -13,17 +12,16 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.recyclerview.widget.RecyclerView
import io.heckel.ntfy.add.AddTopicActivity
import io.heckel.ntfy.data.*
import io.heckel.ntfy.data.Notification
import io.heckel.ntfy.data.Status
import io.heckel.ntfy.data.Subscription
import io.heckel.ntfy.data.topicShortUrl
import io.heckel.ntfy.detail.DetailActivity
import kotlin.random.Random
const val SUBSCRIPTION_ID = "topic_id"
const val TOPIC_NAME = "topic_name"
const val SERVICE_BASE_URL = "base_url"
class MainActivity : AppCompatActivity() {
private val newSubscriptionActivityRequestCode = 1
class MainActivity : AppCompatActivity(), AddFragment.Listener {
private val subscriptionViewModel by viewModels<SubscriptionsViewModel> {
SubscriptionsViewModelFactory()
}
@ -64,21 +62,13 @@ class MainActivity : AppCompatActivity() {
/* Adds topic to topicList when FAB is clicked. */
private fun fabOnClick() {
val intent = Intent(this, AddTopicActivity::class.java)
startActivityForResult(intent, newSubscriptionActivityRequestCode)
val newFragment = AddFragment(this)
newFragment.show(supportFragmentManager, "AddFragment")
}
override fun onActivityResult(requestCode: Int, resultCode: Int, intentData: Intent?) {
super.onActivityResult(requestCode, resultCode, intentData)
if (requestCode == newSubscriptionActivityRequestCode && resultCode == Activity.RESULT_OK) {
intentData?.let { data ->
val name = data.getStringExtra(TOPIC_NAME) ?: return
val baseUrl = data.getStringExtra(SERVICE_BASE_URL) ?: return
val subscription = Subscription(Random.nextLong(), name, baseUrl, Status.CONNECTING, 0)
subscriptionViewModel.add(subscription)
}
}
override fun onAddClicked(topic: String, baseUrl: String) {
val subscription = Subscription(Random.nextLong(), topic, baseUrl, Status.CONNECTING, 0)
subscriptionViewModel.add(subscription)
}
private fun displayNotification(n: Notification) {

View file

@ -1,47 +0,0 @@
package io.heckel.ntfy.add
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.textfield.TextInputEditText
import io.heckel.ntfy.R
import io.heckel.ntfy.SERVICE_BASE_URL
import io.heckel.ntfy.TOPIC_NAME
class AddTopicActivity : AppCompatActivity() {
private lateinit var topicName: TextInputEditText
private lateinit var baseUrl: TextInputEditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.add_topic_layout)
findViewById<Button>(R.id.subscribe_button).setOnClickListener {
addTopic()
}
topicName = findViewById(R.id.add_topic_name)
baseUrl = findViewById(R.id.add_topic_base_url)
baseUrl.setText(R.string.topic_base_url_default_value)
}
/* The onClick action for the done button. Closes the activity and returns the new topic name
and description as part of the intent. If the name or description are missing, the result is set
to cancelled. */
private fun addTopic() {
val resultIntent = Intent()
// TODO don't allow this
if (baseUrl.text.isNullOrEmpty()) {
setResult(Activity.RESULT_CANCELED, resultIntent)
} else {
resultIntent.putExtra(TOPIC_NAME, topicName.text.toString())
resultIntent.putExtra(SERVICE_BASE_URL, baseUrl.text.toString())
setResult(Activity.RESULT_OK, resultIntent)
}
finish()
}
}

View file

@ -47,7 +47,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:hint="@string/topic_base_url_edit_text"
android:hint="@string/add_dialog_base_url_layout"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<com.google.android.material.textfield.TextInputEditText

View file

@ -0,0 +1,32 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<TextView
android:id="@+id/add_dialog_title_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:text="@string/add_dialog_title"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.AppCompat.Large"/>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/add_dialog_topic_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:hint="@string/add_dialog_topic_name_hint"/>
<CheckBox
android:text="@string/add_dialog_use_another_server"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:id="@+id/add_dialog_use_another_server_checkbox"/>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/add_dialog_base_url_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:visibility="gone"
android:hint="@string/add_dialog_base_url_hint"/>
</LinearLayout>

View file

@ -1,22 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<color name="colorPrimary">#6200EE</color>
<color name="colorPrimaryDark">#3700B3</color>
<color name="colorAccent">#03DAC5</color>
<color name="colorPrimary">#3949ab</color>
<color name="colorPrimaryDark">#3949ab</color>
<!--<color name="colorAccent">#03DAC5</color>-->
</resources>

View file

@ -4,7 +4,7 @@
<string name="topic_name_edit_text">Topic Name</string>
<string name="topic_base_url_edit_text">Service URL</string>
<string name="add_dialog_base_url_layout">Service URL</string>
<string name="topic_base_url_default_value">https://ntfy.sh</string>
<string name="subscribe_button_text">Subscribe</string>
<string name="status_connected">Connected</string>
@ -14,6 +14,16 @@
<string name="fab_content_description">fab</string>
<string name="remove_topic">Unsubscribe</string>
<!-- Notifications -->
<string name="notification_channel_name">Ntfy</string>
<string name="notification_channel_id">ntfy</string>
<!-- Add dialog -->
<string name="add_dialog_title">Subscribe to topic</string>
<string name="add_dialog_topic_name_hint">Topic name, e.g. phils_alerts</string>
<string name="add_dialog_use_another_server">Use another server</string>
<string name="add_dialog_base_url_hint">https://ntfy.sh</string>
<string name="add_dialog_base_url_default">https://ntfy.sh</string>
<string name="add_dialog_button_cancel">Cancel</string>
<string name="add_dialog_button_subscribe">Subscribe</string>
</resources>

View file

@ -1,28 +1,12 @@
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<!-- Base application theme. -->
<!-- No action bar, https://stackoverflow.com/a/36236700 -->
<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryVariant">@color/colorPrimaryDark</item>
<item name="colorSecondary">@color/colorAccent</item>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<!--<item name="colorSecondary">@color/colorAccent</item>-->
<!--<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>-->
<!--<item name="android:windowFullscreen">true</item>-->
</style>
</resources>