diff --git a/app/build.gradle b/app/build.gradle index 9d59b16..98d6261 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -37,8 +37,8 @@ android { applicationId "zechs.drive.stream" minSdk 28 targetSdk 32 - versionCode 4 - versionName "1.0.3" + versionCode 5 + versionName "1.1.0" archivesBaseName = "${applicationId}-${versionCode}" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/zechs/drive/stream/data/remote/DriveHelper.kt b/app/src/main/java/zechs/drive/stream/data/remote/DriveHelper.kt index 905b5ba..7217fd0 100755 --- a/app/src/main/java/zechs/drive/stream/data/remote/DriveHelper.kt +++ b/app/src/main/java/zechs/drive/stream/data/remote/DriveHelper.kt @@ -5,6 +5,9 @@ import android.content.Intent import android.util.Log import com.google.android.gms.auth.GoogleAuthUtil import com.google.android.gms.auth.api.signin.GoogleSignIn +import com.google.android.gms.auth.api.signin.GoogleSignInClient +import com.google.android.gms.auth.api.signin.GoogleSignInOptions +import com.google.android.gms.common.Scopes import com.google.android.gms.common.api.Scope import com.google.api.client.extensions.android.http.AndroidHttp import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential @@ -35,6 +38,17 @@ class DriveHelper @Inject constructor( var hasSignedIn = false private set + + fun getClient(): GoogleSignInClient { + val signInOptions = GoogleSignInOptions + .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) + .requestScopes(Scope(Scopes.DRIVE_FULL)) + .requestEmail() + .build() + + return GoogleSignIn.getClient(context, signInOptions) + } + /** * Public method to send intent result from * Google Sign-in flow diff --git a/app/src/main/java/zechs/drive/stream/ui/home/HomeFragment.kt b/app/src/main/java/zechs/drive/stream/ui/home/HomeFragment.kt index 25c4576..101e8f4 100644 --- a/app/src/main/java/zechs/drive/stream/ui/home/HomeFragment.kt +++ b/app/src/main/java/zechs/drive/stream/ui/home/HomeFragment.kt @@ -5,13 +5,21 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController import com.google.android.material.button.MaterialButton +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import zechs.drive.stream.R import zechs.drive.stream.databinding.FragmentHomeBinding import zechs.drive.stream.ui.BaseFragment import zechs.drive.stream.utils.ext.navigateSafe + class HomeFragment : BaseFragment() { companion object { @@ -21,6 +29,8 @@ class HomeFragment : BaseFragment() { private var _binding: FragmentHomeBinding? = null private val binding get() = _binding!! + private val viewModel by activityViewModels() + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -75,6 +85,8 @@ class HomeFragment : BaseFragment() { } + setupToolbar() + observeLogOutState() } private fun navigateToFiles( @@ -90,6 +102,41 @@ class HomeFragment : BaseFragment() { } } + private fun setupToolbar() { + binding.toolbar.setOnMenuItemClickListener { item -> + if (item.itemId == R.id.action_logOut) { + MaterialAlertDialogBuilder(context!!) + .setTitle(getString(R.string.log_out_dialog_title)) + .setNegativeButton(getString(R.string.no)) { dialog, _ -> + dialog.dismiss() + } + .setPositiveButton(getString(R.string.yes)) { dialog, _ -> + dialog.dismiss() + Log.d(TAG, "Logging out...") + viewModel.logOut() + } + .show() + return@setOnMenuItemClickListener true + } + return@setOnMenuItemClickListener false + } + } + + private fun observeLogOutState() { + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.hasLoggedOut.collect { + if (it) { + // restart activity + activity!!.finish() + delay(250L) + activity!!.startActivity(activity!!.intent) + } + } + } + } + } + override fun onDestroyView() { super.onDestroyView() _binding = null diff --git a/app/src/main/java/zechs/drive/stream/ui/home/HomeViewModel.kt b/app/src/main/java/zechs/drive/stream/ui/home/HomeViewModel.kt new file mode 100644 index 0000000..26dccce --- /dev/null +++ b/app/src/main/java/zechs/drive/stream/ui/home/HomeViewModel.kt @@ -0,0 +1,29 @@ +package zechs.drive.stream.ui.home + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import zechs.drive.stream.data.remote.DriveHelper +import javax.inject.Inject + +@HiltViewModel +class HomeViewModel @Inject constructor( + private val driveHelper: DriveHelper +) : ViewModel() { + + private val _hasLoggedOut = MutableStateFlow(false) + val hasLoggedOut = _hasLoggedOut.asStateFlow() + + fun logOut() = viewModelScope.launch { + delay(250L) + val client = driveHelper.getClient() + // client.revokeAccess() + client.signOut() + _hasLoggedOut.value = true + } + +} diff --git a/app/src/main/java/zechs/drive/stream/ui/main/MainActivity.kt b/app/src/main/java/zechs/drive/stream/ui/main/MainActivity.kt index f2f80fe..28a2699 100644 --- a/app/src/main/java/zechs/drive/stream/ui/main/MainActivity.kt +++ b/app/src/main/java/zechs/drive/stream/ui/main/MainActivity.kt @@ -12,10 +12,6 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment -import com.google.android.gms.auth.api.signin.GoogleSignIn -import com.google.android.gms.auth.api.signin.GoogleSignInOptions -import com.google.android.gms.common.Scopes -import com.google.android.gms.common.api.Scope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import zechs.drive.stream.R @@ -69,13 +65,7 @@ class MainActivity : AppCompatActivity() { private fun requestSignIn() { Log.d(TAG, "Requesting sign-in") - val signInOptions = GoogleSignInOptions - .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) - .requestScopes(Scope(Scopes.DRIVE_FULL)) - .requestEmail() - .build() - - val client = GoogleSignIn.getClient(this, signInOptions) + val client = viewModel.getClient() val startForResult = registerForActivityResult( ActivityResultContracts.StartActivityForResult() diff --git a/app/src/main/java/zechs/drive/stream/ui/main/MainViewModel.kt b/app/src/main/java/zechs/drive/stream/ui/main/MainViewModel.kt index cabf246..09de927 100755 --- a/app/src/main/java/zechs/drive/stream/ui/main/MainViewModel.kt +++ b/app/src/main/java/zechs/drive/stream/ui/main/MainViewModel.kt @@ -47,4 +47,5 @@ class MainViewModel @Inject constructor( Log.d(TAG, msg) } + fun getClient() = driveHelper.getClient() } diff --git a/app/src/main/java/zechs/drive/stream/ui/player/PlayerActivity.kt b/app/src/main/java/zechs/drive/stream/ui/player/PlayerActivity.kt index 23ae971..7769a2c 100644 --- a/app/src/main/java/zechs/drive/stream/ui/player/PlayerActivity.kt +++ b/app/src/main/java/zechs/drive/stream/ui/player/PlayerActivity.kt @@ -7,7 +7,9 @@ import android.content.res.Configuration import android.net.Uri import android.os.Bundle import android.util.Log +import android.view.View import android.view.animation.AccelerateInterpolator +import android.widget.HorizontalScrollView import android.widget.LinearLayout import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat @@ -69,6 +71,7 @@ class PlayerActivity : AppCompatActivity() { // Player views private lateinit var mainControlsRoot: LinearLayout + private lateinit var controlsScrollView: HorizontalScrollView private lateinit var progressViewGroup: LinearLayout private lateinit var toolbar: MaterialToolbar private lateinit var btnPlay: MaterialButton @@ -80,6 +83,8 @@ class PlayerActivity : AppCompatActivity() { private lateinit var btnPip: MaterialButton private lateinit var btnSpeed: MaterialButton private lateinit var btnRotate: MaterialButton + private lateinit var btnLock: MaterialButton + private lateinit var btnUnlock: MaterialButton // Dialogs private var audioDialog: Dialog? = null @@ -88,6 +93,7 @@ class PlayerActivity : AppCompatActivity() { // States private var onStopCalled = false + private var controlsLocked = false // Configs private var speed = arrayOf("0.25x", "0.5x", "Normal", "1.25x", "1.5x", "2x") @@ -105,6 +111,7 @@ class PlayerActivity : AppCompatActivity() { playerView = binding.playerView mainControlsRoot = playerView.findViewById(R.id.mainControls) + controlsScrollView = playerView.findViewById(R.id.controlsScrollView) progressViewGroup = playerView.findViewById(R.id.linearLayout2) toolbar = playerView.findViewById(R.id.playerToolbar) btnPlay = playerView.findViewById(R.id.btnPlay) @@ -116,6 +123,8 @@ class PlayerActivity : AppCompatActivity() { btnPip = playerView.findViewById(R.id.btnPip) btnSpeed = playerView.findViewById(R.id.btnSpeed) btnRotate = playerView.findViewById(R.id.btnRotate) + btnLock = playerView.findViewById(R.id.btnLock) + btnUnlock = playerView.findViewById(R.id.btnUnlock) // Back button toolbar.setNavigationOnClickListener { @@ -194,6 +203,16 @@ class PlayerActivity : AppCompatActivity() { setOrientation(this@PlayerActivity, orientation) } + btnLock.setOnClickListener { + controlsLocked = true + handleLockingControls() + } + + btnUnlock.setOnClickListener { + controlsLocked = false + handleLockingControls() + } + updateOrientation(resources.configuration) initPlayer() playMedia() @@ -325,6 +344,40 @@ class PlayerActivity : AppCompatActivity() { .setSeekForwardIncrementMs(10_000) .setSeekBackIncrementMs(10_000) .build() + + playerView.setControllerVisibilityListener { + if (it == View.VISIBLE) { + handleLockingControls() + } + } + + } + + private fun handleLockingControls() { + TransitionManager.beginDelayedTransition( + playerView, AutoTransition().apply { + duration = 150L + } + ) + if (controlsLocked) { + lockControls() + } else { + unlockControls() + } + } + + private fun lockControls() { + controlsScrollView.visibility = View.GONE + mainControlsRoot.visibility = View.GONE + btnUnlock.visibility = View.VISIBLE + toolbar.visibility = View.GONE + } + + private fun unlockControls() { + btnUnlock.visibility = View.GONE + toolbar.visibility = View.VISIBLE + controlsScrollView.visibility = View.VISIBLE + mainControlsRoot.visibility = View.VISIBLE } private fun releasePlayer() { diff --git a/app/src/main/java/zechs/drive/stream/ui/player/utils/Orientation.kt b/app/src/main/java/zechs/drive/stream/ui/player/utils/Orientation.kt index e542ffb..35e5f1e 100644 --- a/app/src/main/java/zechs/drive/stream/ui/player/utils/Orientation.kt +++ b/app/src/main/java/zechs/drive/stream/ui/player/utils/Orientation.kt @@ -12,8 +12,8 @@ enum class Orientation( fun setOrientation(activity: Activity, orientation: Orientation?) { activity.requestedOrientation = when (orientation) { - Orientation.LANDSCAPE -> SCREEN_ORIENTATION_LANDSCAPE - Orientation.PORTRAIT -> SCREEN_ORIENTATION_PORTRAIT + Orientation.LANDSCAPE -> SCREEN_ORIENTATION_SENSOR_LANDSCAPE + Orientation.PORTRAIT -> SCREEN_ORIENTATION_SENSOR_PORTRAIT else -> SCREEN_ORIENTATION_FULL_SENSOR } } diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9..0000000 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..d306148 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_lock_24.xml b/app/src/main/res/drawable/ic_lock_24.xml new file mode 100644 index 0000000..87281bf --- /dev/null +++ b/app/src/main/res/drawable/ic_lock_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_logout_24.xml b/app/src/main/res/drawable/ic_logout_24.xml new file mode 100644 index 0000000..7b5f586 --- /dev/null +++ b/app/src/main/res/drawable/ic_logout_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_unlock_24.xml b/app/src/main/res/drawable/ic_unlock_24.xml new file mode 100644 index 0000000..5d19d4f --- /dev/null +++ b/app/src/main/res/drawable/ic_unlock_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index a185562..7feac63 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -20,6 +20,7 @@ style="@style/MenuTheme" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + app:menu="@menu/main_menu" app:title="@string/app_name" /> diff --git a/app/src/main/res/layout/player_control_view.xml b/app/src/main/res/layout/player_control_view.xml index b462d91..47a512c 100644 --- a/app/src/main/res/layout/player_control_view.xml +++ b/app/src/main/res/layout/player_control_view.xml @@ -26,6 +26,29 @@ tools:subtitle="video metadata" tools:title="Video title" /> + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index eca70cf..7353dbd 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index eca70cf..7353dbd 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..91bc8c6 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index c209e78..0000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..8c5d104 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index b2dfe3d..0000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..a56ba0e Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 4f0f1d6..0000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..f534365 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index 62b611d..0000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..2305f67 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 948a307..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..afb4450 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 1b9a695..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..0154c3b Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 28d4b77..0000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..5d210ce Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9287f50..0000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..004af19 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index aa7d642..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..fb7ae98 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9126ae3..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..c5d5899 --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #FFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 51f0cc5..a209945 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -23,4 +23,10 @@ OK Details Retry + Lock + Unlock + Logout + Are you sure you want to log out? + No + Yes \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 2ae2de4..5f37909 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -57,4 +57,8 @@ @color/textColor_87 + + \ No newline at end of file