Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
50b98f2
ShareIntent - Use the original uri if getRealPathFromURI returns empty
Feb 7, 2024
abc7992
Update WordPressUtils ref
Feb 7, 2024
9d3e3e2
Send uri path directly and add try/catch to prevent permission relate…
Feb 7, 2024
daccf7e
Update log message
Feb 7, 2024
0c98841
Add uri as fallback
Feb 7, 2024
79db663
Only pass a single category to the Filters Sheet
Feb 8, 2024
51ae6a7
Fix minor warnings in SubfilterPageFragment
Feb 8, 2024
ceb4c52
Dismiss the Subfilter sheet if any required argument is null
Feb 8, 2024
51a40e4
Update logic to unsubscribe tag in manage tags and blogs screen: keep…
RenanLukas Feb 9, 2024
a41e57d
ShareIntentReceiver - Add Toast message when media can be loaded
Feb 9, 2024
b52d837
Merge pull request #20157 from wordpress-mobile/issue/20148-reader-cr…
Feb 9, 2024
414ddf8
Allow defining which notification channels to create on app initialis…
antonis Feb 9, 2024
80790f9
Only create the transient channel when notifications are disabled to…
antonis Feb 9, 2024
07d89b5
Adds release note
antonis Feb 9, 2024
4c494c8
Disable follow button while subscribe/unsubscribe from tag API reques…
RenanLukas Feb 9, 2024
e2dd699
Refresh tags chip count when subscribed status changes in manage tags…
RenanLukas Feb 9, 2024
920d4f8
Update tag followed status in memory only when the action finishes
RenanLukas Feb 9, 2024
a02167a
Fix PR comment: tag was being deleted on success but never added again
RenanLukas Feb 9, 2024
85f5945
Merge pull request #20171 from wordpress-mobile/issue/19353-update-fo…
RenanLukas Feb 10, 2024
db61f3b
Merge pull request #20168 from wordpress-mobile/fix/18495-CannotPostF…
mkevins Feb 11, 2024
47b2fc5
Update WordPressUtils to 3.13.0
Feb 12, 2024
42ad16f
Update error message when sharing a media file to the app fails
Feb 12, 2024
1d0c632
Split long string
Feb 12, 2024
6c13ba8
Fix style issues with long line
Feb 12, 2024
9972cb5
Merge pull request #20138 from wordpress-mobile/fix/sharing-image-reg…
Feb 12, 2024
84f2668
Freeze strings for translation
mokagio Feb 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* [**] Prevent images from temporarily disappearing when uploading media [https://github.com/WordPress/gutenberg/pull/57869]
* [***] [Jetpack-only] Reader: introduced new UI/UX for content navigation and filtering [https://github.com/wordpress-mobile/WordPress-Android/pull/19978]
* [**] Prevents crashes when the webview state is too big [https://github.com/wordpress-mobile/WordPress-Android/pull/20139]
* [*] [WordPress-only] Prevents a crash occurring when uploading videos under certain conditions [https://github.com/wordpress-mobile/WordPress-Android/pull/20168]

24.1
-----
Expand Down
132 changes: 77 additions & 55 deletions WordPress/src/main/java/org/wordpress/android/AppInitializer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -464,65 +464,78 @@ class AppInitializer @Inject constructor(
credentialsClient.connect()
}

private fun createNotificationChannelsOnSdk26() {
private fun createNotificationChannelsOnSdk26(
normal: Boolean = true,
important: Boolean = true,
reminder: Boolean = true,
transient: Boolean = true,
weeklyRoundup: Boolean = true
) {
// create Notification channels introduced in Android Oreo
if (Build.VERSION.SDK_INT >= VERSION_CODES.O) {
// Create the NORMAL channel (used for likes, comments, replies, etc.)
val normalChannel = NotificationChannel(
application.getString(R.string.notification_channel_normal_id),
application.getString(R.string.notification_channel_general_title),
NotificationManager.IMPORTANCE_DEFAULT
)
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
val notificationManager = application.getSystemService(
Context.NOTIFICATION_SERVICE
) as NotificationManager
notificationManager.createNotificationChannel(normalChannel)
if (normal) {
// Create the NORMAL channel (used for likes, comments, replies, etc.)
val normalChannel = NotificationChannel(
application.getString(R.string.notification_channel_normal_id),
application.getString(R.string.notification_channel_general_title),
NotificationManager.IMPORTANCE_DEFAULT
)
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this

// Create the IMPORTANT channel (used for 2fa auth, for example)
val importantChannel = NotificationChannel(
application.getString(R.string.notification_channel_important_id),
application.getString(R.string.notification_channel_important_title),
NotificationManager.IMPORTANCE_HIGH
)
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
notificationManager.createNotificationChannel(importantChannel)

// Create the REMINDER channel (used for various reminders, like Quick Start, etc.)
val reminderChannel = NotificationChannel(
application.getString(R.string.notification_channel_reminder_id),
application.getString(R.string.notification_channel_reminder_title),
NotificationManager.IMPORTANCE_LOW
)
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
notificationManager.createNotificationChannel(reminderChannel)

// Create the TRANSIENT channel (used for short-lived notifications such as processing a Like/Approve,
// or media upload)
val transientChannel = NotificationChannel(
application.getString(R.string.notification_channel_transient_id),
application.getString(R.string.notification_channel_transient_title),
NotificationManager.IMPORTANCE_DEFAULT
)
transientChannel.setSound(null, null)
transientChannel.enableVibration(false)
transientChannel.enableLights(false)
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
notificationManager.createNotificationChannel(transientChannel)

// Create the WEEKLY ROUNDUP channel (used for weekly roundup notification containing weekly stats)
val weeklyRoundupChannel = NotificationChannel(
application.getString(R.string.notification_channel_weekly_roundup_id),
application.getString(R.string.notification_channel_weekly_roundup_title),
NotificationManager.IMPORTANCE_LOW
)
// Register the channel with the system; you can't change the importance or other notification behaviors
// after this
notificationManager.createNotificationChannel(weeklyRoundupChannel)
notificationManager.createNotificationChannel(normalChannel)
}
if (important) {
// Create the IMPORTANT channel (used for 2fa auth, for example)
val importantChannel = NotificationChannel(
application.getString(R.string.notification_channel_important_id),
application.getString(R.string.notification_channel_important_title),
NotificationManager.IMPORTANCE_HIGH
)
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
notificationManager.createNotificationChannel(importantChannel)
}
if (reminder) {
// Create the REMINDER channel (used for various reminders, like Quick Start, etc.)
val reminderChannel = NotificationChannel(
application.getString(R.string.notification_channel_reminder_id),
application.getString(R.string.notification_channel_reminder_title),
NotificationManager.IMPORTANCE_LOW
)
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
notificationManager.createNotificationChannel(reminderChannel)
}
if (transient) {
// Create the TRANSIENT channel (used for short-lived notifications such as processing a Like/Approve,
// or media upload)
val transientChannel = NotificationChannel(
application.getString(R.string.notification_channel_transient_id),
application.getString(R.string.notification_channel_transient_title),
NotificationManager.IMPORTANCE_DEFAULT
)
transientChannel.setSound(null, null)
transientChannel.enableVibration(false)
transientChannel.enableLights(false)
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
notificationManager.createNotificationChannel(transientChannel)
}
if (weeklyRoundup) {
// Create the WEEKLY ROUNDUP channel (used for weekly roundup notification containing weekly stats)
val weeklyRoundupChannel = NotificationChannel(
application.getString(R.string.notification_channel_weekly_roundup_id),
application.getString(R.string.notification_channel_weekly_roundup_title),
NotificationManager.IMPORTANCE_LOW
)
// Register the channel with the system; you can't change the importance or other notification behaviors
// after this
notificationManager.createNotificationChannel(weeklyRoundupChannel)
}
}
}

Expand Down Expand Up @@ -980,10 +993,19 @@ class AppInitializer @Inject constructor(
}

private fun updateNotificationSettings() {
if(!jetpackFeatureRemovalPhaseHelper.shouldShowNotifications())
if (!jetpackFeatureRemovalPhaseHelper.shouldShowNotifications()) {
NotificationsUtils.cancelAllNotifications(application)
else
// Only create the transient notification channel to handle upload notifications
createNotificationChannelsOnSdk26(
normal = false,
important = false,
reminder = false,
transient = true,
weeklyRoundup = false,
)
} else {
createNotificationChannelsOnSdk26()
}
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
Expand All @@ -21,6 +22,8 @@
import org.wordpress.android.ui.main.WPMainActivity;
import org.wordpress.android.ui.media.MediaBrowserActivity;
import org.wordpress.android.ui.media.MediaBrowserType;
import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.AppLog.T;
import org.wordpress.android.util.FluxCUtils;
import org.wordpress.android.util.MediaUtils;
import org.wordpress.android.util.ToastUtils;
Expand Down Expand Up @@ -94,23 +97,33 @@ private void refreshContent() {
}

private void downloadExternalMedia() {
if (Intent.ACTION_SEND_MULTIPLE.equals(getIntent().getAction())) {
ArrayList<Uri> externalUris = getIntent().getParcelableArrayListExtra((Intent.EXTRA_STREAM));
for (Uri uri : externalUris) {
if (uri != null && isAllowedMediaType(uri)) {
mLocalMediaUris.add(MediaUtils.downloadExternalMedia(this, uri));
try {
if (Intent.ACTION_SEND_MULTIPLE.equals(getIntent().getAction())) {
ArrayList<Uri> externalUris = getIntent().getParcelableArrayListExtra((Intent.EXTRA_STREAM));
for (Uri uri : externalUris) {
if (uri != null && isAllowedMediaType(uri)) {
mLocalMediaUris.add(MediaUtils.downloadExternalMedia(this, uri));
}
}
} else if (Intent.ACTION_SEND.equals(getIntent().getAction())) {
Uri externalUri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
if (externalUri != null && isAllowedMediaType(externalUri)) {
mLocalMediaUris.add(MediaUtils.downloadExternalMedia(this, externalUri));
}
}
} else if (Intent.ACTION_SEND.equals(getIntent().getAction())) {
Uri externalUri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
if (externalUri != null && isAllowedMediaType(externalUri)) {
mLocalMediaUris.add(MediaUtils.downloadExternalMedia(this, externalUri));
}
} catch (Exception e) {
ToastUtils.showToast(this,
R.string.error_media_could_not_share_media_from_device, ToastUtils.Duration.LONG);
AppLog.e(T.MEDIA, "ShareIntentReceiver failed to download media ", e);
}
}

private boolean isAllowedMediaType(@NonNull Uri uri) {
String filePath = MediaUtils.getRealPathFromURI(this, uri);
// For cases when getRealPathFromURI returns an empty string
if (TextUtils.isEmpty(filePath)) {
filePath = String.valueOf(uri);
}
return MediaUtils.isValidImage(filePath) || MediaUtils.isVideo(filePath);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,7 @@ private void initSubFilterViewModel(@Nullable Bundle savedInstanceState) {
BottomSheetVisible visibleState = (BottomSheetVisible) uiState;
bottomSheet = SubfilterBottomSheetFragment.newInstance(
SubFilterViewModel.getViewModelKeyForTag(mTagFragmentStartedWith),
visibleState.getCategories(),
visibleState.getCategory(),
mUiHelpers.getTextOfUiString(requireContext(), visibleState.getTitle())
);
bottomSheet.show(getChildFragmentManager(), SUBFILTER_BOTTOM_SHEET_TAG);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
* followed tags and followed blogs
*/
public class ReaderSubsActivity extends LocaleAwareActivity
implements ReaderTagAdapter.TagDeletedListener {
implements ReaderTagAdapter.TagDeletedListener, ReaderTagAdapter.TagAddedListener {
private EditText mEditAdd;
private FloatingActionButton mFabButton;
private ReaderFollowButton mBtnAdd;
Expand Down Expand Up @@ -176,8 +176,7 @@ private void setResult() {
boolean shouldRefreshSubscriptions = false;
if (mPageAdapter != null) {
final ReaderTagFragment readerTagFragment = mPageAdapter.getReaderTagFragment();
final ReaderBlogFragment readerBlogFragment = mPageAdapter.getReaderBlogFragment();
if (readerTagFragment != null && readerBlogFragment != null) {
if (readerTagFragment != null) {
shouldRefreshSubscriptions = readerTagFragment.hasChangedSelectedTags();
}
}
Expand Down Expand Up @@ -497,8 +496,14 @@ public void onTagDeleted(ReaderTag tag) {
if (mLastAddedTagName != null && mLastAddedTagName.equalsIgnoreCase(tag.getTagSlug())) {
mLastAddedTagName = null;
}
String labelRemovedTag = getString(R.string.reader_label_removed_tag);
showInfoSnackbar(String.format(labelRemovedTag, tag.getLabel()));
}

@Override public void onTagAdded(@NonNull ReaderTag readerTag) {
mReaderTracker.trackTag(
AnalyticsTracker.Stat.READER_TAG_FOLLOWED,
readerTag.getTagSlug(),
ReaderTracker.SOURCE_SETTINGS
);
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
/*
* fragment hosted by ReaderSubsActivity which shows followed tags
*/
public class ReaderTagFragment extends Fragment implements ReaderTagAdapter.TagDeletedListener {
public class ReaderTagFragment extends Fragment
implements ReaderTagAdapter.TagDeletedListener, ReaderTagAdapter.TagAddedListener {
private ReaderRecyclerView mRecyclerView;
private ReaderTagAdapter mTagAdapter;

Expand All @@ -49,10 +50,10 @@ public boolean hasChangedSelectedTags() {
for (final ReaderTag readerTag : mInitialReaderTagList) {
initialTagsSlugs.add(readerTag.getTagSlug());
}
final List<ReaderTag> currentReaderTagList = getTagAdapter().getItems();
final List<ReaderTag> currentlySubscribedReaderTagList = getTagAdapter().getSubscribedItems();
final Set<String> currentTagsSlugs = new HashSet<>();
if (currentReaderTagList != null) {
for (final ReaderTag readerTag : currentReaderTagList) {
if (currentlySubscribedReaderTagList != null) {
for (final ReaderTag readerTag : currentlySubscribedReaderTagList) {
currentTagsSlugs.add(readerTag.getTagSlug());
}
}
Expand Down Expand Up @@ -103,6 +104,7 @@ private ReaderTagAdapter getTagAdapter() {
Context context = WPActivityUtils.getThemedContext(getActivity());
mTagAdapter = new ReaderTagAdapter(context);
mTagAdapter.setTagDeletedListener(this);
mTagAdapter.setTagAddedListener(this);
mTagAdapter.setDataLoadedListener(isEmpty -> {
checkEmptyView();
if (mIsFirstDataLoaded) {
Expand Down Expand Up @@ -133,4 +135,10 @@ public void onTagDeleted(ReaderTag tag) {
((ReaderTagAdapter.TagDeletedListener) getActivity()).onTagDeleted(tag);
}
}

@Override public void onTagAdded(@NonNull ReaderTag readerTag) {
if (getActivity() instanceof ReaderTagAdapter.TagDeletedListener) {
((ReaderTagAdapter.TagAddedListener) getActivity()).onTagAdded(readerTag);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import org.wordpress.android.ui.reader.subfilter.SubfilterCategory.SITES
import org.wordpress.android.ui.reader.subfilter.SubfilterCategory.TAGS
import org.wordpress.android.ui.reader.subfilter.SubfilterListItem.Tag
import org.wordpress.android.ui.reader.subfilter.SubfilterPagerAdapter
import org.wordpress.android.util.extensions.getParcelableArrayListCompat
import org.wordpress.android.util.extensions.getSerializableCompat
import javax.inject.Inject
import com.google.android.material.R as MaterialR

Expand All @@ -36,19 +36,19 @@ class SubfilterBottomSheetFragment : BottomSheetDialogFragment() {
companion object {
const val SUBFILTER_VIEW_MODEL_KEY = "subfilter_view_model_key"
const val SUBFILTER_TITLE_KEY = "subfilter_title_key"
const val SUBFILTER_CATEGORIES_KEY = "subfilter_categories_key"
const val SUBFILTER_CATEGORY_KEY = "subfilter_category_key"

@JvmStatic
fun newInstance(
subfilterViewModelKey: String,
categories: List<SubfilterCategory>,
category: SubfilterCategory,
title: CharSequence
): SubfilterBottomSheetFragment {
val fragment = SubfilterBottomSheetFragment()
val bundle = Bundle()
bundle.putString(SUBFILTER_VIEW_MODEL_KEY, subfilterViewModelKey)
bundle.putCharSequence(SUBFILTER_TITLE_KEY, title)
bundle.putParcelableArrayList(SUBFILTER_CATEGORIES_KEY, ArrayList(categories))
bundle.putSerializable(SUBFILTER_CATEGORY_KEY, category)

fragment.arguments = bundle
return fragment
Expand All @@ -66,11 +66,14 @@ class SubfilterBottomSheetFragment : BottomSheetDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

val subfilterVmKey = requireArguments().getString(SUBFILTER_VIEW_MODEL_KEY)!!
val bottomSheetTitle = requireArguments().getCharSequence(SUBFILTER_TITLE_KEY)!!
val categories = requireNotNull(
requireArguments().getParcelableArrayListCompat<SubfilterCategory>(SUBFILTER_CATEGORIES_KEY)
)
val subfilterVmKey = requireArguments().getString(SUBFILTER_VIEW_MODEL_KEY)
val bottomSheetTitle = requireArguments().getCharSequence(SUBFILTER_TITLE_KEY)
val category = requireArguments().getSerializableCompat<SubfilterCategory>(SUBFILTER_CATEGORY_KEY)

if (subfilterVmKey == null || category == null || bottomSheetTitle == null) {
dismiss()
return
}

viewModel = ViewModelProvider(
parentFragment as ViewModelStoreOwner,
Expand All @@ -87,7 +90,7 @@ class SubfilterBottomSheetFragment : BottomSheetDialogFragment() {
requireActivity(),
childFragmentManager,
subfilterVmKey,
categories.toList()
listOf(category)
)
pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
Expand All @@ -110,7 +113,6 @@ class SubfilterBottomSheetFragment : BottomSheetDialogFragment() {
}

editSubscriptions.setOnClickListener {
val category = categories.firstOrNull() ?: return@setOnClickListener
val subsPageIndex = when (category) {
SITES -> ReaderSubsActivity.TAB_IDX_FOLLOWED_BLOGS
TAGS -> ReaderSubsActivity.TAB_IDX_FOLLOWED_TAGS
Expand Down
Loading