diff --git a/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt b/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt index e9f5c355..514b66d4 100644 --- a/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt +++ b/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt @@ -52,6 +52,7 @@ import com.futo.platformplayer.fragment.mainactivity.main.CommentsFragment import com.futo.platformplayer.fragment.mainactivity.main.ContentSearchResultsFragment import com.futo.platformplayer.fragment.mainactivity.main.CreatorSearchResultsFragment import com.futo.platformplayer.fragment.mainactivity.main.CreatorsFragment +import com.futo.platformplayer.fragment.mainactivity.main.DeveloperFragment import com.futo.platformplayer.fragment.mainactivity.main.DownloadsFragment import com.futo.platformplayer.fragment.mainactivity.main.HistoryFragment import com.futo.platformplayer.fragment.mainactivity.main.HomeFragment @@ -71,6 +72,7 @@ import com.futo.platformplayer.fragment.mainactivity.main.PlaylistSearchResultsF import com.futo.platformplayer.fragment.mainactivity.main.PlaylistsFragment import com.futo.platformplayer.fragment.mainactivity.main.PostDetailFragment import com.futo.platformplayer.fragment.mainactivity.main.RemotePlaylistFragment +import com.futo.platformplayer.fragment.mainactivity.main.SettingsFragment import com.futo.platformplayer.fragment.mainactivity.main.ShortsFragment import com.futo.platformplayer.fragment.mainactivity.main.SourceDetailFragment import com.futo.platformplayer.fragment.mainactivity.main.SourcesFragment @@ -197,6 +199,8 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { lateinit var _fragLibraryVideos: LibraryVideosFragment; lateinit var _fragLibrarySearch: LibrarySearchFragment; lateinit var _fragLibraryFiles: LibraryFilesFragment; + lateinit var _fragSettings: SettingsFragment; + lateinit var _fragDeveloper: DeveloperFragment; lateinit var _fragBrowser: BrowserFragment; @@ -238,6 +242,17 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { } } } + private val _notifPermission = "android.permission.POST_NOTIFICATIONS"; + private val _notificationPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> + if (isGranted) + UIDialogs.toast(this, "Notification permission granted"); + else + UIDialogs.toast(this, "Notification permission denied"); + }; + + fun requestNotificationPermissions() { + _notificationPermissionLauncher?.launch(_notifPermission); + } val mainId = UUID.randomUUID().toString().substring(0, 5) @@ -377,6 +392,8 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { _fragLibraryVideos = LibraryVideosFragment.newInstance(); _fragLibraryFiles = LibraryFilesFragment.newInstance(); _fragLibrarySearch = LibrarySearchFragment.newInstance(); + _fragSettings = SettingsFragment.newInstance(); + _fragDeveloper = DeveloperFragment.newInstance(); _fragBrowser = BrowserFragment.newInstance(); @@ -516,6 +533,8 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { _fragLibraryVideos.topBar = _fragTopBarNavigation; _fragLibraryFiles.topBar = _fragTopBarFiles; _fragLibrarySearch.topBar = _fragTopBarSearch; + _fragSettings.topBar = _fragTopBarNavigation; + _fragDeveloper.topBar = _fragTopBarNavigation; _fragBrowser.topBar = _fragTopBarNavigation; @@ -1324,6 +1343,8 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { LibraryVideosFragment::class -> _fragLibraryVideos as T; LibraryFilesFragment::class -> _fragLibraryFiles as T; LibrarySearchFragment::class -> _fragLibrarySearch as T; + SettingsFragment:: class -> _fragSettings as T; + DeveloperFragment::class -> _fragDeveloper as T; else -> throw IllegalArgumentException("Fragment type ${T::class.java.name} is not available in MainActivity"); } } diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/bottombar/MenuBottomBarFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/bottombar/MenuBottomBarFragment.kt index 98560d3d..b26988bd 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/bottombar/MenuBottomBarFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/bottombar/MenuBottomBarFragment.kt @@ -400,6 +400,8 @@ class MenuBottomBarFragment : MainActivityFragment() { ButtonDefinition(9, R.drawable.ic_subscriptions, R.drawable.ic_subscriptions_filled, R.string.subscription_group_menu, canToggle = true, { it.currentMain is SubscriptionGroupListFragment }, { it.navigate(withHistory = false) }), ButtonDefinition(10, R.drawable.ic_help_square, R.drawable.ic_help_square_fill, R.string.tutorials, canToggle = true, { it.currentMain is TutorialFragment }, { it.navigate(withHistory = false) }), ButtonDefinition(7, R.drawable.ic_settings, R.drawable.ic_settings_filled, R.string.settings, canToggle = false, { false }, { + it.navigate(); + /* val c = it.context ?: return@ButtonDefinition; Logger.i(TAG, "settings preventPictureInPicture()"); it.requireFragment().preventPictureInPicture(); @@ -407,7 +409,7 @@ class MenuBottomBarFragment : MainActivityFragment() { c.startActivity(intent); if (c is Activity) { c.overridePendingTransition(R.anim.slide_in_up, R.anim.slide_darken); - } + }*/ }), ButtonDefinition(96, R.drawable.ic_disabled_visible, R.drawable.ic_disabled_visible, R.string.privacy_mode, canToggle = true, { false }, { UIDialogs.showDialog(it.context ?: return@ButtonDefinition, R.drawable.ic_disabled_visible_purple, "Privacy Mode", diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/DeveloperFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/DeveloperFragment.kt new file mode 100644 index 00000000..29b17301 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/DeveloperFragment.kt @@ -0,0 +1,129 @@ +package com.futo.platformplayer.fragment.mainactivity.main + +import android.app.NotificationManager +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Bundle +import android.provider.MediaStore +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageButton +import android.widget.LinearLayout +import android.widget.TextView +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.RecyclerView +import com.futo.platformplayer.R +import com.futo.platformplayer.Settings +import com.futo.platformplayer.SettingsDev +import com.futo.platformplayer.UIDialogs +import com.futo.platformplayer.activities.DeveloperActivity +import com.futo.platformplayer.activities.MainActivity +import com.futo.platformplayer.api.media.models.contents.IPlatformContent +import com.futo.platformplayer.api.media.models.video.IPlatformVideo +import com.futo.platformplayer.assume +import com.futo.platformplayer.dp +import com.futo.platformplayer.logging.Logger +import com.futo.platformplayer.states.Album +import com.futo.platformplayer.states.Artist +import com.futo.platformplayer.states.ArtistOrdering +import com.futo.platformplayer.states.FileEntry +import com.futo.platformplayer.states.StateApp +import com.futo.platformplayer.states.StateLibrary +import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny +import com.futo.platformplayer.views.AnyInsertedAdapterView +import com.futo.platformplayer.views.AnyInsertedAdapterView.Companion.asAnyWithTop +import com.futo.platformplayer.views.AnyInsertedAdapterView.Companion.asAnyWithViews +import com.futo.platformplayer.views.LibrarySection +import com.futo.platformplayer.views.LoaderView +import com.futo.platformplayer.views.adapters.AnyAdapter +import com.futo.platformplayer.views.adapters.InsertedViewAdapter +import com.futo.platformplayer.views.adapters.viewholders.AlbumTileViewHolder +import com.futo.platformplayer.views.adapters.viewholders.ArtistTileViewHolder +import com.futo.platformplayer.views.adapters.viewholders.FileViewHolder +import com.futo.platformplayer.views.adapters.viewholders.LocalVideoTileViewHolder +import com.futo.platformplayer.views.buttons.BigButton +import com.futo.platformplayer.views.fields.FieldForm +import com.futo.platformplayer.views.fields.IField +import com.futo.platformplayer.views.fields.ReadOnlyTextField +import com.google.android.material.button.MaterialButton + + +class DeveloperFragment : MainFragment() { + override val isMainView : Boolean = true; + override val isTab: Boolean = true; + override val hasBottomBar: Boolean get() = true; + + private var view: FragView? = null; + + + override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + val newView = FragView(this); + view = newView; + return newView; + } + + override fun onShownWithView(parameter: Any?, isBack: Boolean) { + super.onShownWithView(parameter, isBack); + view?.onShown(); + } + + override fun onDestroyMainView() { + view = null; + super.onDestroyMainView(); + } + + companion object { + fun newInstance() = DeveloperFragment().apply {} + } + + + class FragView: ConstraintLayout { + val fragment: DeveloperFragment; + + private lateinit var _form: FieldForm; + private lateinit var _buttonBack: ImageButton; + + private var _isFinished = false; + + lateinit var overlay: FrameLayout; + + val notifPermission = "android.permission.POST_NOTIFICATIONS"; + + constructor(fragment: DeveloperFragment) : super(fragment.requireContext()) { + inflate(context, R.layout.activity_dev, this); + this.fragment = fragment; + + val activity = fragment.activity; + findViewById(R.id.container_topbar).isVisible = false; + + _buttonBack = findViewById(R.id.button_back); + _form = findViewById(R.id.settings_form); + + _form.fromObject(SettingsDev.instance); + _form.onChanged.subscribe { _, _ -> + _form.setObjectValues(); + SettingsDev.instance.save(); + }; + } + + fun getField(id: String): IField? { + return _form.findField(id); + } + + fun onShown() { + + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SettingsFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SettingsFragment.kt new file mode 100644 index 00000000..eff9dfd9 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SettingsFragment.kt @@ -0,0 +1,200 @@ +package com.futo.platformplayer.fragment.mainactivity.main + +import android.app.NotificationManager +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Bundle +import android.provider.MediaStore +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageButton +import android.widget.LinearLayout +import android.widget.TextView +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.RecyclerView +import com.futo.platformplayer.R +import com.futo.platformplayer.Settings +import com.futo.platformplayer.SettingsDev +import com.futo.platformplayer.UIDialogs +import com.futo.platformplayer.activities.DeveloperActivity +import com.futo.platformplayer.activities.MainActivity +import com.futo.platformplayer.api.media.models.contents.IPlatformContent +import com.futo.platformplayer.api.media.models.video.IPlatformVideo +import com.futo.platformplayer.assume +import com.futo.platformplayer.dp +import com.futo.platformplayer.logging.Logger +import com.futo.platformplayer.states.Album +import com.futo.platformplayer.states.Artist +import com.futo.platformplayer.states.ArtistOrdering +import com.futo.platformplayer.states.FileEntry +import com.futo.platformplayer.states.StateApp +import com.futo.platformplayer.states.StateLibrary +import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny +import com.futo.platformplayer.views.AnyInsertedAdapterView +import com.futo.platformplayer.views.AnyInsertedAdapterView.Companion.asAnyWithTop +import com.futo.platformplayer.views.AnyInsertedAdapterView.Companion.asAnyWithViews +import com.futo.platformplayer.views.LibrarySection +import com.futo.platformplayer.views.LoaderView +import com.futo.platformplayer.views.adapters.AnyAdapter +import com.futo.platformplayer.views.adapters.InsertedViewAdapter +import com.futo.platformplayer.views.adapters.viewholders.AlbumTileViewHolder +import com.futo.platformplayer.views.adapters.viewholders.ArtistTileViewHolder +import com.futo.platformplayer.views.adapters.viewholders.FileViewHolder +import com.futo.platformplayer.views.adapters.viewholders.LocalVideoTileViewHolder +import com.futo.platformplayer.views.buttons.BigButton +import com.futo.platformplayer.views.fields.FieldForm +import com.futo.platformplayer.views.fields.ReadOnlyTextField +import com.google.android.material.button.MaterialButton + + +class SettingsFragment : MainFragment() { + override val isMainView : Boolean = true; + override val isTab: Boolean = true; + override val hasBottomBar: Boolean get() = true; + + private var view: FragView? = null; + + + override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): android.view.View { + val newView = FragView(this); + view = newView; + return newView; + } + + override fun onShownWithView(parameter: Any?, isBack: Boolean) { + super.onShownWithView(parameter, isBack); + view?.onShown(); + } + + override fun onDestroyMainView() { + view = null; + super.onDestroyMainView(); + } + + companion object { + fun newInstance() = SettingsFragment().apply {} + } + + + class FragView: ConstraintLayout { + val fragment: SettingsFragment; + + private val _form: FieldForm; + private val _buttonBack: ImageButton; + private val _loaderView: LoaderView; + + private val _devSets: LinearLayout; + private val _buttonDev: MaterialButton; + + private var _isFinished = false; + + lateinit var overlay: FrameLayout; + + val notifPermission = "android.permission.POST_NOTIFICATIONS"; + + constructor(fragment: SettingsFragment) : super(fragment.requireContext()) { + inflate(context, R.layout.activity_settings, this); + this.fragment = fragment; + + val activity = fragment.activity; + + findViewById(R.id.container_topbar).isVisible = false; + _form = findViewById(R.id.settings_form); + _buttonBack = findViewById(R.id.button_back); + _buttonDev = findViewById(R.id.button_dev); + _devSets = findViewById(R.id.dev_settings); + _loaderView = findViewById(R.id.loader); + overlay = findViewById(R.id.overlay_container); + + _form.onChanged.subscribe { field, _ -> + Logger.i("SettingsActivity", "Setting [${field.field?.name}] changed, saving"); + _form.setObjectValues(); + Settings.instance.save(); + + if(field.descriptor?.id == "app_language") { + Logger.i("SettingsActivity", "App language change detected, propogating to shared preferences"); + StateApp.instance.setLocaleSetting(context, Settings.instance.language.getAppLanguageLocaleString()); + } + + if(field.descriptor?.id == "background_update" && activity is MainActivity) { + Logger.i("SettingsActivity", "Detected change in background work ${field.value}"); + if(Settings.instance.subscriptions.subscriptionsBackgroundUpdateInterval > 0) { + val notifManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager; + if(!notifManager.areNotificationsEnabled()) { + UIDialogs.toast(context, "Notifications aren't enabled"); + activity.requestNotificationPermissions("Notifications need to be enabled for background updating to function") + } + } + } + }; + _buttonBack.setOnClickListener { + //finish(); + } + + _buttonDev.setOnClickListener { + //startActivity(Intent(this, DeveloperActivity::class.java)); + fragment.navigate(null, true); + } + + //_lastActivity = this; + + reloadSettings(); + } + + var isFirstLoad = true; + fun reloadSettings() { + val firstLoad = isFirstLoad; + isFirstLoad = false; + _form.setSearchVisible(false); + _loaderView.start(); + _form.fromObject(fragment.lifecycleScope, Settings.instance) { + _loaderView.stop(); + _form.setSearchVisible(true); + + var devCounter = 0; + _form.findField("code")?.assume()?.setOnClickListener { + devCounter++; + if(devCounter > 5) { + devCounter = 0; + SettingsDev.instance.developerMode = true; + SettingsDev.instance.save(); + updateDevMode(); + UIDialogs.toast(context, fragment.getString(R.string.you_are_now_in_developer_mode)); + } + }; + + /* + if(firstLoad) { + val query = intent.getStringExtra("query"); + if(!query.isNullOrEmpty()) { + _form.setSearchQuery(query); + } + }*/ + }; + } + + + fun onShown() { + updateDevMode(); + } + + fun updateDevMode() { + if(SettingsDev.instance.developerMode) + _devSets.visibility = View.VISIBLE; + else + _devSets.visibility = View.GONE; + } + + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_dev.xml b/app/src/main/res/layout/activity_dev.xml index 6567d67f..af9a4a2a 100644 --- a/app/src/main/res/layout/activity_dev.xml +++ b/app/src/main/res/layout/activity_dev.xml @@ -11,6 +11,7 @@