diff --git a/app_fenrir/build.gradle b/app_fenrir/build.gradle index 21884db4a..a0c9d77d8 100644 --- a/app_fenrir/build.gradle +++ b/app_fenrir/build.gradle @@ -18,6 +18,7 @@ android { exclude("META-INF/LICENSE") exclude("META-INF/NOTICE") exclude("META-INF/*.version") + exclude("META-INF/versions/**") } dependenciesInfo { diff --git a/app_fenrir/src/main/AndroidManifest.xml b/app_fenrir/src/main/AndroidManifest.xml index e36b34188..4e7c2faba 100644 --- a/app_fenrir/src/main/AndroidManifest.xml +++ b/app_fenrir/src/main/AndroidManifest.xml @@ -401,6 +401,14 @@ android:theme="@style/App.DayNight.Swipes" android:windowSoftInputMode="adjustResize|stateHidden" /> + + place.launchActivityForResult( + this, + ShortVideoPagerActivity.newInstance(this, args) + ) + Place.SINGLE_PHOTO -> place.launchActivityForResult( this, SinglePhotoActivity.newInstance(this, args) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/ChatActivityBubbles.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/ChatActivityBubbles.kt index c25c2b008..7960699b7 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/ChatActivityBubbles.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/ChatActivityBubbles.kt @@ -14,6 +14,7 @@ import dev.ragnarok.fenrir.Extra import dev.ragnarok.fenrir.R import dev.ragnarok.fenrir.activity.gifpager.GifPagerActivity import dev.ragnarok.fenrir.activity.photopager.PhotoPagerActivity.Companion.newInstance +import dev.ragnarok.fenrir.activity.shortvideopager.ShortVideoPagerActivity import dev.ragnarok.fenrir.activity.slidr.Slidr.attach import dev.ragnarok.fenrir.activity.slidr.model.SlidrConfig import dev.ragnarok.fenrir.activity.slidr.model.SlidrListener @@ -115,6 +116,11 @@ class ChatActivityBubbles : NoMainActivity(), PlaceProvider, AppStyleable { StoryPagerActivity.newInstance(this, args) ) + Place.SHORT_VIDEOS -> place.launchActivityForResult( + this, + ShortVideoPagerActivity.newInstance(this, args) + ) + Place.GIF_PAGER -> place.launchActivityForResult( this, GifPagerActivity.newInstance(this, args) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/LocalJsonToChatActivity.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/LocalJsonToChatActivity.kt index 26d9d831e..537f87457 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/LocalJsonToChatActivity.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/LocalJsonToChatActivity.kt @@ -13,6 +13,7 @@ import dev.ragnarok.fenrir.Extra import dev.ragnarok.fenrir.R import dev.ragnarok.fenrir.activity.gifpager.GifPagerActivity import dev.ragnarok.fenrir.activity.photopager.PhotoPagerActivity.Companion.newInstance +import dev.ragnarok.fenrir.activity.shortvideopager.ShortVideoPagerActivity import dev.ragnarok.fenrir.activity.storypager.StoryPagerActivity import dev.ragnarok.fenrir.fragment.audio.AudioPlayerFragment import dev.ragnarok.fenrir.fragment.audio.AudioPlayerFragment.Companion.newInstance @@ -100,6 +101,11 @@ class LocalJsonToChatActivity : NoMainActivity(), PlaceProvider, AppStyleable { StoryPagerActivity.newInstance(this, args) ) + Place.SHORT_VIDEOS -> place.launchActivityForResult( + this, + ShortVideoPagerActivity.newInstance(this, args) + ) + Place.SINGLE_PHOTO -> place.launchActivityForResult( this, SinglePhotoActivity.newInstance(this, args) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/LoginActivity.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/LoginActivity.kt index d6d53d0c7..18e795387 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/LoginActivity.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/LoginActivity.kt @@ -35,6 +35,10 @@ class LoginActivity : AppCompatActivity() { setContentView(R.layout.activity_login) val webview = findViewById(R.id.vkontakteview) webview.settings.javaScriptEnabled = true + webview.settings.domStorageEnabled = true + webview.settings.blockNetworkLoads = false + webview.settings.blockNetworkImage = false + webview.settings.databaseEnabled = true webview.clearCache(true) webview.settings.userAgentString = getUserAgentByType(Constants.DEFAULT_ACCOUNT_TYPE) 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 10503a50a..53d403873 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 @@ -40,6 +40,7 @@ import dev.ragnarok.fenrir.activity.EnterPinActivity.Companion.getClass import dev.ragnarok.fenrir.activity.gifpager.GifPagerActivity import dev.ragnarok.fenrir.activity.photopager.PhotoPagerActivity.Companion.newInstance import dev.ragnarok.fenrir.activity.qr.CameraScanActivity +import dev.ragnarok.fenrir.activity.shortvideopager.ShortVideoPagerActivity import dev.ragnarok.fenrir.activity.storypager.StoryPagerActivity import dev.ragnarok.fenrir.db.Stores import dev.ragnarok.fenrir.dialog.ResolveDomainDialog @@ -86,6 +87,7 @@ import dev.ragnarok.fenrir.fragment.friends.friendstabs.FriendsTabsFragment import dev.ragnarok.fenrir.fragment.gifts.GiftsFragment import dev.ragnarok.fenrir.fragment.groupchats.GroupChatsFragment import dev.ragnarok.fenrir.fragment.likes.LikesFragment +import dev.ragnarok.fenrir.fragment.likes.storiesview.StoriesViewFragment import dev.ragnarok.fenrir.fragment.localserver.filemanagerremote.FileManagerRemoteFragment import dev.ragnarok.fenrir.fragment.localserver.photoslocalserver.PhotosLocalServerFragment import dev.ragnarok.fenrir.fragment.logs.LogsFragment @@ -615,7 +617,7 @@ open class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks, OnSect val menus = ModalBottomSheetDialogFragment.Builder() menus.add( OptionRequest( - R.id.button_ok, + 0, getString(R.string.set_offline), R.drawable.offline, true @@ -623,7 +625,7 @@ open class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks, OnSect ) menus.add( OptionRequest( - R.id.button_cancel, + 1, getString(R.string.open_clipboard_url), R.drawable.web, false @@ -631,7 +633,25 @@ open class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks, OnSect ) menus.add( OptionRequest( - R.id.action_preferences, + 2, + getString(R.string.stories), + R.drawable.story_outline, + true + ) + ) + if (Utils.isOfficialVKCurrent) { + menus.add( + OptionRequest( + 3, + getString(R.string.clips), + R.drawable.clip_outline, + true + ) + ) + } + menus.add( + OptionRequest( + 4, getString(R.string.settings), R.drawable.preferences, true @@ -639,7 +659,7 @@ open class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks, OnSect ) menus.add( OptionRequest( - R.id.button_camera, + 5, getString(R.string.scan_qr), R.drawable.qr_code, false @@ -650,8 +670,8 @@ open class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks, OnSect "left_options", object : ModalBottomSheetDialogFragment.Listener { override fun onModalOptionSelected(option: Option) { - when { - option.id == R.id.button_ok -> { + when (option.id) { + 0 -> { mCompositeDisposable.add(InteractorFactory.createAccountInteractor() .setOffline( Settings.get().accounts().current @@ -664,7 +684,7 @@ open class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks, OnSect }) } - option.id == R.id.button_cancel -> { + 1 -> { val clipBoard = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager? if (clipBoard != null && clipBoard.primaryClip != null && (clipBoard.primaryClip?.itemCount @@ -682,12 +702,42 @@ open class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks, OnSect } } - option.id == R.id.action_preferences -> { + 2 -> { + mCompositeDisposable.add(InteractorFactory.createStoriesInteractor() + .getStory( + Settings.get().accounts().current, + null + ) + .fromIOToMain() + .subscribe({ + if (it.isEmpty()) { + createCustomToast(this@MainActivity).showToastError( + R.string.list_is_empty + ) + } + PlaceFactory.getHistoryVideoPreviewPlace( + mAccountId, + ArrayList(it), + 0 + ).tryOpenWith(this@MainActivity) + }) { + createCustomToast(this@MainActivity).showToastThrowable( + it + ) + }) + } + + 3 -> { + PlaceFactory.getShortVideoPlace(mAccountId, null) + .tryOpenWith(this@MainActivity) + } + + 4 -> { PlaceFactory.getPreferencesPlace(mAccountId) .tryOpenWith(this@MainActivity) } - option.id == R.id.button_camera && FenrirNative.isNativeLoaded -> { + 5 -> { val intent = Intent( this@MainActivity, @@ -1284,6 +1334,11 @@ open class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks, OnSect StoryPagerActivity.newInstance(this, args) ) + Place.SHORT_VIDEOS -> place.launchActivityForResult( + this, + ShortVideoPagerActivity.newInstance(this, args) + ) + Place.FRIENDS_AND_FOLLOWERS -> attachToFront(FriendsTabsFragment.newInstance(args)) Place.EXTERNAL_LINK -> attachToFront(BrowserFragment.newInstance(args)) Place.DOC_PREVIEW -> { @@ -1480,6 +1535,7 @@ open class MainActivity : AppCompatActivity(), NavigationDrawerCallbacks, OnSect Place.NOTIFICATION_SETTINGS -> attachToFront(NotificationPreferencesFragment()) Place.LIKES_AND_COPIES -> attachToFront(LikesFragment.newInstance(args)) + Place.STORIES_VIEWS -> attachToFront(StoriesViewFragment.newInstance(args)) Place.CREATE_PHOTO_ALBUM, Place.EDIT_PHOTO_ALBUM -> { val createPhotoAlbumFragment = CreatePhotoAlbumFragment.newInstance(args) attachToFront(createPhotoAlbumFragment) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/NotReadMessagesActivity.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/NotReadMessagesActivity.kt index cf3a2e291..5e057e4d7 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/NotReadMessagesActivity.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/NotReadMessagesActivity.kt @@ -13,6 +13,7 @@ import dev.ragnarok.fenrir.Extra import dev.ragnarok.fenrir.R import dev.ragnarok.fenrir.activity.gifpager.GifPagerActivity import dev.ragnarok.fenrir.activity.photopager.PhotoPagerActivity.Companion.newInstance +import dev.ragnarok.fenrir.activity.shortvideopager.ShortVideoPagerActivity import dev.ragnarok.fenrir.activity.slidr.Slidr.attach import dev.ragnarok.fenrir.activity.slidr.model.SlidrConfig import dev.ragnarok.fenrir.activity.slidr.model.SlidrListener @@ -115,6 +116,11 @@ class NotReadMessagesActivity : NoMainActivity(), PlaceProvider, AppStyleable { StoryPagerActivity.newInstance(this, args) ) + Place.SHORT_VIDEOS -> place.launchActivityForResult( + this, + ShortVideoPagerActivity.newInstance(this, args) + ) + Place.SINGLE_PHOTO -> place.launchActivityForResult( this, SinglePhotoActivity.newInstance(this, args) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/SinglePhotoActivity.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/SinglePhotoActivity.kt index ea997a4ee..0a6c36875 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/SinglePhotoActivity.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/SinglePhotoActivity.kt @@ -45,7 +45,8 @@ import java.io.File import java.lang.ref.WeakReference import java.text.DateFormat import java.text.SimpleDateFormat -import java.util.* +import java.util.Calendar +import java.util.Date import java.util.concurrent.TimeUnit class SinglePhotoActivity : NoMainActivity(), PlaceProvider, AppStyleable { diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/ValidateActivity.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/ValidateActivity.kt index d70e4ea4a..4a634dc07 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/ValidateActivity.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/ValidateActivity.kt @@ -85,6 +85,10 @@ class ValidateActivity : AppCompatActivity() { ) val webview = findViewById(R.id.vkontakteview) webview.settings.javaScriptEnabled = true + webview.settings.domStorageEnabled = true + webview.settings.blockNetworkLoads = false + webview.settings.blockNetworkImage = false + webview.settings.databaseEnabled = true webview.clearCache(true) webview.settings.userAgentString = UserAgentTool.getAccountUserAgent(accountId) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/crash/CrashUtils.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/crash/CrashUtils.kt index d1b9883cd..a4137e801 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/crash/CrashUtils.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/crash/CrashUtils.kt @@ -15,7 +15,8 @@ import java.io.PrintWriter import java.io.StringWriter import java.text.DateFormat import java.text.SimpleDateFormat -import java.util.* +import java.util.Date +import java.util.Locale object CrashUtils { private const val TAG = "CrashUtils" diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/photopager/PhotoPagerActivity.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/photopager/PhotoPagerActivity.kt index 330588c8e..e38e23e22 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/photopager/PhotoPagerActivity.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/photopager/PhotoPagerActivity.kt @@ -59,7 +59,6 @@ import dev.ragnarok.fenrir.view.natives.rlottie.RLottieImageView import dev.ragnarok.fenrir.view.pager.WeakPicassoLoadCallback import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.disposables.Disposable -import java.util.* import java.util.concurrent.TimeUnit class PhotoPagerActivity : BaseMvpActivity(), IPhotoPagerView, @@ -569,7 +568,7 @@ class PhotoPagerActivity : BaseMvpActivity override fun displayPhotos(photos: List, initialIndex: Int) { if (bShowPhotosLine) { if (photos.size <= 1) { - mAdapterRecycler.setData(Collections.emptyList()) + mAdapterRecycler.setData(emptyList()) mAdapterRecycler.notifyDataSetChanged() } else { mAdapterRecycler.setData(photos) diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/qr/CameraScanActivity.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/qr/CameraScanActivity.kt index 4f4d7ab5f..7292ab63b 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/qr/CameraScanActivity.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/qr/CameraScanActivity.kt @@ -32,7 +32,8 @@ import dev.ragnarok.fenrir.util.AppPerms import dev.ragnarok.fenrir.util.AppPerms.requestPermissionsResultAbs import dev.ragnarok.fenrir.util.Utils import java.nio.ByteBuffer -import java.util.* +import java.util.EnumMap +import java.util.EnumSet class CameraScanActivity : NoMainActivity() { private lateinit var textureView: PreviewView diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/qr/CustomQRCodeWriter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/qr/CustomQRCodeWriter.kt index a52d90812..5f45c4073 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/qr/CustomQRCodeWriter.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/qr/CustomQRCodeWriter.kt @@ -10,7 +10,7 @@ import com.google.zxing.WriterException import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel import com.google.zxing.qrcode.encoder.ByteMatrix import com.google.zxing.qrcode.encoder.Encoder -import java.util.* +import java.util.Arrays import kotlin.math.roundToInt class CustomQRCodeWriter { diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/selectprofiles/SelectedProfilesAdapter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/selectprofiles/SelectedProfilesAdapter.kt index 3e86c8e83..2317fb717 100644 --- a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/selectprofiles/SelectedProfilesAdapter.kt +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/selectprofiles/SelectedProfilesAdapter.kt @@ -14,7 +14,6 @@ import dev.ragnarok.fenrir.model.Owner import dev.ragnarok.fenrir.model.User import dev.ragnarok.fenrir.picasso.PicassoInstance.Companion.with import dev.ragnarok.fenrir.settings.CurrentTheme -import java.util.EventListener class SelectedProfilesAdapter(private val mContext: Context, private val mData: List) : RecyclerView.Adapter() { @@ -99,7 +98,7 @@ class SelectedProfilesAdapter(private val mContext: Context, private val mData: notifyItemChanged(0) } - interface ActionListener : EventListener { + interface ActionListener { fun onClick(adapterPosition: Int, owner: Owner) fun onCheckClick() } diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/shortvideopager/IShortVideoPagerView.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/shortvideopager/IShortVideoPagerView.kt new file mode 100644 index 000000000..c294aaf2d --- /dev/null +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/shortvideopager/IShortVideoPagerView.kt @@ -0,0 +1,33 @@ +package dev.ragnarok.fenrir.activity.shortvideopager + +import androidx.annotation.StringRes +import dev.ragnarok.fenrir.fragment.base.core.IErrorView +import dev.ragnarok.fenrir.fragment.base.core.IMvpView +import dev.ragnarok.fenrir.fragment.base.core.IToastView +import dev.ragnarok.fenrir.media.story.IStoryPlayer +import dev.ragnarok.fenrir.model.Commented +import dev.ragnarok.fenrir.model.Video + +interface IShortVideoPagerView : IMvpView, IErrorView, IToastView { + fun displayData(pageCount: Int, selectedIndex: Int) + fun setAspectRatioAt(position: Int, w: Int, h: Int) + fun setPreparingProgressVisible(position: Int, preparing: Boolean) + fun attachDisplayToPlayer(adapterPosition: Int, storyPlayer: IStoryPlayer?) + fun setToolbarTitle(@StringRes titleRes: Int, vararg params: Any?) + fun setToolbarSubtitle(shortVideo: Video, account_id: Long) + fun onShare(shortVideo: Video, account_id: Long) + fun configHolder(adapterPosition: Int, progress: Boolean, aspectRatioW: Int, aspectRatioH: Int) + fun onNext() + fun requestWriteExternalStoragePermission() + fun showMessage(@StringRes message: Int, error: Boolean) + fun showMessage(message: String, error: Boolean) + fun updateCount(count: Int) + fun notifyDataSetChanged() + fun notifyDataAdded(pos: Int, count: Int) + fun displayListLoading(loading: Boolean) + + fun displayLikes(count: Int, userLikes: Boolean) + fun displayCommentCount(count: Int) + fun showComments(accountId: Long, commented: Commented) + fun goToLikes(accountId: Long, type: String, ownerId: Long, id: Int) +} diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/shortvideopager/ShortVideoPagerActivity.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/shortvideopager/ShortVideoPagerActivity.kt new file mode 100644 index 000000000..4793739af --- /dev/null +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/shortvideopager/ShortVideoPagerActivity.kt @@ -0,0 +1,589 @@ +package dev.ragnarok.fenrir.activity.shortvideopager + +import android.Manifest +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.graphics.Color +import android.os.Bundle +import android.util.SparseArray +import android.view.* +import android.widget.ImageView +import android.widget.RelativeLayout +import android.widget.TextView +import androidx.annotation.ColorInt +import androidx.annotation.LayoutRes +import androidx.annotation.StringRes +import androidx.appcompat.widget.Toolbar +import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 +import com.google.android.material.snackbar.Snackbar +import com.squareup.picasso3.Transformation +import dev.ragnarok.fenrir.* +import dev.ragnarok.fenrir.activity.ActivityFeatures +import dev.ragnarok.fenrir.activity.BaseMvpActivity +import dev.ragnarok.fenrir.activity.SendAttachmentsActivity +import dev.ragnarok.fenrir.activity.slidr.Slidr +import dev.ragnarok.fenrir.activity.slidr.model.SlidrConfig +import dev.ragnarok.fenrir.activity.slidr.model.SlidrListener +import dev.ragnarok.fenrir.activity.slidr.model.SlidrPosition +import dev.ragnarok.fenrir.domain.ILikesInteractor +import dev.ragnarok.fenrir.fragment.audio.AudioPlayerFragment +import dev.ragnarok.fenrir.fragment.base.core.IPresenterFactory +import dev.ragnarok.fenrir.listener.AppStyleable +import dev.ragnarok.fenrir.media.story.IStoryPlayer +import dev.ragnarok.fenrir.model.Commented +import dev.ragnarok.fenrir.model.Video +import dev.ragnarok.fenrir.place.Place +import dev.ragnarok.fenrir.place.PlaceFactory +import dev.ragnarok.fenrir.place.PlaceProvider +import dev.ragnarok.fenrir.settings.CurrentTheme +import dev.ragnarok.fenrir.settings.Settings +import dev.ragnarok.fenrir.util.AppPerms.requestPermissionsAbs +import dev.ragnarok.fenrir.util.AppTextUtils +import dev.ragnarok.fenrir.util.HelperSimple +import dev.ragnarok.fenrir.util.Utils +import dev.ragnarok.fenrir.util.ViewUtils +import dev.ragnarok.fenrir.util.rxutils.RxUtils +import dev.ragnarok.fenrir.util.toast.CustomSnackbars +import dev.ragnarok.fenrir.view.CircleCounterButton +import dev.ragnarok.fenrir.view.ExpandableSurfaceView +import dev.ragnarok.fenrir.view.natives.rlottie.RLottieImageView +import io.reactivex.rxjava3.core.Completable +import io.reactivex.rxjava3.disposables.Disposable +import java.lang.ref.WeakReference +import java.util.concurrent.TimeUnit + +class ShortVideoPagerActivity : BaseMvpActivity(), + IShortVideoPagerView, PlaceProvider, AppStyleable { + private val mHolderSparseArray = SparseArray>() + private var mViewPager: ViewPager2? = null + private var mToolbar: Toolbar? = null + private var mAvatar: ImageView? = null + private var transformation: Transformation? = null + private var mDownload: CircleCounterButton? = null + private var mShare: CircleCounterButton? = null + private var likeButton: CircleCounterButton? = null + private var commentsButton: CircleCounterButton? = null + private var mFullscreen = false + private var mContentRoot: View? = null + private var helpDisposable = Disposable.disposed() + private var mAdapter: Adapter? = null + private var mLoadingProgressBarDispose = Disposable.disposed() + private var mLoadingProgressBarLoaded = false + private var mLoadingProgressBar: RLottieImageView? = null + private var shortVideoLength: TextView? = null + + @LayoutRes + override fun getNoMainContentView(): Int { + return R.layout.activity_shortvideo_pager + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mFullscreen = savedInstanceState?.getBoolean("mFullscreen") ?: false + transformation = CurrentTheme.createTransformationForAvatar() + mContentRoot = findViewById(R.id.shortvideo_pager_root) + mToolbar = findViewById(R.id.toolbar) + setSupportActionBar(mToolbar) + mAvatar = findViewById(R.id.toolbar_avatar) + mViewPager = findViewById(R.id.view_pager) + mViewPager?.offscreenPageLimit = 1 + likeButton = findViewById(R.id.like_button) + commentsButton = findViewById(R.id.comments_button) + shortVideoLength = findViewById(R.id.item_short_video_length) + commentsButton?.setOnClickListener { + presenter?.fireCommentsClick() + } + likeButton?.setOnClickListener { + presenter?.fireLikeClick() + } + likeButton?.setOnLongClickListener { + presenter?.fireLikeLongClick() + true + } + mLoadingProgressBar = findViewById(R.id.loading_progress_bar) + mViewPager?.setPageTransformer( + Utils.createPageTransform( + Settings.get().main().viewpager_page_transform + ) + ) + val mHelper = findViewById(R.id.swipe_helper) + if (HelperSimple.needHelp(HelperSimple.SHORT_VIDEO_HELPER, 2)) { + mHelper?.visibility = View.VISIBLE + mHelper?.fromRes( + dev.ragnarok.fenrir_common.R.raw.story_guide_hand_swipe, + Utils.dp(500F), + Utils.dp(500F), + intArrayOf(0x333333, CurrentTheme.getColorSecondary(this)) + ) + mHelper?.playAnimation() + helpDisposable = Completable.create { + it.onComplete() + }.delay(5, TimeUnit.SECONDS).fromIOToMain().subscribe({ + mHelper?.clearAnimationDrawable() + mHelper?.visibility = View.GONE + }, RxUtils.ignore()) + } else { + mHelper?.visibility = View.GONE + } + mViewPager?.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + presenter?.firePageSelected(position) + } + }) + mDownload = findViewById(R.id.button_download) + mShare = findViewById(R.id.button_share) + mShare?.setOnClickListener { presenter?.fireShareButtonClick() } + mDownload?.setOnClickListener { presenter?.fireDownloadButtonClick() } + resolveFullscreenViews() + val mButtonsRoot: View = findViewById(R.id.buttons) + + Slidr.attach( + this, + SlidrConfig.Builder().setAlphaForView(false).fromUnColoredToColoredStatusBar(true) + .position(SlidrPosition.LEFT) + .listener(object : SlidrListener { + override fun onSlideStateChanged(state: Int) { + + } + + @SuppressLint("Range") + override fun onSlideChange(percent: Float) { + var tmp = 1f - percent + tmp *= 4 + tmp = Utils.clamp(1f - tmp, 0f, 1f) + if (Utils.hasOreo()) { + mContentRoot?.setBackgroundColor(Color.argb(tmp, 0f, 0f, 0f)) + } else { + mContentRoot?.setBackgroundColor( + Color.argb( + (tmp * 255).toInt(), + 0, + 0, + 0 + ) + ) + } + mButtonsRoot.alpha = tmp + mToolbar?.alpha = tmp + mViewPager?.alpha = Utils.clamp(percent, 0f, 1f) + } + + override fun onSlideOpened() { + + } + + override fun onSlideClosed(): Boolean { + finish() + overridePendingTransition(0, 0) + return true + } + + }).build() + ) + } + + override fun displayLikes(count: Int, userLikes: Boolean) { + likeButton?.setIcon(if (userLikes) R.drawable.heart_filled else R.drawable.heart) + likeButton?.count = count + likeButton?.isActive = userLikes + } + + override fun displayCommentCount(count: Int) { + commentsButton?.count = count + } + + override fun showComments(accountId: Long, commented: Commented) { + PlaceFactory.getCommentsPlace(accountId, commented, null).tryOpenWith(this) + } + + override fun goToLikes(accountId: Long, type: String, ownerId: Long, id: Int) { + PlaceFactory.getLikesCopiesPlace( + accountId, + type, + ownerId, + id, + ILikesInteractor.FILTER_LIKES + ) + .tryOpenWith(this) + } + + override fun displayListLoading(loading: Boolean) { + mLoadingProgressBarDispose.dispose() + if (loading) { + mLoadingProgressBarDispose = Completable.create { + it.onComplete() + }.delay(300, TimeUnit.MILLISECONDS).fromIOToMain().subscribe({ + mLoadingProgressBarLoaded = true + mLoadingProgressBar?.visibility = View.VISIBLE + mLoadingProgressBar?.fromRes( + dev.ragnarok.fenrir_common.R.raw.loading, + Utils.dp(100F), + Utils.dp(100F), + intArrayOf( + 0x000000, + Color.WHITE, + 0x777777, + Color.WHITE + ) + ) + mLoadingProgressBar?.playAnimation() + }, RxUtils.ignore()) + } else if (mLoadingProgressBarLoaded) { + mLoadingProgressBarLoaded = false + mLoadingProgressBar?.visibility = View.GONE + mLoadingProgressBar?.clearAnimationDrawable() + } + } + + override fun showMessage(@StringRes message: Int, error: Boolean) { + CustomSnackbars.createCustomSnackbars(mContentRoot, null, true) + ?.setDurationSnack(Snackbar.LENGTH_LONG)?.let { + if (error) { + it.coloredSnack(message, Color.parseColor("#ff0000")).show() + } else { + it.defaultSnack(message).show() + } + } + } + + override fun showMessage(message: String, error: Boolean) { + CustomSnackbars.createCustomSnackbars(mContentRoot, null, true) + ?.setDurationSnack(Snackbar.LENGTH_LONG)?.let { + if (error) { + it.coloredSnack(message, Color.parseColor("#ff0000")).show() + } else { + it.defaultSnack(message).show() + } + } + } + + override fun updateCount(count: Int) { + mAdapter?.updateCount(count) + } + + override fun notifyDataSetChanged() { + mAdapter?.notifyDataSetChanged() + } + + override fun notifyDataAdded(pos: Int, count: Int) { + mAdapter?.notifyItemRangeInserted(pos, count) + } + + override fun openPlace(place: Place) { + val args = place.safeArguments() + when (place.type) { + Place.PLAYER -> { + val player = supportFragmentManager.findFragmentByTag("audio_player") + if (player is AudioPlayerFragment) player.dismiss() + AudioPlayerFragment.newInstance(args).show(supportFragmentManager, "audio_player") + } + + else -> Utils.openPlaceWithSwipebleActivity(this, place) + } + } + + override fun hideMenu(hide: Boolean) {} + override fun openMenu(open: Boolean) {} + + @Suppress("DEPRECATION") + override fun setStatusbarColored(colored: Boolean, invertIcons: Boolean) { + val statusbarNonColored = CurrentTheme.getStatusBarNonColored(this) + val statusbarColored = CurrentTheme.getStatusBarColor(this) + val w = window + w.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) + w.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) + w.statusBarColor = if (colored) statusbarColored else statusbarNonColored + @ColorInt val navigationColor = + if (colored) CurrentTheme.getNavigationBarColor(this) else Color.BLACK + w.navigationBarColor = navigationColor + if (Utils.hasMarshmallow()) { + var flags = window.decorView.systemUiVisibility + flags = if (invertIcons) { + flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR + } else { + flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() + } + window.decorView.systemUiVisibility = flags + } + if (Utils.hasOreo()) { + var flags = window.decorView.systemUiVisibility + if (invertIcons) { + flags = flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR + w.decorView.systemUiVisibility = flags + w.navigationBarColor = Color.WHITE + } else { + flags = flags and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv() + w.decorView.systemUiVisibility = flags + } + } + } + + private val requestWritePermission = requestPermissionsAbs( + arrayOf( + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_EXTERNAL_STORAGE + ) + ) { + lazyPresenter { fireWritePermissionResolved() } + } + + override fun requestWriteExternalStoragePermission() { + requestWritePermission.launch() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putBoolean("mFullscreen", mFullscreen) + } + + override fun onResume() { + super.onResume() + ActivityFeatures.Builder() + .begin() + .setHideNavigationMenu(true) + .setBarsColored(colored = false, invertIcons = false) + .build() + .apply(this) + } + + internal fun toggleFullscreen() { + mFullscreen = !mFullscreen + resolveFullscreenViews() + } + + private fun resolveFullscreenViews() { + mToolbar?.visibility = if (mFullscreen) View.GONE else View.VISIBLE + mDownload?.visibility = if (mFullscreen) View.GONE else View.VISIBLE + mShare?.visibility = if (mFullscreen) View.GONE else View.VISIBLE + likeButton?.visibility = if (mFullscreen) View.GONE else View.VISIBLE + commentsButton?.visibility = if (mFullscreen) View.GONE else View.VISIBLE + } + + override fun getPresenterFactory(saveInstanceState: Bundle?): IPresenterFactory = + object : IPresenterFactory { + override fun create(): ShortVideoPagerPresenter { + val aid = requireArguments().getLong(Extra.ACCOUNT_ID) + val ownerId: Long? = if (!requireArguments().getBoolean(Extra.NO_OWNER_ID)) { + requireArguments().getLong(Extra.OWNER_ID) + } else { + null + } + return ShortVideoPagerPresenter( + aid, + ownerId, + this@ShortVideoPagerActivity, + saveInstanceState + ) + } + } + + override fun displayData(pageCount: Int, selectedIndex: Int) { + mAdapter = Adapter(pageCount) + mViewPager?.adapter = mAdapter + mViewPager?.setCurrentItem(selectedIndex, false) + } + + override fun setAspectRatioAt(position: Int, w: Int, h: Int) { + findByPosition(position)?.setAspectRatio(w, h) + } + + override fun setPreparingProgressVisible(position: Int, preparing: Boolean) { + for (i in 0 until mHolderSparseArray.size()) { + val key = mHolderSparseArray.keyAt(i) + val holder = findByPosition(key) + val isCurrent = position == key + val progressVisible = isCurrent && preparing + holder?.setProgressVisible(progressVisible) + holder?.setSurfaceVisible(if (isCurrent && !preparing) View.VISIBLE else View.GONE) + } + } + + override fun attachDisplayToPlayer(adapterPosition: Int, storyPlayer: IStoryPlayer?) { + val holder = findByPosition(adapterPosition) + if (holder?.isSurfaceReady == true) { + storyPlayer?.setDisplay(holder.mSurfaceHolder) + } + } + + override fun setToolbarTitle(@StringRes titleRes: Int, vararg params: Any?) { + supportActionBar?.title = getString(titleRes, *params) + } + + override fun setToolbarSubtitle(shortVideo: Video, account_id: Long) { + supportActionBar?.subtitle = shortVideo.optionalOwner?.fullName + mAvatar?.setOnClickListener { + shortVideo.optionalOwner?.let { it1 -> + PlaceFactory.getOwnerWallPlace(account_id, it1) + .tryOpenWith(this) + } + } + mAvatar?.let { + ViewUtils.displayAvatar( + it, + transformation, + shortVideo.optionalOwner?.maxSquareAvatar, + Constants.PICASSO_TAG + ) + } + shortVideoLength?.text = AppTextUtils.getDurationString(shortVideo.duration) + displayLikes(shortVideo.likesCount, shortVideo.isUserLikes) + displayCommentCount(shortVideo.commentsCount) + } + + override fun onShare(shortVideo: Video, account_id: Long) { + SendAttachmentsActivity.startForSendAttachments(this, account_id, shortVideo) + } + + override fun configHolder( + adapterPosition: Int, + progress: Boolean, + aspectRatioW: Int, + aspectRatioH: Int + ) { + val holder = findByPosition(adapterPosition) + holder?.setProgressVisible(progress) + holder?.setAspectRatio(aspectRatioW, aspectRatioH) + holder?.setSurfaceVisible(if (progress) View.GONE else View.VISIBLE) + } + + override fun onDestroy() { + super.onDestroy() + helpDisposable.dispose() + mLoadingProgressBarDispose.dispose() + } + + override fun onNext() { + mViewPager?.let { + it.adapter?.let { so -> + if (so.itemCount > it.currentItem + 1) { + it.setCurrentItem(it.currentItem + 1, true) + } + } + } + } + + internal fun fireHolderCreate(holder: Holder) { + presenter?.fireHolderCreate(holder.bindingAdapterPosition) + } + + private fun findByPosition(position: Int): Holder? { + val weak = mHolderSparseArray[position] + return weak?.get() + } + + inner class Holder(rootView: View) : RecyclerView.ViewHolder(rootView), SurfaceHolder.Callback { + val mSurfaceView: ExpandableSurfaceView = rootView.findViewById(R.id.videoSurface) + val mSurfaceHolder: SurfaceHolder = mSurfaceView.holder + val mProgressBar: RLottieImageView + var isSurfaceReady = false + override fun surfaceCreated(holder: SurfaceHolder) { + isSurfaceReady = true + presenter?.fireSurfaceCreated(bindingAdapterPosition) + } + + override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {} + override fun surfaceDestroyed(holder: SurfaceHolder) { + isSurfaceReady = false + } + + fun setProgressVisible(visible: Boolean) { + mProgressBar.visibility = if (visible) View.VISIBLE else View.GONE + if (visible) { + mProgressBar.fromRes( + dev.ragnarok.fenrir_common.R.raw.loading, + Utils.dp(100F), + Utils.dp(100F), + intArrayOf( + 0x000000, + CurrentTheme.getColorPrimary(this@ShortVideoPagerActivity), + 0x777777, + CurrentTheme.getColorSecondary(this@ShortVideoPagerActivity) + ) + ) + mProgressBar.playAnimation() + } else { + mProgressBar.clearAnimationDrawable() + } + } + + fun setAspectRatio(w: Int, h: Int) { + mSurfaceView.setAspectRatio(w, h) + } + + fun setSurfaceVisible(Vis: Int) { + mSurfaceView.visibility = Vis + } + + init { + mSurfaceHolder.addCallback(this) + mProgressBar = rootView.findViewById(R.id.preparing_progress_bar) + mSurfaceView.setOnClickListener { toggleFullscreen() } + } + } + + private inner class Adapter(private var mPageCount: Int) : RecyclerView.Adapter() { + @SuppressLint("ClickableViewAccessibility") + override fun onCreateViewHolder(container: ViewGroup, viewType: Int): Holder { + return Holder( + LayoutInflater.from(container.context) + .inflate(R.layout.content_shortvideo_page, container, false) + ) + } + + override fun onBindViewHolder(holder: Holder, position: Int) { + + } + + fun updateCount(count: Int) { + mPageCount = count + } + + override fun getItemCount(): Int { + return mPageCount + } + + override fun onViewDetachedFromWindow(holder: Holder) { + super.onViewDetachedFromWindow(holder) + mHolderSparseArray.remove(holder.bindingAdapterPosition) + } + + override fun onViewAttachedToWindow(holder: Holder) { + super.onViewAttachedToWindow(holder) + mHolderSparseArray.put(holder.bindingAdapterPosition, WeakReference(holder)) + fireHolderCreate(holder) + } + + init { + mHolderSparseArray.clear() + } + } + + companion object { + const val ACTION_OPEN = + "dev.ragnarok.fenrir.activity.shortvideopager.ShortVideoPagerActivity" + + fun newInstance(context: Context, args: Bundle?): Intent { + val ph = Intent(context, ShortVideoPagerActivity::class.java) + val targetArgs = Bundle() + targetArgs.putAll(args) + ph.action = ACTION_OPEN + ph.putExtras(targetArgs) + return ph + } + + fun buildArgs(aid: Long, ownerId: Long?): Bundle { + val args = Bundle() + args.putLong(Extra.ACCOUNT_ID, aid) + if (ownerId != null) { + args.putBoolean(Extra.NO_OWNER_ID, false) + args.putLong(Extra.OWNER_ID, ownerId) + } else { + args.putBoolean(Extra.NO_OWNER_ID, true) + } + return args + } + } +} diff --git a/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/shortvideopager/ShortVideoPagerPresenter.kt b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/shortvideopager/ShortVideoPagerPresenter.kt new file mode 100644 index 000000000..d6b2af3d9 --- /dev/null +++ b/app_fenrir/src/main/kotlin/dev/ragnarok/fenrir/activity/shortvideopager/ShortVideoPagerPresenter.kt @@ -0,0 +1,393 @@ +package dev.ragnarok.fenrir.activity.shortvideopager + +import android.content.Context +import android.os.Bundle +import dev.ragnarok.fenrir.App.Companion.instance +import dev.ragnarok.fenrir.Includes.storyPlayerFactory +import dev.ragnarok.fenrir.R +import dev.ragnarok.fenrir.domain.IVideosInteractor +import dev.ragnarok.fenrir.domain.InteractorFactory +import dev.ragnarok.fenrir.fragment.base.AccountDependencyPresenter +import dev.ragnarok.fenrir.fromIOToMain +import dev.ragnarok.fenrir.media.story.IStoryPlayer +import dev.ragnarok.fenrir.media.story.IStoryPlayer.IStatusChangeListener +import dev.ragnarok.fenrir.model.Commented +import dev.ragnarok.fenrir.model.Video +import dev.ragnarok.fenrir.model.VideoSize +import dev.ragnarok.fenrir.nonNullNoEmpty +import dev.ragnarok.fenrir.util.AppPerms.hasReadWriteStoragePermission +import dev.ragnarok.fenrir.util.DownloadWorkUtils.doDownloadVideo +import dev.ragnarok.fenrir.util.Pair +import dev.ragnarok.fenrir.util.Utils +import dev.ragnarok.fenrir.util.Utils.firstNonEmptyString + +class ShortVideoPagerPresenter( + accountId: Long, + private val ownerId: Long?, + private val context: Context, + savedInstanceState: Bundle? +) : AccountDependencyPresenter(accountId, savedInstanceState), + IStatusChangeListener, IStoryPlayer.IVideoSizeChangeListener { + private var mShortVideoPlayer: IStoryPlayer? = null + private val mShortVideos: ArrayList