Default server; works like a charm; now we just need the wording in the "add dialog" to be just right

This commit is contained in:
Philipp Heckel 2022-02-15 16:16:46 -05:00
parent ab0f707501
commit 1d916de81e
13 changed files with 53 additions and 212 deletions

View file

@ -293,20 +293,6 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
.apply()
}
fun getUnifiedPushEnabled(): Boolean {
return sharedPrefs.getBoolean(SHARED_PREFS_UNIFIED_PUSH_ENABLED, true) // Enabled by default
}
fun setUnifiedPushEnabled(enabled: Boolean) {
sharedPrefs.edit()
.putBoolean(SHARED_PREFS_UNIFIED_PUSH_ENABLED, enabled)
.apply()
}
fun getUnifiedPushBaseUrl(): String? {
return sharedPrefs.getString(SHARED_PREFS_UNIFIED_PUSH_BASE_URL, null)
}
fun setUnifiedPushBaseUrl(baseUrl: String) {
if (baseUrl == "") {
sharedPrefs
@ -321,17 +307,20 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
}
fun getDefaultBaseUrl(): String? {
return sharedPrefs.getString(SHARED_PREFS_DEFAULT_BASE_URL, null)
return sharedPrefs.getString(SHARED_PREFS_DEFAULT_BASE_URL, null) ?:
sharedPrefs.getString(SHARED_PREFS_UNIFIED_PUSH_BASE_URL, null) // Fall back to UP URL, removed when default is set!
}
fun setDefaultBaseUrl(baseUrl: String) {
if (baseUrl == "") {
sharedPrefs
.edit()
.remove(SHARED_PREFS_UNIFIED_PUSH_BASE_URL) // Remove legacy key
.remove(SHARED_PREFS_DEFAULT_BASE_URL)
.apply()
} else {
sharedPrefs.edit()
.remove(SHARED_PREFS_UNIFIED_PUSH_BASE_URL) // Remove legacy key
.putString(SHARED_PREFS_DEFAULT_BASE_URL, baseUrl)
.apply()
}

View file

@ -367,11 +367,7 @@ class AddFragment : DialogFragment() {
activity.runOnUiThread {
val topic = subscribeTopicText.text.toString()
val baseUrl = getBaseUrl()
val instant = if (!BuildConfig.FIREBASE_AVAILABLE || subscribeUseAnotherServerCheckbox.isChecked) {
true
} else {
subscribeInstantDeliveryCheckbox.isChecked
}
val instant = !BuildConfig.FIREBASE_AVAILABLE || baseUrl != appBaseUrl
subscribeListener.onSubscribe(topic, baseUrl, instant)
dialog?.dismiss()
}
@ -381,7 +377,7 @@ class AddFragment : DialogFragment() {
return if (subscribeUseAnotherServerCheckbox.isChecked) {
subscribeBaseUrlText.text.toString()
} else {
return repository.getDefaultBaseUrl() ?: appBaseUrl
return defaultBaseUrl ?: appBaseUrl
}
}

View file

@ -265,10 +265,6 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
onInstantEnableClick(enable = false)
true
}
R.id.detail_menu_instant_info -> {
onInstantInfoClick()
true
}
R.id.detail_menu_copy_url -> {
onCopyUrlClick()
true
@ -419,28 +415,19 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
}
}
private fun onInstantInfoClick() {
Log.d(TAG, "Showing instant info toast")
Toast.makeText(this@DetailActivity, getString(R.string.detail_instant_info), Toast.LENGTH_LONG)
.show()
}
private fun showHideInstantMenuItems(enable: Boolean) {
subscriptionInstant = enable
runOnUiThread {
val appBaseUrl = getString(R.string.app_base_url)
val enableInstantItem = menu.findItem(R.id.detail_menu_enable_instant)
val disableInstantItem = menu.findItem(R.id.detail_menu_disable_instant)
val instantInfoItem = menu.findItem(R.id.detail_menu_instant_info)
val allowToggleInstant = BuildConfig.FIREBASE_AVAILABLE && subscriptionBaseUrl == appBaseUrl
if (allowToggleInstant) {
enableInstantItem?.isVisible = !subscriptionInstant
disableInstantItem?.isVisible = subscriptionInstant
instantInfoItem?.isVisible = false
} else {
enableInstantItem?.isVisible = false
disableInstantItem?.isVisible = false
instantInfoItem?.isVisible = BuildConfig.FIREBASE_AVAILABLE
}
}
}

View file

@ -371,7 +371,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
}
override fun onSubscribe(topic: String, baseUrl: String, instant: Boolean) {
Log.d(TAG, "Adding subscription ${topicShortUrl(baseUrl, topic)}")
Log.d(TAG, "Adding subscription ${topicShortUrl(baseUrl, topic)} (instant = $instant)")
// Add subscription to database
val subscription = Subscription(
@ -390,7 +390,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
// Subscribe to Firebase topic if ntfy.sh (even if instant, just to be sure!)
if (baseUrl == appBaseUrl) {
Log.d(TAG, "Subscribing to Firebase")
Log.d(TAG, "Subscribing to Firebase topic $topic")
messenger.subscribe(topic)
}
@ -401,7 +401,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
val notifications = api.poll(subscription.id, subscription.baseUrl, subscription.topic, user)
notifications.forEach { notification -> repository.addNotification(notification) }
} catch (e: Exception) {
Log.e(TAG, "Unable to fetch notifications: ${e.stackTrace}")
Log.e(TAG, "Unable to fetch notifications: ${e.message}", e)
}
}

View file

@ -283,50 +283,8 @@ class SettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPrefere
}
}
// UnifiedPush enabled
val upEnabledPrefId = context?.getString(R.string.settings_unified_push_enabled_key) ?: return
val upEnabled: SwitchPreference? = findPreference(upEnabledPrefId)
upEnabled?.isChecked = repository.getUnifiedPushEnabled()
upEnabled?.preferenceDataStore = object : PreferenceDataStore() {
override fun putBoolean(key: String?, value: Boolean) {
repository.setUnifiedPushEnabled(value)
}
override fun getBoolean(key: String?, defValue: Boolean): Boolean {
return repository.getUnifiedPushEnabled()
}
}
upEnabled?.summaryProvider = Preference.SummaryProvider<SwitchPreference> { pref ->
if (pref.isChecked) {
getString(R.string.settings_unified_push_enabled_summary_on)
} else {
getString(R.string.settings_unified_push_enabled_summary_off)
}
}
// UnifiedPush Base URL
val appBaseUrl = context?.getString(R.string.app_base_url) ?: return
val upBaseUrlPrefId = context?.getString(R.string.settings_unified_push_base_url_key) ?: return
val upBaseUrl: EditTextPreference? = findPreference(upBaseUrlPrefId)
upBaseUrl?.text = repository.getUnifiedPushBaseUrl() ?: ""
upBaseUrl?.preferenceDataStore = object : PreferenceDataStore() {
override fun putString(key: String, value: String?) {
val baseUrl = value ?: return
repository.setUnifiedPushBaseUrl(baseUrl)
}
override fun getString(key: String, defValue: String?): String? {
return repository.getUnifiedPushBaseUrl()
}
}
upBaseUrl?.summaryProvider = Preference.SummaryProvider<EditTextPreference> { pref ->
if (TextUtils.isEmpty(pref.text)) {
getString(R.string.settings_unified_push_base_url_default_summary, appBaseUrl)
} else {
pref.text
}
}
// Default Base URL
val appBaseUrl = getString(R.string.app_base_url)
val defaultBaseUrlPrefId = context?.getString(R.string.settings_advanced_default_base_url_key) ?: return
val defaultBaseUrl: EditTextPreference? = findPreference(defaultBaseUrlPrefId)
defaultBaseUrl?.text = repository.getDefaultBaseUrl() ?: ""

View file

@ -29,6 +29,7 @@ class ShareActivity : AppCompatActivity() {
// Context-dependent things
private lateinit var appBaseUrl: String
private var defaultBaseUrl: String? = null
// UI elements
private lateinit var menu: Menu
@ -42,7 +43,7 @@ class ShareActivity : AppCompatActivity() {
private lateinit var baseUrlLayout: TextInputLayout
private lateinit var baseUrlText: AutoCompleteTextView
private lateinit var useAnotherServerCheckbox: CheckBox
private lateinit var lastTopicsList: RecyclerView
private lateinit var suggestedTopicsList: RecyclerView
private lateinit var progress: ProgressBar
private lateinit var errorText: TextView
private lateinit var errorImage: ImageView
@ -62,6 +63,7 @@ class ShareActivity : AppCompatActivity() {
// Context-dependent things
appBaseUrl = getString(R.string.app_base_url)
defaultBaseUrl = repository.getDefaultBaseUrl()
// UI elements
val root: View = findViewById(R.id.share_root_view)
@ -76,8 +78,9 @@ class ShareActivity : AppCompatActivity() {
baseUrlLayout.makeEndIconSmaller(resources) // Hack!
baseUrlText = findViewById(R.id.share_base_url_text)
baseUrlText.background = root.background
baseUrlText.hint = defaultBaseUrl ?: appBaseUrl
useAnotherServerCheckbox = findViewById(R.id.share_use_another_server_checkbox)
lastTopicsList = findViewById(R.id.share_last_topics)
suggestedTopicsList = findViewById(R.id.share_suggested_topics)
progress = findViewById(R.id.share_progress)
progress.visibility = View.GONE
errorText = findViewById(R.id.share_error_text)
@ -113,19 +116,25 @@ class ShareActivity : AppCompatActivity() {
val lastShareTopics = repository.getLastShareTopics()
val subscribedTopics = subscriptions
.map { topicUrl(it.baseUrl, it.topic) }
.toSet()
.subtract(lastShareTopics.toSet())
val suggestedTopics = lastShareTopics.reversed() + subscribedTopics
val baseUrls = suggestedTopics
val suggestedTopics = (lastShareTopics.reversed() + subscribedTopics).distinct()
val baseUrlsRaw = suggestedTopics
.mapNotNull {
try { splitTopicUrl(it).first }
catch (_: Exception) { null }
}
.filterNot { it == appBaseUrl }
lastTopicsList.adapter = TopicAdapter(suggestedTopics) { topicUrl ->
.distinct()
val baseUrls = if (defaultBaseUrl != null) {
baseUrlsRaw.filterNot { it == defaultBaseUrl }
} else {
baseUrlsRaw.filterNot { it == appBaseUrl }
}
suggestedTopicsList.adapter = TopicAdapter(suggestedTopics) { topicUrl ->
try {
val (baseUrl, topic) = splitTopicUrl(topicUrl)
topicText.text = topic
if (baseUrl == appBaseUrl) {
if (baseUrl == defaultBaseUrl) {
useAnotherServerCheckbox.isChecked = false
} else {
useAnotherServerCheckbox.isChecked = true
@ -143,7 +152,8 @@ class ShareActivity : AppCompatActivity() {
useAnotherServerCheckbox.isChecked = if (suggestedTopics.isNotEmpty()) {
try {
val (baseUrl, _) = splitTopicUrl(suggestedTopics.first())
baseUrl != appBaseUrl
val defaultUrl = defaultBaseUrl ?: appBaseUrl
baseUrl != defaultUrl
} catch (_: Exception) {
false
}
@ -254,6 +264,11 @@ class ShareActivity : AppCompatActivity() {
val topic = topicText.text.toString()
val message = contentText.text.toString()
progress.visibility = View.VISIBLE
contentText.isEnabled = false
topicText.isEnabled = false
useAnotherServerCheckbox.isEnabled = false
baseUrlText.isEnabled = false
suggestedTopicsList.isEnabled = false
lifecycleScope.launch(Dispatchers.IO) {
val user = repository.getUser(baseUrl)
try {
@ -316,7 +331,7 @@ class ShareActivity : AppCompatActivity() {
return if (useAnotherServerCheckbox.isChecked) {
baseUrlText.text.toString()
} else {
getString(R.string.app_base_url)
defaultBaseUrl ?: appBaseUrl
}
}

View file

@ -38,8 +38,8 @@ class BroadcastReceiver : android.content.BroadcastReceiver() {
val repository = app.repository
val distributor = Distributor(app)
Log.d(TAG, "REGISTER received for app $appId (connectorToken=$connectorToken)")
if (!repository.getUnifiedPushEnabled() || appId.isBlank()) {
Log.w(TAG, "Refusing registration: UnifiedPush disabled or empty application")
if (appId.isBlank()) {
Log.w(TAG, "Refusing registration: Empty application")
distributor.sendRegistrationRefused(appId, connectorToken)
return
}
@ -58,7 +58,7 @@ class BroadcastReceiver : android.content.BroadcastReceiver() {
}
// Add subscription
val baseUrl = repository.getUnifiedPushBaseUrl() ?: context.getString(R.string.app_base_url)
val baseUrl = repository.getDefaultBaseUrl() ?: context.getString(R.string.app_base_url)
val topic = UP_PREFIX + randomString(TOPIC_RANDOM_ID_LENGTH)
val endpoint = topicUrlUp(baseUrl, topic)
val subscription = Subscription(

View file

@ -137,7 +137,7 @@
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="@+id/share_last_topics"
android:id="@+id/share_suggested_topics"
app:layout_constraintTop_toBottomOf="@id/share_last_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@ -154,7 +154,7 @@
android:paddingEnd="4dp"
android:textAppearance="@style/DangerText"
app:layout_constraintStart_toEndOf="@id/share_error_image"
android:layout_marginTop="10dp" app:layout_constraintTop_toBottomOf="@id/share_last_topics"/>
android:layout_marginTop="10dp" app:layout_constraintTop_toBottomOf="@id/share_suggested_topics"/>
<ImageView
android:layout_width="20dp"
android:layout_height="20dp" app:srcCompat="@drawable/ic_error_red_24dp"

View file

@ -1,69 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
ntfy EDIT:
This is a slightly edited copy of the original Android project layout
to make wrapping the summary line work.
~ Copyright (C) 2015 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/selectableItemBackground"
android:baselineAligned="false"
android:layout_marginTop="16dp"
android:gravity="center_vertical">
<include layout="@layout/image_frame"/>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<TextView
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:textAlignment="viewStart"
style="@style/PreferenceCategoryTitleTextStyle"/>
<!-- EDITED singleLine -->
<TextView
android:id="@android:id/summary"
android:ellipsize="end"
android:singleLine="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignLeft="@android:id/title"
android:layout_alignStart="@android:id/title"
android:layout_gravity="start"
android:textAlignment="viewStart"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10"
style="@style/PreferenceSummaryTextStyle"/>
</RelativeLayout>
</LinearLayout>

View file

@ -39,7 +39,8 @@
android:layout_marginRight="24dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="4dp"
android:layout_marginBottom="2dp"
android:layout_marginTop="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary"/>

View file

@ -9,8 +9,6 @@
app:showAsAction="ifRoom" android:icon="@drawable/ic_bolt_outline_white_24dp"/>
<item android:id="@+id/detail_menu_disable_instant" android:title="@string/detail_menu_disable_instant"
android:icon="@drawable/ic_bolt_white_24dp" app:showAsAction="ifRoom"/>
<item android:id="@+id/detail_menu_instant_info" android:title="@string/detail_menu_instant_info"
android:icon="@drawable/ic_bolt_white_24dp" app:showAsAction="ifRoom"/>
<item android:id="@+id/detail_menu_test" android:title="@string/detail_menu_test"/>
<item android:id="@+id/detail_menu_copy_url" android:title="@string/detail_menu_copy_url"/>
<item android:id="@+id/detail_menu_clear" android:title="@string/detail_menu_clear"/>

View file

@ -82,10 +82,10 @@
<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_use_another_server_description">
You can subscribe to topics from your own server. This option requires a foreground service.
You can subscribe to topics from another server. This option requires a foreground service.
</string>
<string name="add_dialog_use_another_server_description_noinstant">
You can subscribe to topics from your own server. Type the server URL below.
You can subscribe to topics from another server. Type the server URL below.
</string>
<string name="add_dialog_instant_delivery">Instant delivery in doze mode</string>
<string name="add_dialog_instant_delivery_description">
@ -133,7 +133,6 @@
<string name="detail_copied_to_clipboard_message">Copied to clipboard</string>
<string name="detail_instant_delivery_enabled">Instant delivery enabled</string>
<string name="detail_instant_delivery_disabled">Instant delivery disabled</string>
<string name="detail_instant_info">Instant delivery is enabled</string>
<string name="detail_item_tags">Tags: %1$s</string>
<string name="detail_item_snack_deleted">Notification deleted</string>
<string name="detail_item_snack_undo">Undo</string>
@ -166,7 +165,6 @@
<string name="detail_menu_notifications_disabled_until">Notifications disabled until %1$s</string>
<string name="detail_menu_enable_instant">Enable instant delivery</string>
<string name="detail_menu_disable_instant">Disable instant delivery</string>
<string name="detail_menu_instant_info">Instant delivery enabled</string>
<string name="detail_menu_test">Send test notification</string>
<string name="detail_menu_copy_url">Copy topic address</string>
<string name="detail_menu_clear">Clear all notifications</string>
@ -226,6 +224,7 @@
<!-- Settings -->
<string name="settings_title">Settings</string>
<string name="settings_general_header">General</string>
<string name="settings_notifications_header">Notifications</string>
<string name="settings_notifications_muted_until_key">MutedUntil</string>
<string name="settings_notifications_muted_until_title">Pause notifications</string>
@ -289,20 +288,10 @@
<string name="settings_users_prefs_user_add">Add users</string>
<string name="settings_users_prefs_user_add_title">Add new user</string>
<string name="settings_users_prefs_user_add_summary">Create a new user for a new server</string>
<string name="settings_unified_push_header">UnifiedPush</string>
<string name="settings_unified_push_header_summary">Allows other apps to use ntfy as a message distributor. Find out more at unifiedpush.org.</string>
<string name="settings_unified_push_enabled_key">UnifiedPushEnabled</string>
<string name="settings_unified_push_enabled_title">Allow distributor use</string>
<string name="settings_unified_push_enabled_summary_on">Apps can use ntfy as distributor</string>
<string name="settings_unified_push_enabled_summary_off">Apps cannot use ntfy as distributor</string>
<string name="settings_unified_push_base_url_key">UnifiedPushBaseURL</string>
<string name="settings_unified_push_base_url_title">Server URL</string>
<string name="settings_unified_push_base_url_message">Set the root server URL to be used for new UnifiedPush topics here.</string>
<string name="settings_unified_push_base_url_default_summary">%1$s (default)</string>
<string name="settings_advanced_header">Advanced</string>
<string name="settings_advanced_default_base_url_key">DefaultBaseURL</string>
<string name="settings_advanced_default_base_url_title">Default Server URL</string>
<string name="settings_advanced_default_base_url_message">Set the default server URL to be used for new topics here. Topics on other hosts can still be subscribed to using the "use another server" checkbox.</string>
<string name="settings_advanced_default_base_url_title">Default server</string>
<string name="settings_advanced_default_base_url_message">To use your own server as a default when subscribing to new topics and/or sharing to topics, enter the server base URL.</string>
<string name="settings_advanced_default_base_url_default_summary">%1$s (default)</string>
<string name="settings_advanced_broadcast_key">BroadcastEnabled</string>
<string name="settings_advanced_broadcast_title">Broadcast messages</string>

View file

@ -1,8 +1,6 @@
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
app:title="@string/settings_title">
<PreferenceCategory
app:title="@string/settings_notifications_header"
app:layout="@layout/preference_category_material_edited">
<PreferenceCategory app:title="@string/settings_notifications_header">
<ListPreference
app:key="@string/settings_notifications_muted_until_key"
app:title="@string/settings_notifications_muted_until_title"
@ -28,46 +26,25 @@
app:entryValues="@array/settings_notifications_auto_delete_values"
app:defaultValue="2592000"/>
</PreferenceCategory>
<PreferenceCategory
app:title="@string/settings_appearance_header"
app:layout="@layout/preference_category_material_edited">
<PreferenceCategory app:title="@string/settings_general_header">
<EditTextPreference
app:key="@string/settings_advanced_default_base_url_key"
app:title="@string/settings_advanced_default_base_url_title"
app:dialogLayout="@layout/preference_dialog_edittext_edited"
app:dialogMessage="@string/settings_advanced_default_base_url_message"/>
<ListPreference
app:key="@string/settings_appearance_dark_mode_key"
app:title="@string/settings_appearance_dark_mode_title"
app:entries="@array/settings_appearance_dark_mode_entries"
app:entryValues="@array/settings_appearance_dark_mode_values"
app:defaultValue="-1"/>
</PreferenceCategory>
<PreferenceCategory
app:title="@string/settings_users_header"
app:layout="@layout/preference_category_material_edited">
<Preference
app:key="@string/settings_users_key"
app:title="@string/settings_users_title"
app:summary="@string/settings_users_summary"
app:fragment="io.heckel.ntfy.ui.SettingsActivity$UserSettingsFragment"/>
</PreferenceCategory>
<PreferenceCategory
app:title="@string/settings_unified_push_header"
app:summary="@string/settings_unified_push_header_summary"
app:layout="@layout/preference_category_material_edited">
<SwitchPreference
app:key="@string/settings_unified_push_enabled_key"
app:title="@string/settings_unified_push_enabled_title"
app:enabled="true"/>
<EditTextPreference
app:key="@string/settings_unified_push_base_url_key"
app:title="@string/settings_unified_push_base_url_title"
app:dependency="@string/settings_unified_push_enabled_key"
app:dialogLayout="@layout/preference_dialog_edittext_edited"
app:dialogMessage="@string/settings_unified_push_base_url_message"/>
</PreferenceCategory>
<PreferenceCategory app:title="@string/settings_advanced_header">
<EditTextPreference
app:key="@string/settings_advanced_default_base_url_key"
app:title="@string/settings_advanced_default_base_url_title"
app:dialogLayout="@layout/preference_dialog_edittext_edited"
app:dialogMessage="@string/settings_advanced_default_base_url_message"/>
<SwitchPreference
app:key="@string/settings_advanced_broadcast_key"
app:title="@string/settings_advanced_broadcast_title"