From c8d80a51dd44f38d5c20cb9a2abc0fb3cc590030 Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy <nicolas@videolabs.io> Date: Tue, 14 May 2024 14:50:26 +0200 Subject: [PATCH] Better handling of metered connection warning --- .../videolan/mobile/app/AppSetupDelegate.kt | 24 +++- .../videolan/resources/AppContextProvider.kt | 4 + .../src/org/videolan/vlc/PlaybackService.kt | 19 ++- .../vlc/gui/video/VideoPlayerActivity.kt | 126 ++++++++++++++---- .../org/videolan/vlc/media/PlaylistManager.kt | 3 - 5 files changed, 140 insertions(+), 36 deletions(-) diff --git a/application/app/src/main/java/org/videolan/mobile/app/AppSetupDelegate.kt b/application/app/src/main/java/org/videolan/mobile/app/AppSetupDelegate.kt index 38697d50bc..eaabf7a5fc 100644 --- a/application/app/src/main/java/org/videolan/mobile/app/AppSetupDelegate.kt +++ b/application/app/src/main/java/org/videolan/mobile/app/AppSetupDelegate.kt @@ -20,11 +20,15 @@ package org.videolan.mobile.app import android.annotation.TargetApi +import android.app.Activity +import android.app.Application +import android.app.Application.ActivityLifecycleCallbacks import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.os.Build +import android.os.Bundle import kotlinx.coroutines.DEBUG_PROPERTY_NAME import kotlinx.coroutines.DEBUG_PROPERTY_VALUE_ON import kotlinx.coroutines.Dispatchers @@ -56,7 +60,7 @@ import org.videolan.vlc.widget.MiniPlayerAppWidgetProvider interface AppDelegate { val appContextProvider : AppContextProvider - fun Context.setupApplication() + fun Application.setupApplication() } class AppSetupDelegate : AppDelegate, @@ -67,7 +71,7 @@ class AppSetupDelegate : AppDelegate, override val appContextProvider = AppContextProvider @TargetApi(Build.VERSION_CODES.O) - override fun Context.setupApplication() { + override fun Application.setupApplication() { appContextProvider.init(this) NotificationHelper.createNotificationChannels(this) @@ -92,6 +96,22 @@ class AppSetupDelegate : AppDelegate, backgroundInit() if (Settings.getInstance(this).getBoolean(KEY_ENABLE_REMOTE_ACCESS, false)) startRemoteAccess() + + registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { + override fun onActivityResumed(activity: Activity) { + AppContextProvider.currentActivity = activity + } + + override fun onActivityPaused(activity: Activity) { + AppContextProvider.currentActivity = null + } + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} + override fun onActivityStarted(activity: Activity) {} + override fun onActivityStopped(activity: Activity) {} + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} + override fun onActivityDestroyed(activity: Activity) {} + }) } // init operations executed in background threads diff --git a/application/resources/src/main/java/org/videolan/resources/AppContextProvider.kt b/application/resources/src/main/java/org/videolan/resources/AppContextProvider.kt index b294721ac2..076c0ea4ea 100644 --- a/application/resources/src/main/java/org/videolan/resources/AppContextProvider.kt +++ b/application/resources/src/main/java/org/videolan/resources/AppContextProvider.kt @@ -24,6 +24,7 @@ package org.videolan.resources +import android.app.Activity import android.app.Application import android.content.Context import android.content.ContextWrapper @@ -34,6 +35,9 @@ import java.lang.reflect.InvocationTargetException object AppContextProvider { private lateinit var context: Context + var currentActivity: Activity? = null + + // Property to get the new locale only on restart to prevent change the locale partially on runtime var locale: String? = "" private set diff --git a/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt b/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt index 490610f9b1..8288067251 100644 --- a/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt +++ b/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt @@ -20,6 +20,7 @@ package org.videolan.vlc import android.annotation.TargetApi +import android.app.Activity import android.app.KeyguardManager import android.app.Notification import android.app.NotificationManager @@ -153,6 +154,7 @@ import org.videolan.vlc.gui.dialogs.VideoTracksDialog import org.videolan.vlc.gui.dialogs.adapters.VlcTrack import org.videolan.vlc.gui.helpers.AudioUtil import org.videolan.vlc.gui.helpers.NotificationHelper +import org.videolan.vlc.gui.helpers.UiTools import org.videolan.vlc.gui.helpers.getBitmapFromDrawable import org.videolan.vlc.gui.video.PopupManager import org.videolan.vlc.gui.video.VideoPlayerActivity @@ -766,17 +768,20 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner, CoroutineSc } } - fun checkMetered(it: Boolean) { + private fun checkMetered(metered: Boolean, activity: Activity? = null) { + if (!metered) return val meteredAction = (settings.getString("metered_connection", "0") ?: "0").toInt() - if (isVideoPlaying) { - //delegated to the VideoPlayerActivity - return - } - if (it && meteredAction != 0 && isSchemeStreaming(currentMediaLocation)) { + if (meteredAction != 0 && isSchemeStreaming(currentMediaLocation)) { if (meteredAction == 1) { stop() Toast.makeText(this, R.string.metered_connection_stopped, Toast.LENGTH_LONG).show() - } else Toast.makeText(this, R.string.metered_connection_warning, Toast.LENGTH_LONG).show() + } else { + AppContextProvider.currentActivity?.let { + UiTools.snacker(it, R.string.metered_connection_warning) + } ?: run { + Toast.makeText(this, R.string.metered_connection_warning, Toast.LENGTH_LONG).show() + } + } } } diff --git a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt index ff93c607a3..3fb3813f8e 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt @@ -27,27 +27,50 @@ import android.app.KeyguardManager import android.app.PictureInPictureParams import android.bluetooth.BluetoothA2dp import android.bluetooth.BluetoothHeadset -import android.content.* +import android.content.BroadcastReceiver +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.content.IntentFilter +import android.content.SharedPreferences import android.content.pm.ActivityInfo import android.content.res.Configuration import android.graphics.Bitmap import android.media.AudioManager import android.net.Uri -import android.os.* +import android.os.Build +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.os.Message +import android.os.Parcelable +import android.os.PowerManager import android.text.Editable import android.text.TextWatcher import android.util.DisplayMetrics import android.util.Log import android.util.Rational -import android.view.* +import android.view.Gravity +import android.view.KeyEvent +import android.view.MotionEvent +import android.view.PixelCopy +import android.view.Surface +import android.view.SurfaceView +import android.view.View import android.view.View.OnClickListener import android.view.View.OnLongClickListener +import android.view.WindowManager import android.view.animation.Animation import android.view.animation.AnimationSet import android.view.animation.DecelerateInterpolator import android.view.animation.RotateAnimation -import android.widget.* +import android.widget.CheckBox +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.SeekBar import android.widget.SeekBar.OnSeekBarChangeListener +import android.widget.TextView +import android.widget.Toast import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity @@ -76,9 +99,14 @@ import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import androidx.window.layout.FoldingFeature import androidx.window.layout.WindowInfoTracker import androidx.window.layout.WindowLayoutInfo -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.videolan.libvlc.Dialog import org.videolan.libvlc.MediaPlayer import org.videolan.libvlc.interfaces.IMedia @@ -89,13 +117,64 @@ import org.videolan.medialibrary.MLServiceLocator import org.videolan.medialibrary.Tools import org.videolan.medialibrary.interfaces.Medialibrary import org.videolan.medialibrary.interfaces.media.MediaWrapper -import org.videolan.resources.* +import org.videolan.resources.AndroidDevices +import org.videolan.resources.AppContextProvider +import org.videolan.resources.EXIT_PLAYER +import org.videolan.resources.MOBILE_MAIN_ACTIVITY +import org.videolan.resources.PLAYLIST_TYPE_ALL +import org.videolan.resources.PLAY_DISABLE_HARDWARE +import org.videolan.resources.PLAY_EXTRA_FROM_START +import org.videolan.resources.PLAY_EXTRA_ITEM_LOCATION +import org.videolan.resources.PLAY_EXTRA_ITEM_TITLE +import org.videolan.resources.PLAY_EXTRA_OPENED_POSITION +import org.videolan.resources.PLAY_EXTRA_START_TIME +import org.videolan.resources.PLAY_EXTRA_SUBTITLES_LOCATION +import org.videolan.resources.PLAY_FROM_SERVICE +import org.videolan.resources.PLAY_FROM_VIDEOGRID +import org.videolan.resources.TV_AUDIOPLAYER_ACTIVITY +import org.videolan.resources.buildPkgString import org.videolan.resources.util.parcelable import org.videolan.resources.util.parcelableList -import org.videolan.tools.* -import org.videolan.vlc.* +import org.videolan.tools.AUDIO_BOOST +import org.videolan.tools.AUDIO_PREFERRED_LANGUAGE +import org.videolan.tools.BRIGHTNESS_VALUE +import org.videolan.tools.DISPLAY_UNDER_NOTCH +import org.videolan.tools.ENABLE_BRIGHTNESS_GESTURE +import org.videolan.tools.ENABLE_DOUBLE_TAP_PLAY +import org.videolan.tools.ENABLE_DOUBLE_TAP_SEEK +import org.videolan.tools.ENABLE_SCALE_GESTURE +import org.videolan.tools.ENABLE_SEEK_BUTTONS +import org.videolan.tools.ENABLE_SWIPE_SEEK +import org.videolan.tools.ENABLE_VOLUME_GESTURE +import org.videolan.tools.KEY_VIDEO_APP_SWITCH +import org.videolan.tools.KEY_VIDEO_CONFIRM_RESUME +import org.videolan.tools.LAST_LOCK_ORIENTATION +import org.videolan.tools.LOCK_USE_SENSOR +import org.videolan.tools.POPUP_FORCE_LEGACY +import org.videolan.tools.PREF_TIPS_SHOWN +import org.videolan.tools.SAVE_BRIGHTNESS +import org.videolan.tools.SCREENSHOT_MODE +import org.videolan.tools.SCREEN_ORIENTATION +import org.videolan.tools.SUBTITLE_PREFERRED_LANGUAGE +import org.videolan.tools.Settings +import org.videolan.tools.VIDEO_PAUSED +import org.videolan.tools.VIDEO_RATIO +import org.videolan.tools.VIDEO_RESUME_TIME +import org.videolan.tools.VIDEO_RESUME_URI +import org.videolan.tools.VIDEO_TRANSITION_SHOW +import org.videolan.tools.dp +import org.videolan.tools.getContextWithLocale +import org.videolan.tools.isStarted +import org.videolan.tools.isVisible +import org.videolan.tools.putSingle +import org.videolan.tools.setGone +import org.videolan.tools.setInvisible +import org.videolan.tools.setVisible import org.videolan.vlc.BuildConfig +import org.videolan.vlc.PlaybackService import org.videolan.vlc.R +import org.videolan.vlc.getAllTracks +import org.videolan.vlc.getSelectedVideoTrack import org.videolan.vlc.gui.DialogActivity import org.videolan.vlc.gui.audio.EqualizerFragment import org.videolan.vlc.gui.audio.PlaylistAdapter @@ -104,7 +183,11 @@ import org.videolan.vlc.gui.dialogs.PlaybackSpeedDialog import org.videolan.vlc.gui.dialogs.RenderersDialog import org.videolan.vlc.gui.dialogs.SleepTimerDialog import org.videolan.vlc.gui.dialogs.adapters.VlcTrack -import org.videolan.vlc.gui.helpers.* +import org.videolan.vlc.gui.helpers.BitmapUtil +import org.videolan.vlc.gui.helpers.KeycodeListener +import org.videolan.vlc.gui.helpers.PlayerKeyListenerDelegate +import org.videolan.vlc.gui.helpers.PlayerOptionsDelegate +import org.videolan.vlc.gui.helpers.UiTools import org.videolan.vlc.gui.helpers.UiTools.isTablet import org.videolan.vlc.gui.helpers.UiTools.showPinIfNeeded import org.videolan.vlc.gui.helpers.hf.StoragePermissionsDelegate @@ -115,21 +198,28 @@ import org.videolan.vlc.media.VideoResumeStatus import org.videolan.vlc.media.WaitConfirmation import org.videolan.vlc.repository.ExternalSubRepository import org.videolan.vlc.repository.SlaveRepository -import org.videolan.vlc.util.* +import org.videolan.vlc.util.DialogDelegate import org.videolan.vlc.util.FileUtils import org.videolan.vlc.util.FileUtils.getUri +import org.videolan.vlc.util.FrameRateManager +import org.videolan.vlc.util.IDialogManager +import org.videolan.vlc.util.LocaleUtil import org.videolan.vlc.util.LocaleUtil.localeEquivalent +import org.videolan.vlc.util.Permissions +import org.videolan.vlc.util.Util +import org.videolan.vlc.util.hasNotch +import org.videolan.vlc.util.isTalkbackIsEnabled import org.videolan.vlc.viewmodels.BookmarkModel import org.videolan.vlc.viewmodels.PlaylistModel import java.io.File -import java.lang.Runnable import java.text.SimpleDateFormat -import java.util.* +import java.util.Date import kotlin.math.roundToInt open class VideoPlayerActivity : AppCompatActivity(), PlaybackService.Callback, PlaylistAdapter.IPlayer, OnClickListener, OnLongClickListener, StoragePermissionsDelegate.CustomActionController, TextWatcher, IDialogManager, KeycodeListener { + private var warnMetered = false var hasPhysicalNotch: Boolean = false private var subtitlesExtraPath: String? = null private lateinit var startedScope: CoroutineScope @@ -2309,18 +2399,6 @@ open class VideoPlayerActivity : AppCompatActivity(), PlaybackService.Callback, open fun onServiceChanged(service: PlaybackService?) { if (service != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - NetworkConnectionManager.isMetered.observe(this) { - val meteredAction =(settings.getString("metered_connection", "0") ?: "0").toInt() - if (it && meteredAction != 0 && isSchemeStreaming(service.currentMediaLocation)) { - if (meteredAction == 1) { - stop() - Toast.makeText(this, R.string.metered_connection_stopped, Toast.LENGTH_LONG).show() - finish() - } else UiTools.snacker(this, R.string.metered_connection_warning) - } - } - } this.service = service if (savedMediaList != null && service.currentMediaWrapper == null) { service.append(savedMediaList!!, savedMediaIndex) diff --git a/application/vlc-android/src/org/videolan/vlc/media/PlaylistManager.kt b/application/vlc-android/src/org/videolan/vlc/media/PlaylistManager.kt index f7eb021a6f..5a7609a723 100644 --- a/application/vlc-android/src/org/videolan/vlc/media/PlaylistManager.kt +++ b/application/vlc-android/src/org/videolan/vlc/media/PlaylistManager.kt @@ -268,9 +268,6 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList service.showNotification() } if (settings.getBoolean(KEY_AUDIO_FORCE_SHUFFLE, false) && getCurrentMedia()?.type == MediaWrapper.TYPE_AUDIO && !shuffling && canShuffle()) shuffle() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - service.checkMetered(NetworkConnectionManager.isMetered.value ?: false) - } } @Volatile -- GitLab