From d6288db492c25609f21af9b70a0c06a96ccaba6c Mon Sep 17 00:00:00 2001 From: charlag Date: Sun, 10 Mar 2019 19:52:19 +0100 Subject: [PATCH] Media previews improvements (in progress) --- .../tusky/adapter/StatusBaseViewHolder.java | 33 ++++++++----- .../conversation/ConversationsFragment.kt | 15 +++++- .../keylesspalace/tusky/db/AccountEntity.kt | 8 ++- .../tusky/fragment/NotificationsFragment.java | 15 +++--- .../tusky/fragment/SearchFragment.kt | 3 +- .../tusky/fragment/TimelineFragment.java | 27 +++++++--- .../tusky/fragment/ViewThreadFragment.java | 3 +- .../preference/AccountPreferencesFragment.kt | 49 +++++++++++++------ app/src/main/res/values/donottranslate.xml | 9 ++++ app/src/main/res/values/strings.xml | 6 +++ app/src/main/res/xml/account_preferences.xml | 4 +- 11 files changed, 122 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java index 0fd5f6186..8e8c570d0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -414,15 +414,26 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { } @NonNull - private static String getLabelTypeText(Context context, Attachment.Type type) { - switch (type) { - default: - case IMAGE: - return context.getString(R.string.status_media_images); - case GIFV: - case VIDEO: - return context.getString(R.string.status_media_video); + private static CharSequence getLabelTypeText(Context context, List attachments, + boolean sensitive) { + StringBuilder text = new StringBuilder(); + for (int i = 0; i < attachments.size(); i++) { + Attachment attachment = attachments.get(i); + if (TextUtils.isEmpty(attachment.getDescription())) { + text.append(context.getString( + R.string.description_status_media_no_description_placeholder)); + } else { + text.append(attachment.getDescription()); + } + if (i != attachments.size() - 1) { + text.append('\n'); + } } + if (sensitive) { + String sensitiveText = context.getString(R.string.status_sensitive_media_title); + text.append(String.format(" (%s)", sensitiveText)); + } + return text; } @DrawableRes @@ -447,11 +458,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { // Set the label's text. Context context = itemView.getContext(); - String labelText = getLabelTypeText(context, attachments.get(0).getType()); - if (sensitive) { - String sensitiveText = context.getString(R.string.status_sensitive_media_title); - labelText += String.format(" (%s)", sensitiveText); - } + CharSequence labelText = getLabelTypeText(context, attachments, sensitive); mediaLabel.setText(labelText); // Set the icon next to the label. diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt index 2cda3a96b..7df90b951 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt @@ -15,12 +15,15 @@ package com.keylesspalace.tusky.components.conversation +import android.content.Context import android.content.Intent +import android.net.ConnectivityManager import android.os.Bundle import android.preference.PreferenceManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.net.ConnectivityManagerCompat import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders import androidx.paging.PagedList @@ -30,6 +33,7 @@ import androidx.recyclerview.widget.SimpleItemAnimator import com.keylesspalace.tusky.AccountActivity import com.keylesspalace.tusky.R import com.keylesspalace.tusky.ViewTagActivity +import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.db.AppDatabase import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.ViewModelFactory @@ -40,6 +44,7 @@ import com.keylesspalace.tusky.util.NetworkState import com.keylesspalace.tusky.util.ThemeUtils import com.keylesspalace.tusky.util.hide import kotlinx.android.synthetic.main.fragment_timeline.* +import kotlinx.android.synthetic.main.item_saved_toot.view.* import javax.inject.Inject class ConversationsFragment : SFragment(), StatusActionListener, Injectable { @@ -67,10 +72,16 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable { val useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false) val account = accountManager.activeAccount - val mediaPreviewEnabled = account?.mediaPreviewEnabled ?: true - adapter = ConversationAdapter(useAbsoluteTime, mediaPreviewEnabled,this, ::onTopLoaded, viewModel::retry) + val isMetered = ConnectivityManagerCompat.isActiveNetworkMetered( + view.context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager) + val mediaPreviewEnabled = account != null + && (account.mediaPreviewEnabled == AccountEntity.MEDIA_PREVIEW_ALWAYS + || (account.mediaPreviewEnabled == AccountEntity.MEDIA_PREVIEW_ON_UNMETERED + && !isMetered)) + adapter = ConversationAdapter(useAbsoluteTime, mediaPreviewEnabled, this, ::onTopLoaded, + viewModel::retry) recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL)) recyclerView.layoutManager = LinearLayoutManager(view.context) diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt b/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt index 5b3390e2e..73e8d59e3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt @@ -47,7 +47,7 @@ data class AccountEntity(@field:PrimaryKey(autoGenerate = true) var id: Long, var defaultPostPrivacy: Status.Visibility = Status.Visibility.PUBLIC, var defaultMediaSensitivity: Boolean = false, var alwaysShowSensitiveMedia: Boolean = false, - var mediaPreviewEnabled: Boolean = true, + var mediaPreviewEnabled: Int = MEDIA_PREVIEW_ALWAYS, var lastNotificationId: String = "0", var activeNotifications: String = "[]", var emojis: List = emptyList(), @@ -77,4 +77,10 @@ data class AccountEntity(@field:PrimaryKey(autoGenerate = true) var id: Long, result = 31 * result + accountId.hashCode() return result } + + companion object { + const val MEDIA_PREVIEW_ALWAYS = 1 + const val MEDIA_PREVIEW_NEVER = 0 + const val MEDIA_PREVIEW_ON_UNMETERED = 2 + } } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java index 68a98cd45..49f1f1e32 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java @@ -209,7 +209,8 @@ public class NotificationsFragment extends SFragment implements adapter = new NotificationsAdapter(dataSource, this, this); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); alwaysShowSensitiveMedia = accountManager.getActiveAccount().getAlwaysShowSensitiveMedia(); - boolean mediaPreviewEnabled = accountManager.getActiveAccount().getMediaPreviewEnabled(); + // TODO + boolean mediaPreviewEnabled = false;//accountManager.getActiveAccount().getMediaPreviewEnabled(); adapter.setMediaPreviewEnabled(mediaPreviewEnabled); boolean useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false); adapter.setUseAbsoluteTime(useAbsoluteTime); @@ -573,12 +574,12 @@ public class NotificationsFragment extends SFragment implements break; } case "mediaPreviewEnabled": { - boolean enabled = accountManager.getActiveAccount().getMediaPreviewEnabled(); - if (enabled != adapter.isMediaPreviewEnabled()) { - adapter.setMediaPreviewEnabled(enabled); - fullyRefresh(); - } - break; +// boolean enabled = accountManager.getActiveAccount().getMediaPreviewEnabled(); +// if (enabled != adapter.isMediaPreviewEnabled()) { +// adapter.setMediaPreviewEnabled(enabled); +// fullyRefresh(); +// } +// break; } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/SearchFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/SearchFragment.kt index 9f772b6c4..b329950b9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/SearchFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/SearchFragment.kt @@ -65,7 +65,8 @@ class SearchFragment : SFragment(), StatusActionListener, Injectable { val account = accountManager.activeAccount alwaysShowSensitiveMedia = account?.alwaysShowSensitiveMedia ?: false - mediaPreviewEnabled = account?.mediaPreviewEnabled ?: true + // TODO + mediaPreviewEnabled = false searchRecyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL)) searchRecyclerView.layoutManager = LinearLayoutManager(view.context) diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java index 7ab89c7c7..3ba18a970 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java @@ -18,6 +18,7 @@ package com.keylesspalace.tusky.fragment; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.net.ConnectivityManager; import android.os.Bundle; import android.preference.PreferenceManager; import android.util.Log; @@ -41,6 +42,7 @@ import com.keylesspalace.tusky.appstore.ReblogEvent; import com.keylesspalace.tusky.appstore.StatusComposedEvent; import com.keylesspalace.tusky.appstore.StatusDeletedEvent; import com.keylesspalace.tusky.appstore.UnfollowEvent; +import com.keylesspalace.tusky.db.AccountEntity; import com.keylesspalace.tusky.db.AccountManager; import com.keylesspalace.tusky.di.Injectable; import com.keylesspalace.tusky.entity.Status; @@ -76,6 +78,7 @@ import javax.inject.Inject; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.arch.core.util.Function; +import androidx.core.net.ConnectivityManagerCompat; import androidx.core.util.Pair; import androidx.lifecycle.Lifecycle; import androidx.recyclerview.widget.AsyncDifferConfig; @@ -319,7 +322,15 @@ public class TimelineFragment extends SFragment implements private void setupTimelinePreferences() { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); alwaysShowSensitiveMedia = accountManager.getActiveAccount().getAlwaysShowSensitiveMedia(); - boolean mediaPreviewEnabled = accountManager.getActiveAccount().getMediaPreviewEnabled(); + AccountEntity account = accountManager.getActiveAccount(); + + boolean isMetered = ConnectivityManagerCompat.isActiveNetworkMetered( + (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE)); + boolean mediaPreviewEnabled = account != null + && (account.getMediaPreviewEnabled() == AccountEntity.MEDIA_PREVIEW_ALWAYS + || (account.getMediaPreviewEnabled() == AccountEntity.MEDIA_PREVIEW_ON_UNMETERED + && !isMetered)); + adapter.setMediaPreviewEnabled(mediaPreviewEnabled); boolean useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false); adapter.setUseAbsoluteTime(useAbsoluteTime); @@ -743,13 +754,13 @@ public class TimelineFragment extends SFragment implements break; } case "mediaPreviewEnabled": { - boolean enabled = accountManager.getActiveAccount().getMediaPreviewEnabled(); - boolean oldMediaPreviewEnabled = adapter.getMediaPreviewEnabled(); - if (enabled != oldMediaPreviewEnabled) { - adapter.setMediaPreviewEnabled(enabled); - fullyRefresh(); - } - break; +// int state = accountManager.getActiveAccount().getMediaPreviewEnabled(); +// boolean oldMediaPreviewEnabled = adapter.getMediaPreviewEnabled(); +// if (enabled != oldMediaPreviewEnabled) { +// adapter.setMediaPreviewEnabled(enabled); +// fullyRefresh(); +// } +// break; } case "tabFilterHomeReplies": { boolean filter = sharedPreferences.getBoolean("tabFilterHomeReplies", true); diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java index 360549ea5..2d8aa1d0b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java @@ -161,7 +161,8 @@ public final class ViewThreadFragment extends SFragment implements SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences( getActivity()); alwaysShowSensitiveMedia = accountManager.getActiveAccount().getAlwaysShowSensitiveMedia(); - boolean mediaPreviewEnabled = accountManager.getActiveAccount().getMediaPreviewEnabled(); + // TODO + boolean mediaPreviewEnabled = false; //accountManager.getActiveAccount().getMediaPreviewEnabled(); adapter.setMediaPreviewEnabled(mediaPreviewEnabled); boolean useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false); adapter.setUseAbsoluteTime(useAbsoluteTime); diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt index 7ce075ae6..cc4494622 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt @@ -15,17 +15,18 @@ package com.keylesspalace.tusky.fragment.preference +import android.annotation.SuppressLint import android.content.Intent import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle -import com.google.android.material.snackbar.Snackbar -import androidx.preference.SwitchPreference +import android.util.Log +import android.view.View import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat -import android.util.Log -import android.view.View +import androidx.preference.SwitchPreference +import com.google.android.material.snackbar.Snackbar import com.keylesspalace.tusky.* import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.PreferenceChangedEvent @@ -64,11 +65,26 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), private lateinit var defaultPostPrivacyPreference: ListPreference private lateinit var defaultMediaSensitivityPreference: SwitchPreference private lateinit var alwaysShowSensitiveMediaPreference: SwitchPreference - private lateinit var mediaPreviewEnabledPreference: SwitchPreference + private lateinit var mediaPreviewEnabledPreference: ListPreference - private val iconSize by lazy {resources.getDimensionPixelSize(R.dimen.preference_icon_size)} + private val iconSize by lazy { resources.getDimensionPixelSize(R.dimen.preference_icon_size) } + @SuppressLint("ApplySharedPref") override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { +// // Migrate preference type +// val valueIndex = when (accountManager.activeAccount!!.mediaPreviewEnabled) { +// AccountEntity.MEDIA_PREVIEW_ALWAYS -> 0 +// AccountEntity.MEDIA_PREVIEW_NEVER -> 2 +// AccountEntity.MEDIA_PREVIEW_ON_UNMETERED -> 1 +// else -> throw AssertionError("Unknown value index") +// } +// val stringValue = context!!.resources.getStringArray(R.array.pref_previews_values)[valueIndex] +// context!!.getSharedPreferences(context!!.getString(R.string.preferences_file_key), Context.MODE_PRIVATE) +// .edit() +// .remove("mediaPreviewEnabled") +// .putString("mediaPreviewEnabled", stringValue) +// .commit() + addPreferencesFromResource(R.xml.account_preferences) notificationPreference = findPreference("notificationPreference") @@ -77,7 +93,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), blockedUsersPreference = findPreference("blockedUsersPreference") defaultPostPrivacyPreference = findPreference("defaultPostPrivacy") as ListPreference defaultMediaSensitivityPreference = findPreference("defaultMediaSensitivity") as SwitchPreference - mediaPreviewEnabledPreference = findPreference("mediaPreviewEnabled") as SwitchPreference + mediaPreviewEnabledPreference = findPreference("mediaPreviewEnabled") as ListPreference alwaysShowSensitiveMediaPreference = findPreference("alwaysShowSensitiveMedia") as SwitchPreference notificationPreference.icon = IconicsDrawable(notificationPreference.context, GoogleMaterial.Icon.gmd_notifications).sizePx(iconSize).color(ThemeUtils.getColor(notificationPreference.context, R.attr.toolbar_icon_tint)) @@ -107,14 +123,14 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), defaultMediaSensitivityPreference.isChecked = it.defaultMediaSensitivity defaultMediaSensitivityPreference.icon = getIconForSensitivity(it.defaultMediaSensitivity) - mediaPreviewEnabledPreference.isChecked = it.mediaPreviewEnabled + mediaPreviewEnabledPreference.value = it.mediaPreviewEnabled.toString() alwaysShowSensitiveMediaPreference.isChecked = it.alwaysShowSensitiveMedia } } override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean { - when(preference) { + when (preference) { defaultPostPrivacyPreference -> { preference.icon = getIconForVisibility(Status.Visibility.byString(newValue as String)) syncWithServer(visibility = newValue) @@ -125,7 +141,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), } mediaPreviewEnabledPreference -> { accountManager.activeAccount?.let { - it.mediaPreviewEnabled = newValue as Boolean + it.mediaPreviewEnabled = (newValue as String).toInt() accountManager.saveAccount(it) } } @@ -144,7 +160,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), override fun onPreferenceClick(preference: Preference): Boolean { - when(preference) { + when (preference) { notificationPreference -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val intent = Intent() @@ -189,13 +205,14 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), private fun syncWithServer(visibility: String? = null, sensitive: Boolean? = null) { mastodonApi.accountUpdateSource(visibility, sensitive) - .enqueue(object: Callback{ + .enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { val account = response.body() - if(response.isSuccessful && account != null) { + if (response.isSuccessful && account != null) { accountManager.activeAccount?.let { - it.defaultPostPrivacy = account.source?.privacy ?: Status.Visibility.PUBLIC + it.defaultPostPrivacy = account.source?.privacy + ?: Status.Visibility.PUBLIC it.defaultMediaSensitivity = account.source?.sensitive ?: false accountManager.saveAccount(it) } @@ -214,9 +231,9 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), } private fun showErrorSnackbar(visibility: String?, sensitive: Boolean?) { - view?.let {view -> + view?.let { view -> Snackbar.make(view, R.string.pref_failed_to_sync, Snackbar.LENGTH_LONG) - .setAction(R.string.action_retry) { syncWithServer( visibility, sensitive)} + .setAction(R.string.action_retry) { syncWithServer(visibility, sensitive) } .show() } } diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index e05d242ae..659a795f3 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -61,6 +61,15 @@ 日本語 + + + 1 + + 2 + + 0 + + default ca diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7711b7438..16db7d3f3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -435,4 +435,10 @@ %s; %s; %s, %s, %s; %s, %s, %s, %s; %s, %s, %s + + Always + On unmetered networks + Never + + diff --git a/app/src/main/res/xml/account_preferences.xml b/app/src/main/res/xml/account_preferences.xml index 081d87c5f..cb0557213 100644 --- a/app/src/main/res/xml/account_preferences.xml +++ b/app/src/main/res/xml/account_preferences.xml @@ -38,8 +38,10 @@ -