diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/MainActivity.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/MainActivity.kt index 5f8a90c78..3874f52f6 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/MainActivity.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/MainActivity.kt @@ -691,7 +691,7 @@ open class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks, OnSect 2 -> { mCompositeDisposable.add(InteractorFactory.createStoriesInteractor() - .getStory( + .getStories( Settings.get().accounts().current, null ) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/adapters/FeedbackVKOfficialDtoAdapter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/adapters/FeedbackVKOfficialDtoAdapter.kt index 2f6c28e7c..262b72fea 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/adapters/FeedbackVKOfficialDtoAdapter.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/adapters/FeedbackVKOfficialDtoAdapter.kt @@ -56,10 +56,15 @@ class FeedbackVKOfficialDtoAdapter : val dto = FeedbackVKOfficial() if (hasObject(root_item, "action")) { val action_item = root_item["action"]?.jsonObject - if ("authorize" == optString(action_item, "type") || "custom" == optString( + if ("authorize" == optString(action_item, "type")) { + dto.action = ActionBrowserURL(optString(action_item, "url")) + } else if ("custom" == optString( action_item, "type" - ) && optString(root_item, "icon_type") == "friend_found" + ) && optString( + root_item, + "icon_type" + ) == "friend_found" ) { dto.action = ActionURL(optString(action_item, "url")) } else if ("message_open" == optString( @@ -182,6 +187,13 @@ class FeedbackVKOfficialDtoAdapter : kJson.decodeFromJsonElement(ImageAdditional.serializer(), s) dto.images?.add(imgh) } + + if (hasObject(additional_item, "action")) { + val action_item = additional_item["action"]?.jsonObject + if ("custom" == optString(action_item, "type")) { + dto.images_action = ActionURL(optString(action_item, "url")) + } + } } if ("photo" == optString(additional_item, "type")) { attachments.add( diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/StoriesShortVideosApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/StoriesShortVideosApi.kt index f33a83c02..56f84ce4d 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/StoriesShortVideosApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/impl/StoriesShortVideosApi.kt @@ -9,8 +9,8 @@ import dev.ragnarok.fenrir.api.model.Items import dev.ragnarok.fenrir.api.model.VKApiNarratives import dev.ragnarok.fenrir.api.model.VKApiStory import dev.ragnarok.fenrir.api.model.response.ShortVideosResponse +import dev.ragnarok.fenrir.api.model.response.StoriesResponse import dev.ragnarok.fenrir.api.model.response.StoryGetResponse -import dev.ragnarok.fenrir.api.model.response.StoryResponse import dev.ragnarok.fenrir.api.model.response.ViewersListResponse import dev.ragnarok.fenrir.api.model.server.VKApiStoryUploadServer import dev.ragnarok.fenrir.api.services.IStoriesShortVideosService @@ -19,10 +19,14 @@ import io.reactivex.rxjava3.core.Single internal class StoriesShortVideosApi(accountId: Long, provider: IServiceProvider) : AbsApi(accountId, provider), IStoriesShortVideosApi { - override fun getStory(owner_id: Long?, extended: Int?, fields: String?): Single { + override fun getStories( + owner_id: Long?, + extended: Int?, + fields: String? + ): Single { return provideService(IStoriesShortVideosService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> - service.getStory(owner_id, extended, fields) + service.getStories(owner_id, extended, fields) .map(extractResponseWithErrorHandling()) } } @@ -67,16 +71,16 @@ internal class StoriesShortVideosApi(accountId: Long, provider: IServiceProvider } } - override fun searchStory( + override fun searchStories( q: String?, mentioned_id: Long?, count: Int?, extended: Int?, fields: String? - ): Single { + ): Single { return provideService(IStoriesShortVideosService(), TokenType.USER, TokenType.COMMUNITY) .flatMap { service -> - service.searchStory(q, mentioned_id, count, extended, fields) + service.searchStories(q, mentioned_id, count, extended, fields) .map(extractResponseWithErrorHandling()) } } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/IStoriesShortVideosApi.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/IStoriesShortVideosApi.kt index 45a2ee2ca..fc1144d25 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/IStoriesShortVideosApi.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/interfaces/IStoriesShortVideosApi.kt @@ -6,8 +6,8 @@ import dev.ragnarok.fenrir.api.model.Items import dev.ragnarok.fenrir.api.model.VKApiNarratives import dev.ragnarok.fenrir.api.model.VKApiStory import dev.ragnarok.fenrir.api.model.response.ShortVideosResponse +import dev.ragnarok.fenrir.api.model.response.StoriesResponse import dev.ragnarok.fenrir.api.model.response.StoryGetResponse -import dev.ragnarok.fenrir.api.model.response.StoryResponse import dev.ragnarok.fenrir.api.model.response.ViewersListResponse import dev.ragnarok.fenrir.api.model.server.VKApiStoryUploadServer import io.reactivex.rxjava3.core.Single @@ -40,7 +40,7 @@ interface IStoriesShortVideosApi { fun stories_save(upload_results: String?): Single> @CheckResult - fun getStory(owner_id: Long?, extended: Int?, fields: String?): Single + fun getStories(owner_id: Long?, extended: Int?, fields: String?): Single @CheckResult fun getNarratives(owner_id: Long, offset: Int?, count: Int?): Single> @@ -53,13 +53,13 @@ interface IStoriesShortVideosApi { ): Single @CheckResult - fun searchStory( + fun searchStories( q: String?, mentioned_id: Long?, count: Int?, extended: Int?, fields: String? - ): Single + ): Single @CheckResult fun getShortVideos( diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/model/response/StoryResponse.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/model/response/StoriesResponse.kt similarity index 95% rename from app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/model/response/StoryResponse.kt rename to app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/model/response/StoriesResponse.kt index 386c8e43f..9b7cf18ca 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/model/response/StoryResponse.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/model/response/StoriesResponse.kt @@ -6,7 +6,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -class StoryResponse { +class StoriesResponse { @SerialName("items") var items: List? = null diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IStoriesShortVideosService.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IStoriesShortVideosService.kt index 4e7af1ded..7aa0fe0b6 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IStoriesShortVideosService.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/api/services/IStoriesShortVideosService.kt @@ -5,8 +5,8 @@ import dev.ragnarok.fenrir.api.model.VKApiNarratives import dev.ragnarok.fenrir.api.model.VKApiStory import dev.ragnarok.fenrir.api.model.response.BaseResponse import dev.ragnarok.fenrir.api.model.response.ShortVideosResponse +import dev.ragnarok.fenrir.api.model.response.StoriesResponse import dev.ragnarok.fenrir.api.model.response.StoryGetResponse -import dev.ragnarok.fenrir.api.model.response.StoryResponse import dev.ragnarok.fenrir.api.model.response.ViewersListResponse import dev.ragnarok.fenrir.api.model.server.VKApiStoryUploadServer import dev.ragnarok.fenrir.api.rest.IServiceRest @@ -82,17 +82,17 @@ class IStoriesShortVideosService : IServiceRest() { ) } - fun getStory( + fun getStories( owner_id: Long?, extended: Int?, fields: String? - ): Single> { + ): Single> { return rest.request( "stories.get", form( "owner_id" to owner_id, "extended" to extended, "fields" to fields - ), base(StoryResponse.serializer()) + ), base(StoriesResponse.serializer()) ) } @@ -124,13 +124,13 @@ class IStoriesShortVideosService : IServiceRest() { ) } - fun searchStory( + fun searchStories( q: String?, mentioned_id: Long?, count: Int?, extended: Int?, fields: String? - ): Single> { + ): Single> { return rest.request( "stories.search", form( "q" to q, @@ -138,7 +138,7 @@ class IStoriesShortVideosService : IServiceRest() { "count" to count, "extended" to extended, "fields" to fields - ), base(StoryResponse.serializer()) + ), base(StoriesResponse.serializer()) ) } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/IStoriesShortVideosInteractor.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/IStoriesShortVideosInteractor.kt index 20519c43a..83502a716 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/IStoriesShortVideosInteractor.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/IStoriesShortVideosInteractor.kt @@ -16,8 +16,8 @@ interface IStoriesShortVideosInteractor { offset: Int ): Single>> - fun searchStory(accountId: Long, q: String?, mentioned_id: Long?): Single> - fun getStory(accountId: Long, owner_id: Long?): Single> + fun searchStories(accountId: Long, q: String?, mentioned_id: Long?): Single> + fun getStories(accountId: Long, owner_id: Long?): Single> fun stories_delete(accountId: Long, owner_id: Long, story_id: Int): Single fun getNarratives( accountId: Long, diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/impl/StoriesShortVideosInteractor.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/impl/StoriesShortVideosInteractor.kt index 4d3f58ca4..a0f6db391 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/impl/StoriesShortVideosInteractor.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/domain/impl/StoriesShortVideosInteractor.kt @@ -121,10 +121,10 @@ class StoriesShortVideosInteractor( .stories().stories_delete(owner_id, story_id) } - override fun getStory(accountId: Long, owner_id: Long?): Single> { + override fun getStories(accountId: Long, owner_id: Long?): Single> { return networker.vkDefault(accountId) .stories() - .getStory(owner_id, 1, Fields.FIELDS_BASE_OWNER) + .getStories(owner_id, 1, Fields.FIELDS_BASE_OWNER) .flatMap { story -> val dtos_multy = listEmptyIfNull(story.items) val dtos: MutableList = ArrayList() @@ -156,14 +156,14 @@ class StoriesShortVideosInteractor( } } - override fun searchStory( + override fun searchStories( accountId: Long, q: String?, mentioned_id: Long? ): Single> { return networker.vkDefault(accountId) .stories() - .searchStory(q, mentioned_id, 1000, 1, Fields.FIELDS_BASE_OWNER) + .searchStories(q, mentioned_id, 1000, 1, Fields.FIELDS_BASE_OWNER) .flatMap { story -> val dtos_multy = listEmptyIfNull(story.items) val dtos: MutableList = ArrayList() diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/feedback/feedbackvkofficial/FeedbackVKOfficialAdapter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/feedback/feedbackvkofficial/FeedbackVKOfficialAdapter.kt index 725a67d39..a311025f8 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/feedback/feedbackvkofficial/FeedbackVKOfficialAdapter.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/feedback/feedbackvkofficial/FeedbackVKOfficialAdapter.kt @@ -231,9 +231,15 @@ class FeedbackVKOfficialAdapter( holder.time.text = AppTextUtils.getDateFromUnixTime(context, Page.time) val Img = Page.getImage(256) if (Img == null) { + holder.additional.setOnClickListener { } holder.additional.visibility = View.GONE with().cancelRequest(holder.additional) } else { + holder.additional.setOnClickListener { + Page.images_action.requireNonNull { sc -> + clickListener?.openAction(sc) + } + } holder.additional.visibility = View.VISIBLE with() .load(Img.url) @@ -297,7 +303,7 @@ class FeedbackVKOfficialAdapter( } } Page.action.ifNonNull({ - if (it.getActionType() == FeedbackVKOfficial.Action_Types.URL) { + if (it.getActionType() == FeedbackVKOfficial.Action_Types.URL || it.getActionType() == FeedbackVKOfficial.Action_Types.BROWSER_URL) { holder.actionButton.setText(R.string.more_info) } else { holder.actionButton.setText(R.string.open) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/feedback/feedbackvkofficial/FeedbackVKOfficialFragment.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/feedback/feedbackvkofficial/FeedbackVKOfficialFragment.kt index fcf8a22f7..616b4c080 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/feedback/feedbackvkofficial/FeedbackVKOfficialFragment.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/feedback/feedbackvkofficial/FeedbackVKOfficialFragment.kt @@ -24,6 +24,7 @@ import dev.ragnarok.fenrir.listener.EndlessRecyclerOnScrollListener import dev.ragnarok.fenrir.listener.OnSectionResumeCallback import dev.ragnarok.fenrir.listener.PicassoPauseOnScrollListener import dev.ragnarok.fenrir.model.FeedbackVKOfficial +import dev.ragnarok.fenrir.model.FeedbackVKOfficial.ActionBrowserURL import dev.ragnarok.fenrir.model.FeedbackVKOfficial.ActionMessage import dev.ragnarok.fenrir.model.FeedbackVKOfficial.ActionURL import dev.ragnarok.fenrir.model.FeedbackVKOfficialList @@ -154,16 +155,32 @@ class FeedbackVKOfficialFragment : } override fun openAction(action: FeedbackVKOfficial.Action) { - if (action.getActionType() == FeedbackVKOfficial.Action_Types.URL) { - LinkHelper.openLinkInBrowser(requireActivity(), (action as ActionURL).getUrl()) - } else if (action.getActionType() == FeedbackVKOfficial.Action_Types.MESSAGE) { - val msg = action as ActionMessage - getMessagesLookupPlace( - Settings.get().accounts().current, - msg.getPeerId(), - msg.getMessageId(), - null - ).tryOpenWith(requireActivity()) + when (action.getActionType()) { + FeedbackVKOfficial.Action_Types.BROWSER_URL -> { + LinkHelper.openLinkInBrowser( + requireActivity(), + (action as ActionBrowserURL).getUrl() + ) + } + + FeedbackVKOfficial.Action_Types.URL -> { + LinkHelper.openUrl( + requireActivity(), + Settings.get().accounts().current, + (action as ActionURL).getUrl(), + false + ) + } + + FeedbackVKOfficial.Action_Types.MESSAGE -> { + val msg = action as ActionMessage + getMessagesLookupPlace( + Settings.get().accounts().current, + msg.getPeerId(), + msg.getMessageId(), + null + ).tryOpenWith(requireActivity()) + } } } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/wall/AbsWallPresenter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/wall/AbsWallPresenter.kt index 27ce19d7f..cb643b2d2 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/wall/AbsWallPresenter.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/wall/AbsWallPresenter.kt @@ -472,7 +472,7 @@ abstract class AbsWallPresenter internal constructor( cacheCompositeDisposable.clear() requestWall(0) if (!Settings.get().other().isDisable_history) { - appendDisposable(storiesInteractor.getStory( + appendDisposable(storiesInteractor.getStories( accountId, if (accountId == ownerId) null else ownerId ) @@ -904,7 +904,7 @@ abstract class AbsWallPresenter internal constructor( storiesInteractor = InteractorFactory.createStoriesInteractor() loadWallCachedData() if (!Settings.get().other().isDisable_history) { - appendDisposable(storiesInteractor.getStory( + appendDisposable(storiesInteractor.getStories( accountId, if (accountId == ownerId) null else ownerId ) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/wall/groupwall/GroupWallPresenter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/wall/groupwall/GroupWallPresenter.kt index 1f637aa91..0786d997f 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/wall/groupwall/GroupWallPresenter.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/wall/groupwall/GroupWallPresenter.kt @@ -573,7 +573,7 @@ class GroupWallPresenter( } override fun searchStory(ByName: Boolean) { - appendDisposable(storiesInteractor.searchStory( + appendDisposable(storiesInteractor.searchStories( accountId, if (ByName) community.fullName else null, if (ByName) null else ownerId diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/wall/userwall/UserWallPresenter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/wall/userwall/UserWallPresenter.kt index 7952bb960..6047d3d3f 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/wall/userwall/UserWallPresenter.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/fragment/wall/userwall/UserWallPresenter.kt @@ -749,7 +749,7 @@ class UserWallPresenter( } override fun searchStory(ByName: Boolean) { - appendDisposable(storiesInteractor.searchStory( + appendDisposable(storiesInteractor.searchStories( accountId, if (ByName) user.fullName else null, if (ByName) null else ownerId diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/link/LinkHelper.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/link/LinkHelper.kt index e30f52eec..761f1d1a6 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/link/LinkHelper.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/link/LinkHelper.kt @@ -13,6 +13,7 @@ import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsService import com.google.android.material.dialog.MaterialAlertDialogBuilder import dev.ragnarok.fenrir.R +import dev.ragnarok.fenrir.api.model.AccessIdPair import dev.ragnarok.fenrir.domain.InteractorFactory import dev.ragnarok.fenrir.fragment.fave.FaveTabsFragment import dev.ragnarok.fenrir.fragment.photos.vkphotos.IVKPhotosView @@ -23,6 +24,7 @@ import dev.ragnarok.fenrir.link.types.* import dev.ragnarok.fenrir.link.types.FaveLink import dev.ragnarok.fenrir.media.music.MusicPlaybackService.Companion.startForPlayList import dev.ragnarok.fenrir.model.* +import dev.ragnarok.fenrir.place.PlaceFactory import dev.ragnarok.fenrir.place.PlaceFactory.getArtistPlace import dev.ragnarok.fenrir.place.PlaceFactory.getAudiosInAlbumPlace import dev.ragnarok.fenrir.place.PlaceFactory.getAudiosPlace @@ -352,6 +354,19 @@ object LinkHelper { }) { e -> createCustomToast(context).showToastThrowable(e) } } + AbsLink.STORY -> { + val storyLink = link as StoryLink + InteractorFactory.createStoriesInteractor().getStoryById( + accountId, + listOf(AccessIdPair(storyLink.storyId, storyLink.ownerId, storyLink.access_key)) + ) + .fromIOToMain() + .subscribe({ + PlaceFactory.getHistoryVideoPreviewPlace(accountId, ArrayList(it), 0) + .tryOpenWith(context) + }) { e -> createCustomToast(context).showToastThrowable(e) } + } + else -> return false } return true diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/link/VKLinkParser.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/link/VKLinkParser.kt index 2b22d887b..450451ae4 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/link/VKLinkParser.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/link/VKLinkParser.kt @@ -41,6 +41,7 @@ object VKLinkParser { val PATTERN_PLAYLIST_ALT: Pattern = Pattern.compile("vk\\.com/.+(?:act=|z=)audio_playlist(-?\\d*)_(\\d*)(?:&access_hash=(\\w+))?") val PATTERN_DOC: Pattern = Pattern.compile("vk\\.com/doc(-?\\d*)_(\\d*)") //+ + val PATTERN_STORY: Pattern = Pattern.compile("vk\\.com/story(-?\\d*)_(\\d*)") //+ val PATTERN_TOPIC: Pattern = Pattern.compile("vk\\.com/topic-(\\d*)_(\\d*)") //+ val PATTERN_FAVE: Pattern = Pattern.compile("vk\\.com/fave") val PATTERN_GROUP_ID: Pattern = Pattern.compile("vk\\.com/(club|event|public)(\\d+)$") //+ @@ -252,6 +253,10 @@ object VKLinkParser { if (vkLink != null) { return vkLink } + vkLink = parseStory(string) + if (vkLink != null) { + return vkLink + } vkLink = parsePhotos(string) if (vkLink != null) { return vkLink @@ -664,6 +669,25 @@ object VKLinkParser { return null } + private fun parseStory(string: String): AbsLink? { + val matcher = patterns.PATTERN_STORY.matcher(string) + try { + if (matcher.find()) { + return matcher.group(1)?.let { + matcher.group(2)?.let { it1 -> + StoryLink( + it.toLong(), + it1.toInt(), + parseAccessKey(string) + ) + } + } + } + } catch (ignored: Exception) { + } + return null + } + private fun parseWall(string: String): AbsLink? { val matcher = patterns.PATTERN_WALL.matcher(string) return if (!matcher.find()) { diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/link/types/AbsLink.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/link/types/AbsLink.kt index 4b096c6b9..565713d39 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/link/types/AbsLink.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/link/types/AbsLink.kt @@ -34,6 +34,7 @@ abstract class AbsLink(val type: Int) { const val VIDEOS = 26 const val APP_LINK = 27 const val ARTICLE_LINK = 28 - const val CATALOG_V2_SECTION_LINK = 29 + const val STORY = 29 + const val CATALOG_V2_SECTION_LINK = 30 } } \ No newline at end of file diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/link/types/StoryLink.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/link/types/StoryLink.kt new file mode 100644 index 000000000..81b72a8f2 --- /dev/null +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/link/types/StoryLink.kt @@ -0,0 +1,11 @@ +package dev.ragnarok.fenrir.link.types + +class StoryLink(val ownerId: Long, val storyId: Int, val access_key: String?) : AbsLink(STORY) { + override fun toString(): String { + return "StoryLink{" + + "ownerId=" + ownerId + + ", storyId=" + storyId + + ", Access_Key=" + access_key + + '}' + } +} diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/model/FeedbackVKOfficial.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/model/FeedbackVKOfficial.kt index 8e98f7aad..a82062e99 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/model/FeedbackVKOfficial.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/model/FeedbackVKOfficial.kt @@ -19,6 +19,7 @@ class FeedbackVKOfficial { var hide_query: String? = null var time: Long = 0 var images: ArrayList? = null + var images_action: Action? = null var attachments: ArrayList? = null var action: Action? = null fun getImage(prefSize: Int): ImageAdditional? { @@ -36,12 +37,13 @@ class FeedbackVKOfficial { return result } - @IntDef(Action_Types.MESSAGE, Action_Types.URL) + @IntDef(Action_Types.MESSAGE, Action_Types.BROWSER_URL, Action_Types.URL) @Retention(AnnotationRetention.SOURCE) annotation class Action_Types { companion object { const val MESSAGE = 0 - const val URL = 1 + const val BROWSER_URL = 1 + const val URL = 2 } } @@ -82,6 +84,19 @@ class FeedbackVKOfficial { } } + @Keep + @Serializable + @SerialName("action_browser_url") + class ActionBrowserURL(private val url: String?) : Action() { + fun getUrl(): String? { + return url + } + + override fun getActionType(): Int { + return Action_Types.BROWSER_URL + } + } + @Keep @Serializable class ImageAdditional { diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/push/message/LikeFCMMessage.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/push/message/LikeFCMMessage.kt index 32b910e4d..3cfb96766 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/push/message/LikeFCMMessage.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/push/message/LikeFCMMessage.kt @@ -150,6 +150,7 @@ class LikeFCMMessage { .setSmallIcon(R.drawable.heart) .setContentTitle(context.getString(R.string.like_title)) .setContentText(title) + .setStyle(NotificationCompat.BigTextStyle().bigText(title)) .setNumber(badge) .setAutoCancel(true) builder.priority = NotificationCompat.PRIORITY_HIGH diff --git a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/fragment/PreferencesFragment.kt b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/fragment/PreferencesFragment.kt index eea1647d6..60da9a43b 100644 --- a/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/fragment/PreferencesFragment.kt +++ b/app_filegallery/src/main/kotlin/dev/ragnarok/filegallery/fragment/PreferencesFragment.kt @@ -797,6 +797,12 @@ class PreferencesFragment : AbsPreferencesFragment(), PreferencesAdapter.OnScree ).absolutePath || sel.absolutePath == File( Environment.getExternalStorageDirectory(), ".sstmp" + ).absolutePath || sel.absolutePath == File( + Environment.getExternalStorageDirectory(), + ".SLOGAN" + ).absolutePath || sel.absolutePath == File( + Environment.getExternalStorageDirectory(), + ".OplusOS" ).absolutePath || sel.absolutePath == File( Environment.getExternalStorageDirectory(), ".time" diff --git a/build.gradle b/build.gradle index 9042bd015..6754a633e 100644 --- a/build.gradle +++ b/build.gradle @@ -13,18 +13,18 @@ buildscript { ext.appFileGalleryVersionName = "1.999" //androidx libraries - ext.activityVersion = "1.7.1" + ext.activityVersion = "1.7.2" ext.annotationVersion = "1.6.0" ext.appcompatVersion = "1.6.1" ext.biometricVersion = "1.2.0-alpha05" ext.browserVersion = "1.5.0" - ext.cameraVersion = "1.2.2" + ext.cameraVersion = "1.2.3" ext.cardviewVersion = "1.0.0" ext.collectionVersion = "1.3.0-alpha04" ext.concurentVersion = "1.2.0-alpha01" - ext.constraintlayoutVersion = "2.2.0-alpha09" + ext.constraintlayoutVersion = "2.2.0-alpha10" ext.coordinatorlayoutVersion = "1.2.0" - ext.coreVersion = "1.10.1" + ext.coreVersion = "1.11.0-beta01" ext.customviewVersion = "1.2.0-alpha02" ext.customviewPoolingcontainerVersion = "1.0.0" ext.drawerlayoutVersion = "1.2.0" @@ -35,10 +35,10 @@ buildscript { ext.loaderVersion = "1.1.0" ext.mediaVersion = "1.7.0-alpha01" ext.media3Version = "1.1.0-alpha01" - ext.recyclerviewVersion = "1.3.0" + ext.recyclerviewVersion = "1.3.1-rc01" ext.savedStateVersion = "1.2.1" ext.swiperefreshlayoutVersion = "1.2.0-alpha01" - ext.tracingVersion = "1.2.0-beta04" + ext.tracingVersion = "1.2.0-rc01" ext.transitionVersion = "1.4.1" ext.vectordrawableVersion = "1.2.0-beta01" ext.webkitVersion = "1.7.0-rc01" @@ -58,12 +58,12 @@ buildscript { //common libraries ext.kotlin_version = "1.8.21" - ext.kotlin_coroutines = "1.7.0" + ext.kotlin_coroutines = "1.7.1" ext.kotlin_serializer = "1.5.1" ext.okhttpLibraryVersion = "5.0.0-alpha.11" ext.okioVersion = "3.3.0" ext.rxJavaVersion = "3.1.6" - ext.guavaVersion = "31.1-android" + ext.guavaVersion = "32.0.0-android" ext.errorproneVersion = "2.15.0" ext.checkerCompatQualVersion = "2.5.5" ext.checkerQualAndroidVersion = "3.34.0" @@ -91,7 +91,7 @@ buildscript { mavenCentral() } dependencies { - classpath "com.android.tools.build:gradle:8.0.1" + classpath "com.android.tools.build:gradle:8.0.2" classpath "com.google.gms:google-services:4.3.15" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" diff --git a/camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java b/camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java index 64494281d..a7b28fbd5 100644 --- a/camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java +++ b/camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java @@ -631,7 +631,7 @@ public void onUseCaseReset(@NonNull UseCase useCase) { mExecutor.execute(() -> { debugLog("Use case " + useCaseId + " RESET"); mUseCaseAttachState.updateUseCase(useCaseId, sessionConfig, useCaseConfig); - + addOrRemoveMeteringRepeatingUseCase(); resetCaptureSession(/*abortInFlightCaptures=*/false); updateCaptureSessionConfig(); diff --git a/libfenrir/src/main/jni/animation/libyuv/source/row_neon.cc b/libfenrir/src/main/jni/animation/libyuv/source/row_neon.cc index d734b817c..b653765cc 100644 --- a/libfenrir/src/main/jni/animation/libyuv/source/row_neon.cc +++ b/libfenrir/src/main/jni/animation/libyuv/source/row_neon.cc @@ -725,12 +725,12 @@ void DetileToYUY2_NEON(const uint8_t* src_y, void UnpackMT2T_NEON(const uint8_t* src, uint16_t* dst, size_t size) { asm volatile( "1: \n" - "vld1.8 q14, [%0]! \n" // Load lower bits. - "vld1.8 q9, [%0]! \n" // Load upper bits row + "vld1.8 {q14}, [%0]! \n" // Load lower bits. + "vld1.8 {q9}, [%0]! \n" // Load upper bits row // by row. - "vld1.8 q11, [%0]! \n" - "vld1.8 q13, [%0]! \n" - "vld1.8 q15, [%0]! \n" + "vld1.8 {q11}, [%0]! \n" + "vld1.8 {q13}, [%0]! \n" + "vld1.8 {q15}, [%0]! \n" "vshl.u8 q8, q14, #6 \n" // Shift lower bit data // appropriately. "vshl.u8 q10, q14, #4 \n" @@ -804,7 +804,7 @@ void SplitRGBRow_NEON(const uint8_t* src_rgb, "+r"(dst_b), // %3 "+r"(width) // %4 : // Input registers - : "cc", "memory", "d0", "d1", "d2" // Clobber List + : "cc", "memory", "q0", "q1", "q2" // Clobber List ); } @@ -3845,7 +3845,7 @@ void SplitUVRow_16_NEON(const uint16_t* src_uv, "+r"(dst_v), // %2 "+r"(width) // %3 : "r"(shift) // %4 - : "cc", "memory", "q0", "q1", "q2", "q3", "q4"); + : "cc", "memory", "q0", "q1", "q2"); } void MergeUVRow_16_NEON(const uint16_t* src_u, diff --git a/libfenrir/src/main/jni/animation/libyuv/source/row_rvv.cc b/libfenrir/src/main/jni/animation/libyuv/source/row_rvv.cc index 893333216..be4c4a309 100644 --- a/libfenrir/src/main/jni/animation/libyuv/source/row_rvv.cc +++ b/libfenrir/src/main/jni/animation/libyuv/source/row_rvv.cc @@ -28,20 +28,20 @@ extern "C" { // Fill YUV -> RGB conversion constants into vectors // NOTE: To match behavior on other platforms, vxrm (fixed-point rounding mode -// register) is set to round-down mode(2). +// register) is set to round-to-nearest-up mode(0). #define YUVTORGB_SETUP(yuvconst, vl, v_ub, v_vr, v_ug, v_vg, v_yg, v_bb, v_bg, \ v_br) \ { \ - asm volatile("csrwi vxrm, 2"); \ + asm volatile("csrwi vxrm, 0"); \ vl = __riscv_vsetvl_e8m1(w); \ v_ub = __riscv_vmv_v_x_u8m1(yuvconst->kUVCoeff[0], vl); \ v_vr = __riscv_vmv_v_x_u8m1(yuvconst->kUVCoeff[1], vl); \ v_ug = __riscv_vmv_v_x_u8m1(yuvconst->kUVCoeff[2], vl); \ v_vg = __riscv_vmv_v_x_u8m1(yuvconst->kUVCoeff[3], vl); \ v_yg = __riscv_vmv_v_x_u16m2(yuvconst->kRGBCoeffBias[0], vl); \ - v_bb = __riscv_vmv_v_x_u16m2(yuvconst->kRGBCoeffBias[1], vl); \ - v_bg = __riscv_vmv_v_x_u16m2(yuvconst->kRGBCoeffBias[2], vl); \ - v_br = __riscv_vmv_v_x_u16m2(yuvconst->kRGBCoeffBias[3], vl); \ + v_bb = __riscv_vmv_v_x_u16m2(yuvconst->kRGBCoeffBias[1] + 32, vl); \ + v_bg = __riscv_vmv_v_x_u16m2(yuvconst->kRGBCoeffBias[2] - 32, vl); \ + v_br = __riscv_vmv_v_x_u16m2(yuvconst->kRGBCoeffBias[3] + 32, vl); \ } // Read [VLEN/8] Y, [VLEN/(8 * 2)] U and [VLEN/(8 * 2)] V from 422 @@ -485,23 +485,21 @@ void I400ToARGBRow_RVV(const uint8_t* src_y, vuint16m4_t v_yb; vuint16m4_t v_yg = __riscv_vmv_v_x_u16m4(yuvconstants->kRGBCoeffBias[0], vl); // To match behavior on other platforms, vxrm (fixed-point rounding mode - // register) sets to round-down mode(2). - asm volatile("csrwi vxrm, 2"); + // register) sets to round-to-nearest-up mode(0). + asm volatile("csrwi vxrm, 0"); if (is_yb_positive) { - v_yb = __riscv_vmv_v_x_u16m4(yuvconstants->kRGBCoeffBias[4], vl); + v_yb = __riscv_vmv_v_x_u16m4(yuvconstants->kRGBCoeffBias[4] - 32, vl); } else { - v_yb = __riscv_vmv_v_x_u16m4(-yuvconstants->kRGBCoeffBias[4], vl); + v_yb = __riscv_vmv_v_x_u16m4(-yuvconstants->kRGBCoeffBias[4] + 32, vl); } do { vuint8m2_t v_y, v_out; vuint16m4_t v_y_16, v_tmp0, v_tmp1, v_tmp2; - vuint32m8_t v_y1; vl = __riscv_vsetvl_e8m2(w); v_y = __riscv_vle8_v_u8m2(src_y, vl); v_y_16 = __riscv_vwaddu_vx_u16m4(v_y, 0, vl); v_tmp0 = __riscv_vmul_vx_u16m4(v_y_16, 0x0101, vl); // 257 * v_y - v_y1 = __riscv_vwmulu_vv_u32m8(v_tmp0, v_yg, vl); - v_tmp1 = __riscv_vnsrl_wx_u16m4(v_y1, 16, vl); + v_tmp1 = __riscv_vmulhu_vv_u16m4(v_tmp0, v_yg, vl); if (is_yb_positive) { v_tmp2 = __riscv_vsaddu_vv_u16m4(v_tmp1, v_yb, vl); } else { diff --git a/libfenrir/src/main/jni/animation/libyuv/source/scale.cc b/libfenrir/src/main/jni/animation/libyuv/source/scale.cc index 90abaefe8..591a6a938 100644 --- a/libfenrir/src/main/jni/animation/libyuv/source/scale.cc +++ b/libfenrir/src/main/jni/animation/libyuv/source/scale.cc @@ -820,9 +820,10 @@ static void ScaleAddCols2_C(int dst_width, int ix = x >> 16; x += dx; boxwidth = MIN1((x >> 16) - ix); - assert(((boxwidth - minboxwidth) == 0) || ((boxwidth - minboxwidth) == 1)); + int scaletbl_index = boxwidth - minboxwidth; + assert((scaletbl_index == 0) || (scaletbl_index == 1)); *dst_ptr++ = (uint8_t)(SumPixels(boxwidth, src_ptr + ix) * - scaletbl[boxwidth - minboxwidth] >> + scaletbl[scaletbl_index] >> 16); } } @@ -843,10 +844,10 @@ static void ScaleAddCols2_16_C(int dst_width, int ix = x >> 16; x += dx; boxwidth = MIN1((x >> 16) - ix); - assert(((boxwidth - minboxwidth) == 0) || ((boxwidth - minboxwidth) == 1)); - *dst_ptr++ = SumPixels_16(boxwidth, src_ptr + ix) * - scaletbl[boxwidth - minboxwidth] >> - 16; + int scaletbl_index = boxwidth - minboxwidth; + assert((scaletbl_index == 0) || (scaletbl_index == 1)); + *dst_ptr++ = + SumPixels_16(boxwidth, src_ptr + ix) * scaletbl[scaletbl_index] >> 16; } } diff --git a/libfenrir/src/main/jni/rlottie/rlottie-master.zip b/libfenrir/src/main/jni/rlottie/rlottie-master.zip index 4e879b73c..9366bf958 100644 Binary files a/libfenrir/src/main/jni/rlottie/rlottie-master.zip and b/libfenrir/src/main/jni/rlottie/rlottie-master.zip differ diff --git a/libfenrir/src/main/jni/rlottie/src/lottie/lottieitem.cpp b/libfenrir/src/main/jni/rlottie/src/lottie/lottieitem.cpp index 0c97fea4e..be036bb22 100644 --- a/libfenrir/src/main/jni/rlottie/src/lottie/lottieitem.cpp +++ b/libfenrir/src/main/jni/rlottie/src/lottie/lottieitem.cpp @@ -168,6 +168,28 @@ bool renderer::Composition::render(const rlottie::Surface &surface, bool clear) painter.setDrawRegion( VRect(int(surface.drawRegionPosX()), int(surface.drawRegionPosY()), int(surface.drawRegionWidth()), int(surface.drawRegionHeight()))); + + // layer surface should be created if it is not created already or its size + // is changed. + bool isLayerSurfaceCreated = mSurfaceCache.is_layer_surface_created(); + bool isLayerSurfaceSizeChanged = + isLayerSurfaceCreated && + (mSurfaceCache.get_layer_surface()->width() != surface.width() || + mSurfaceCache.get_layer_surface()->height() != surface.height()); + + if (!isLayerSurfaceCreated || isLayerSurfaceSizeChanged) { + if (isLayerSurfaceCreated && isLayerSurfaceSizeChanged) + mSurfaceCache.delete_layer_surface(); + + mSurfaceCache.create_layer_surface(surface.width(), surface.height(), + VBitmap::Format::ARGB32_Premultiplied); + + // set layer draw region + mSurfaceCache.get_layer_painter()->setDrawRegion( + VRect(int(surface.drawRegionPosX()), int(surface.drawRegionPosY()), + int(surface.drawRegionWidth()), int(surface.drawRegionHeight()))); + } + mRootLayer->render(&painter, {}, {}, mSurfaceCache); painter.end(); return true; @@ -217,7 +239,7 @@ void renderer::Mask::preprocess(const VRect &clip) } void renderer::Layer::render(VPainter *painter, const VRle &inheritMask, - const VRle &matteRle, SurfaceCache &) + const VRle &matteRle, SurfaceCache &cache) { auto renderlist = renderList(); @@ -233,31 +255,42 @@ void renderer::Layer::render(VPainter *painter, const VRle &inheritMask, mask = inheritMask; } + VPainter *usedPainter = painter; + + if (cache.get_layer_painter() != nullptr) { + usedPainter = cache.get_layer_painter(); + usedPainter->begin(cache.get_layer_surface(), true); + } + for (auto &i : renderlist) { - painter->setBrush(i->mBrush); + usedPainter->setBrush(i->mBrush); VRle rle = i->rle(); if (matteRle.empty()) { if (mask.empty()) { // no mask no matte - painter->drawRle(VPoint(), rle); + usedPainter->drawRle(VPoint(), rle); } else { // only mask - painter->drawRle(rle, mask); + usedPainter->drawRle(rle, mask); } - } else { if (!mask.empty()) rle = rle & mask; if (rle.empty()) continue; if (matteType() == model::MatteType::AlphaInv) { rle = rle - matteRle; - painter->drawRle(VPoint(), rle); + usedPainter->drawRle(VPoint(), rle); } else { // render with matteRle as clip. - painter->drawRle(rle, matteRle); + usedPainter->drawRle(rle, matteRle); } } } + + if (cache.get_layer_painter() != nullptr) { + usedPainter->end(); + painter->drawBitmap(VPoint(), *cache.get_layer_surface(), mCombinedAlpha * 255.0f); + } } void renderer::LayerMask::preprocess(const VRect &clip) @@ -977,7 +1010,7 @@ void renderer::Group::update(int frameNo, const VMatrix &parentMatrix, mMatrix = m; - alpha = parentAlpha * mModel.transform()->opacity(frameNo); + alpha = mModel.transform()->opacity(frameNo); if (!vCompare(alpha, parentAlpha)) { newFlag |= DirtyFlagBit::Alpha; } @@ -1303,9 +1336,9 @@ renderer::Stroke::Stroke(model::Stroke *data) static vthread_local std::vector Dash_Vector; bool renderer::Stroke::updateContent(int frameNo, const VMatrix &matrix, - float alpha) + float) { - auto combinedAlpha = alpha * mModel.opacity(frameNo); + auto combinedAlpha = mModel.opacity(frameNo); auto color = mModel.color(frameNo).toColor(combinedAlpha); VBrush brush(color); diff --git a/libfenrir/src/main/jni/rlottie/src/lottie/lottieitem.h b/libfenrir/src/main/jni/rlottie/src/lottie/lottieitem.h index 1be7694b1..0628bdc9f 100644 --- a/libfenrir/src/main/jni/rlottie/src/lottie/lottieitem.h +++ b/libfenrir/src/main/jni/rlottie/src/lottie/lottieitem.h @@ -106,9 +106,31 @@ class SurfaceCache { } void release_surface(VBitmap &surface) { mCache.push_back(surface); } + bool is_layer_surface_created() const { return mIsLayerBitmapCreated; } + void create_layer_surface(size_t width, size_t height, VBitmap::Format format) + { + if (mIsLayerBitmapCreated) return; + + mLayerBitmap = std::make_unique(width, height, format); + mBitmapPainter = std::make_unique(mLayerBitmap.get(), true); + mIsLayerBitmapCreated = true; + } + void delete_layer_surface() + { + if (!mIsLayerBitmapCreated) return; + + mLayerBitmap.reset(); + mBitmapPainter.reset(); + mIsLayerBitmapCreated = false; + } + VPainter* get_layer_painter() const { return mBitmapPainter.get(); } + VBitmap* get_layer_surface() const { return mLayerBitmap.get(); } private: std::vector mCache; + std::unique_ptr mLayerBitmap{nullptr}; + std::unique_ptr mBitmapPainter{nullptr}; + bool mIsLayerBitmapCreated{false}; }; class Drawable final : public VDrawable { diff --git a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h index cf658a6ab..59a83ab06 100644 --- a/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h +++ b/libfenrir/src/main/jni/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h @@ -94,7 +94,7 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, u auto h = static_cast(region.max.y - region.min.y); auto w = static_cast(region.max.x - region.min.x); - auto ialpha = 255 - static_cast(_alpha(color)); + uint32_t ialpha = 255 - a; auto avxColor = _mm_set1_epi32(color); auto avxIalpha = _mm_set1_epi8(ialpha); @@ -148,7 +148,7 @@ static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, ui if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); else src = color; - auto ialpha = 255 - static_cast(_alpha(src)); + auto ialpha = _ialpha(src); //1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required) auto notAligned = ((uintptr_t)dst & 0xf) / 4; diff --git a/viewpager2/src/main/java/androidx/recyclerview/widget/AsyncDifferConfig.java b/viewpager2/src/main/java/androidx/recyclerview/widget/AsyncDifferConfig.java index 7a102fdba..ccd9cfae6 100644 --- a/viewpager2/src/main/java/androidx/recyclerview/widget/AsyncDifferConfig.java +++ b/viewpager2/src/main/java/androidx/recyclerview/widget/AsyncDifferConfig.java @@ -50,6 +50,7 @@ public final class AsyncDifferConfig { mDiffCallback = diffCallback; } + /** @hide */ @SuppressWarnings("WeakerAccess") @RestrictTo(RestrictTo.Scope.LIBRARY) @Nullable @@ -93,6 +94,7 @@ public Builder(@NonNull DiffUtil.ItemCallback diffCallback) { * @param executor The executor which can run tasks in the UI thread. * @return this * + * @hide */ @RestrictTo(RestrictTo.Scope.LIBRARY) @NonNull diff --git a/viewpager2/src/main/java/androidx/recyclerview/widget/GridLayoutManager.java b/viewpager2/src/main/java/androidx/recyclerview/widget/GridLayoutManager.java index 1534330c0..84d11622e 100644 --- a/viewpager2/src/main/java/androidx/recyclerview/widget/GridLayoutManager.java +++ b/viewpager2/src/main/java/androidx/recyclerview/widget/GridLayoutManager.java @@ -17,7 +17,6 @@ import android.content.Context; import android.graphics.Rect; -import android.os.Bundle; import android.util.AttributeSet; import android.util.Log; import android.util.SparseIntArray; @@ -26,7 +25,6 @@ import android.widget.GridView; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import java.util.Arrays; @@ -42,7 +40,6 @@ public class GridLayoutManager extends LinearLayoutManager { private static final boolean DEBUG = false; private static final String TAG = "GridLayoutManager"; public static final int DEFAULT_SPAN_COUNT = -1; - /** * Span size have been changed but we've not done a new layout calculation. */ @@ -122,7 +119,7 @@ public void setStackFromEnd(boolean stackFromEnd) { public int getRowCountForAccessibility(RecyclerView.Recycler recycler, RecyclerView.State state) { if (mOrientation == HORIZONTAL) { - return Math.min(mSpanCount, getItemCount()); + return mSpanCount; } if (state.getItemCount() < 1) { return 0; @@ -136,7 +133,7 @@ public int getRowCountForAccessibility(RecyclerView.Recycler recycler, public int getColumnCountForAccessibility(RecyclerView.Recycler recycler, RecyclerView.State state) { if (mOrientation == VERTICAL) { - return Math.min(mSpanCount, getItemCount()); + return mSpanCount; } if (state.getItemCount() < 1) { return 0; @@ -177,57 +174,6 @@ public void onInitializeAccessibilityNodeInfo(@NonNull RecyclerView.Recycler rec info.setClassName(GridView.class.getName()); } - @Override - boolean performAccessibilityAction(int action, @Nullable Bundle args) { - if (action == android.R.id.accessibilityActionScrollToPosition) { - final int noRow = -1; - final int noColumn = -1; - if (args != null) { - int rowArg = args.getInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_ROW_INT, - noRow); - int columnArg = args.getInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_COLUMN_INT, - noColumn); - - if (rowArg == noRow || columnArg == noColumn) { - return false; - } - - int itemCount = mRecyclerView.mAdapter.getItemCount(); - - int position = -1; - for (int i = 0; i < itemCount; i++) { - // Corresponds to a column value if the orientation is VERTICAL and a row value - // if the orientation is HORIZONTAL - int spanIndex = getSpanIndex(mRecyclerView.mRecycler, mRecyclerView.mState, i); - - // Corresponds to a row value if the orientation is VERTICAL and a column value - // if the orientation is HORIZONTAL - int spanGroupIndex = getSpanGroupIndex(mRecyclerView.mRecycler, - mRecyclerView.mState, i); - - if (mOrientation == VERTICAL) { - if (spanIndex == columnArg && spanGroupIndex == rowArg) { - position = i; - break; - } - } else { // horizontal - if (spanIndex == rowArg && spanGroupIndex == columnArg) { - position = i; - break; - } - } - } - - if (position > -1) { - scrollToPositionWithOffset(position, 0); - return true; - } - return false; - } - } - return super.performAccessibilityAction(action, args); - } - @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { if (state.isPreLayout()) { @@ -1018,9 +964,6 @@ int getCachedSpanGroupIndex(int position, int spanCount) { /** * Returns the final span index of the provided position. *

- * If {@link #getOrientation()} is {@link #VERTICAL}, this is a column value. - * If {@link #getOrientation()} is {@link #HORIZONTAL}, this is a row value. - *

* If you have a faster way to calculate span index for your items, you should override * this method. Otherwise, you should enable span index cache * ({@link #setSpanIndexCacheEnabled(boolean)}) for better performance. When caching is @@ -1097,9 +1040,6 @@ static int findFirstKeyLessThan(SparseIntArray cache, int position) { /** * Returns the index of the group this position belongs. *

- * If {@link #getOrientation()} is {@link #VERTICAL}, this is a row value. - * If {@link #getOrientation()} is {@link #HORIZONTAL}, this is a column value. - *

* For example, if grid has 3 columns and each item occupies 1 span, span group index * for item 1 will be 0, item 5 will be 1. * @@ -1506,4 +1446,5 @@ public int getSpanSize() { return mSpanSize; } } -} \ No newline at end of file + +} diff --git a/viewpager2/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java b/viewpager2/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java index 3a3f1728e..b28479f0c 100644 --- a/viewpager2/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java +++ b/viewpager2/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java @@ -19,8 +19,6 @@ import android.annotation.SuppressLint; import android.content.Context; import android.graphics.PointF; -import android.os.Build; -import android.os.Bundle; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; @@ -29,10 +27,7 @@ import android.view.accessibility.AccessibilityEvent; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat; import androidx.tracing.Trace; import java.util.List; @@ -261,56 +256,6 @@ public void onInitializeAccessibilityEvent(AccessibilityEvent event) { } } - @Override - public void onInitializeAccessibilityNodeInfo(@NonNull RecyclerView.Recycler recycler, - @NonNull RecyclerView.State state, @NonNull AccessibilityNodeInfoCompat info) { - super.onInitializeAccessibilityNodeInfo(recycler, state, info); - // TODO(b/251823537) - if (mRecyclerView.mAdapter != null && mRecyclerView.mAdapter.getItemCount() > 0) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - info.addAction(AccessibilityActionCompat.ACTION_SCROLL_TO_POSITION); - } - } - } - - @Override - boolean performAccessibilityAction(int action, @Nullable Bundle args) { - if (super.performAccessibilityAction(action, args)) { - return true; - } - - if (action == android.R.id.accessibilityActionScrollToPosition && args != null) { - int position = -1; - - if (mOrientation == VERTICAL) { - final int rowArg = args.getInt( - AccessibilityNodeInfoCompat.ACTION_ARGUMENT_ROW_INT, -1); - if (rowArg < 0) { - return false; - } - position = Math.min(rowArg, getRowCountForAccessibility(mRecyclerView.mRecycler, - mRecyclerView.mState) - 1); - } else { // horizontal - final int columnArg = args.getInt( - AccessibilityNodeInfoCompat.ACTION_ARGUMENT_COLUMN_INT, -1); - if (columnArg < 0) { - return false; - } - position = Math.min(columnArg, - getColumnCountForAccessibility(mRecyclerView.mRecycler, - mRecyclerView.mState) - 1); - } - if (position >= 0) { - // We want the target element to be the first on screen. That way, a - // screenreader like Talkback can directly focus on it as part of its default focus - // logic. - scrollToPositionWithOffset(position, 0); - return true; - } - } - return false; - } - @Override @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly public Parcelable onSaveInstanceState() { diff --git a/viewpager2/src/main/java/androidx/recyclerview/widget/RecyclerView.java b/viewpager2/src/main/java/androidx/recyclerview/widget/RecyclerView.java index e1264963a..42e95d456 100644 --- a/viewpager2/src/main/java/androidx/recyclerview/widget/RecyclerView.java +++ b/viewpager2/src/main/java/androidx/recyclerview/widget/RecyclerView.java @@ -283,20 +283,9 @@ public class RecyclerView extends ViewGroup implements ScrollingView, */ private static final float FLING_DESTRETCH_FACTOR = 4f; - /** - * A {@link android.content.pm.PackageManager} feature specifying if a device's rotary encoder - * has low resolution. Low resolution rotary encoders produce small number of - * {@link MotionEvent}s per a 360 degree rotation, meaning that each {@link MotionEvent} has - * large scroll values, which make {@link #scrollBy(int, int)} calls feel broken (due to the - * fact that each event produces large scrolls, and scrolling with large pixels causes a visible - * jump that does not feel smooth). As such, we will try adjusting our handling of generic - * motion caused by such low resolution rotary encoders differently to make the scrolling - * experience smooth. - */ - static final String LOW_RES_ROTARY_ENCODER_FEATURE = "android.hardware.rotaryencoder.lowres"; - static final boolean DISPATCH_TEMP_DETACH = false; + /** @hide */ @RestrictTo(LIBRARY_GROUP_PREFIX) @IntDef({HORIZONTAL, VERTICAL}) @Retention(RetentionPolicy.SOURCE) @@ -695,12 +684,6 @@ public void run() { private int mLastAutoMeasureNonExactMeasuredWidth = 0; private int mLastAutoMeasureNonExactMeasuredHeight = 0; - /** - * Whether or not the device has {@link #LOW_RES_ROTARY_ENCODER_FEATURE}. Computed once and - * cached, since it's a static value that would not change on consecutive calls. - */ - @VisibleForTesting boolean mLowResRotaryEncoderFeature; - /** * The callback to convert view info diffs into animations. */ @@ -811,9 +794,6 @@ public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int } a.recycle(); - mLowResRotaryEncoderFeature = - context.getPackageManager().hasSystemFeature(LOW_RES_ROTARY_ENCODER_FEATURE); - // Create the layoutManager if specified. createLayoutManager(context, layoutManagerName, attrs, defStyleAttr, 0); @@ -2184,12 +2164,6 @@ boolean scrollByInternal(int x, int y, MotionEvent ev, int type) { if (getOverScrollMode() != View.OVER_SCROLL_NEVER) { if (ev != null && !MotionEventCompat.isFromSource(ev, InputDevice.SOURCE_MOUSE)) { pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY); - // For rotary encoders, we release stretch EdgeEffects after they are pulled, to - // avoid the effects being stuck pulled. - if (Build.VERSION.SDK_INT >= 31 - && MotionEventCompat.isFromSource(ev, InputDevice.SOURCE_ROTARY_ENCODER)) { - releaseGlows(); - } } considerReleasingGlowsOnScroll(x, y); } @@ -2284,7 +2258,7 @@ private int releaseVerticalGlow(int deltaY, float x) { /** *

Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal - * range. This value is used to compute the position of the thumb within the scrollbar's track. + * range. This value is used to compute the length of the thumb within the scrollbar's track. *

* *

The range is expressed in arbitrary units that must be the same as the units used by @@ -2345,7 +2319,7 @@ public int computeHorizontalScrollExtent() { * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your * LayoutManager.

* - * @return The total horizontal range represented by the horizontal scrollbar + * @return The total horizontal range represented by the vertical scrollbar * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State) */ @Override @@ -2358,7 +2332,7 @@ public int computeHorizontalScrollRange() { /** *

Compute the vertical offset of the vertical scrollbar's thumb within the vertical range. - * This value is used to compute the position of the thumb within the scrollbar's track.

+ * This value is used to compute the length of the thumb within the scrollbar's track.

* *

The range is expressed in arbitrary units that must be the same as the units used by * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.

@@ -3960,8 +3934,6 @@ public boolean onGenericMotionEvent(MotionEvent event) { if (mLayoutSuppressed) { return false; } - - boolean useSmoothScroll = false; if (event.getAction() == MotionEvent.ACTION_SCROLL) { final float vScroll, hScroll; if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) { @@ -3991,25 +3963,14 @@ public boolean onGenericMotionEvent(MotionEvent event) { vScroll = 0f; hScroll = 0f; } - // Use smooth scrolling for low resolution rotary encoders to avoid the visible - // pixel jumps that would be caused by doing regular scrolling. - useSmoothScroll = mLowResRotaryEncoderFeature; } else { vScroll = 0f; hScroll = 0f; } - int scaledVScroll = (int) (vScroll * mScaledVerticalScrollFactor); - int scaledHScroll = (int) (hScroll * mScaledHorizontalScrollFactor); - if (useSmoothScroll) { - OverScroller overScroller = mViewFlinger.mOverScroller; - // Account for any remaining scroll from a previous generic motion event. - scaledVScroll += overScroller.getFinalY() - overScroller.getCurrY(); - scaledHScroll += overScroller.getFinalX() - overScroller.getCurrX(); - smoothScrollBy(scaledHScroll, scaledVScroll, /* interpolator= */ null, - UNDEFINED_DURATION, /* withNestedScrolling= */ true); - } else { - nestedScrollByInternal(scaledHScroll, scaledVScroll, event, TYPE_NON_TOUCH); + if (vScroll != 0 || hScroll != 0) { + nestedScrollByInternal((int) (hScroll * mScaledHorizontalScrollFactor), + (int) (vScroll * mScaledVerticalScrollFactor), event, TYPE_NON_TOUCH); } } return false; @@ -4976,7 +4937,7 @@ void markItemDecorInsetsDirty() { } @Override - public void draw(@NonNull Canvas c) { + public void draw(Canvas c) { super.draw(c); final int count = mItemDecorations.size(); @@ -5037,7 +4998,7 @@ public void draw(@NonNull Canvas c) { } @Override - public void onDraw(@NonNull Canvas c) { + public void onDraw(Canvas c) { super.onDraw(c); final int count = mItemDecorations.size(); @@ -5577,7 +5538,7 @@ public View findChildViewUnder(float x, float y) { } @Override - public boolean drawChild(@NonNull Canvas canvas, View child, long drawingTime) { + public boolean drawChild(Canvas canvas, View child, long drawingTime) { return super.drawChild(canvas, child, drawingTime); } @@ -11015,7 +10976,7 @@ public int computeHorizontalScrollOffset(@NonNull State state) { *

Default implementation returns 0.

* * @param state Current State of RecyclerView where you can find total item count - * @return The total horizontal range represented by the horizontal scrollbar + * @return The total horizontal range represented by the vertical scrollbar * @see RecyclerView#computeHorizontalScrollRange() */ public int computeHorizontalScrollRange(@NonNull State state) { @@ -11299,12 +11260,6 @@ void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCo public void onInitializeAccessibilityNodeInfoForItem(@NonNull Recycler recycler, @NonNull State state, @NonNull View host, @NonNull AccessibilityNodeInfoCompat info) { - int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0; - int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0; - final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo = - AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1, - columnIndexGuess, 1, false, false); - info.setCollectionItemInfo(itemInfo); } /** @@ -11353,10 +11308,7 @@ public int getSelectionModeForAccessibility(@NonNull Recycler recycler, * @return The number of rows in LayoutManager for accessibility. */ public int getRowCountForAccessibility(@NonNull Recycler recycler, @NonNull State state) { - if (mRecyclerView == null || mRecyclerView.mAdapter == null) { - return 1; - } - return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1; + return -1; } /** @@ -11373,10 +11325,7 @@ public int getRowCountForAccessibility(@NonNull Recycler recycler, @NonNull Stat */ public int getColumnCountForAccessibility(@NonNull Recycler recycler, @NonNull State state) { - if (mRecyclerView == null || mRecyclerView.mAdapter == null) { - return 1; - } - return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1; + return -1; } /** @@ -13298,6 +13247,7 @@ public void notifyItemMoved(int fromPosition, int toPosition) { /** * This is public so that the CREATOR can be accessed on cold launch. * + * @hide */ @RestrictTo(LIBRARY) public static class SavedState extends AbsSavedState { diff --git a/viewpager2/src/main/java/androidx/recyclerview/widget/StaggeredGridLayoutManager.java b/viewpager2/src/main/java/androidx/recyclerview/widget/StaggeredGridLayoutManager.java index 63ce8e4fb..19e5f832e 100644 --- a/viewpager2/src/main/java/androidx/recyclerview/widget/StaggeredGridLayoutManager.java +++ b/viewpager2/src/main/java/androidx/recyclerview/widget/StaggeredGridLayoutManager.java @@ -32,7 +32,6 @@ import androidx.annotation.Nullable; import androidx.annotation.RestrictTo; import androidx.core.view.ViewCompat; -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import java.util.ArrayList; import java.util.Arrays; @@ -1283,36 +1282,6 @@ public Parcelable onSaveInstanceState() { return state; } - @Override - public void onInitializeAccessibilityNodeInfo(@NonNull RecyclerView.Recycler recycler, - @NonNull RecyclerView.State state, @NonNull AccessibilityNodeInfoCompat info) { - super.onInitializeAccessibilityNodeInfo(recycler, state, info); - // Setting the classname allows accessibility services to set a role for staggered grids - // and ensures that they are treated distinctly from canonical grids with clear row/column - // semantics. - info.setClassName("androidx.recyclerview.widget.StaggeredGridLayoutManager"); - } - - @Override - public void onInitializeAccessibilityNodeInfoForItem(@NonNull RecyclerView.Recycler recycler, - @NonNull RecyclerView.State state, @NonNull View host, - @NonNull AccessibilityNodeInfoCompat info) { - ViewGroup.LayoutParams lp = host.getLayoutParams(); - if (!(lp instanceof LayoutParams)) { - super.onInitializeAccessibilityNodeInfoForItem(host, info); - return; - } - LayoutParams sglp = (LayoutParams) lp; - if (mOrientation == HORIZONTAL) { - info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( - sglp.getSpanIndex(), sglp.mFullSpan ? mSpanCount : 1, - -1, -1, false, false)); - } else { // VERTICAL - info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain( - -1, -1, - sglp.getSpanIndex(), sglp.mFullSpan ? mSpanCount : 1, false, false)); - } - } @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); @@ -1345,24 +1314,6 @@ int findFirstVisibleItemPositionInt() { return first == null ? RecyclerView.NO_POSITION : getPosition(first); } - @Override - public int getRowCountForAccessibility(@NonNull RecyclerView.Recycler recycler, - @NonNull RecyclerView.State state) { - if (mOrientation == HORIZONTAL) { - return Math.min(mSpanCount, state.getItemCount()); - } - return -1; - } - - @Override - public int getColumnCountForAccessibility(@NonNull RecyclerView.Recycler recycler, - @NonNull RecyclerView.State state) { - if (mOrientation == VERTICAL) { - return Math.min(mSpanCount, state.getItemCount()); - } - return -1; - } - /** * This is for internal use. Not necessarily the child closest to start but the first child * we find that matches the criteria. @@ -2120,6 +2071,7 @@ public void scrollToPositionWithOffset(int position, int offset) { requestLayout(); } + /** @hide */ @Override @RestrictTo(LIBRARY) public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,