A commit, that was needed...
6
.directory
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[Dolphin]
|
||||||
|
Timestamp=2022,1,4,18,4,55.221
|
||||||
|
Version=4
|
||||||
|
|
||||||
|
[Settings]
|
||||||
|
HiddenFilesShown=true
|
123
.idea/codeStyles/Project.xml
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<JetCodeStyleSettings>
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</JetCodeStyleSettings>
|
||||||
|
<codeStyleSettings language="XML">
|
||||||
|
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
|
</indentOptions>
|
||||||
|
<arrangement>
|
||||||
|
<rules>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>xmlns:android</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>xmlns:.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:id</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>style</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
</rules>
|
||||||
|
</arrangement>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="kotlin">
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
|
@ -1,18 +1,18 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="deploymentTargetDropDown">
|
<component name="deploymentTargetDropDown">
|
||||||
<targetSelectedWithDropDown>
|
<runningDeviceTargetSelectedWithDropDown>
|
||||||
<Target>
|
<Target>
|
||||||
<type value="QUICK_BOOT_TARGET" />
|
<type value="RUNNING_DEVICE_TARGET" />
|
||||||
<deviceKey>
|
<deviceKey>
|
||||||
<Key>
|
<Key>
|
||||||
<type value="VIRTUAL_DEVICE_PATH" />
|
<type value="SERIAL_NUMBER" />
|
||||||
<value value="$USER_HOME$/.android/avd/Pixel_3a_XL_API_30.avd" />
|
<value value="adb-a3950b57-giqMub._adb-tls-connect._tcp" />
|
||||||
</Key>
|
</Key>
|
||||||
</deviceKey>
|
</deviceKey>
|
||||||
</Target>
|
</Target>
|
||||||
</targetSelectedWithDropDown>
|
</runningDeviceTargetSelectedWithDropDown>
|
||||||
<timeTargetWasSelectedWithDropDown value="2021-12-05T17:39:35.648801Z" />
|
<timeTargetWasSelectedWithDropDown value="2022-09-06T09:57:52.303689Z" />
|
||||||
<targetsSelectedWithDialog>
|
<targetsSelectedWithDialog>
|
||||||
<Target>
|
<Target>
|
||||||
<type value="QUICK_BOOT_TARGET" />
|
<type value="QUICK_BOOT_TARGET" />
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
<option value="$PROJECT_DIR$/app" />
|
<option value="$PROJECT_DIR$/app" />
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
<option name="resolveModulePerSourceSet" value="false" />
|
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -51,6 +51,43 @@
|
||||||
<entry key="../../../../../layout/compose-model-1641222972102.xml" value="0.46296296296296297" />
|
<entry key="../../../../../layout/compose-model-1641222972102.xml" value="0.46296296296296297" />
|
||||||
<entry key="../../../../../layout/compose-model-1641224248444.xml" value="0.46296296296296297" />
|
<entry key="../../../../../layout/compose-model-1641224248444.xml" value="0.46296296296296297" />
|
||||||
<entry key="../../../../../layout/compose-model-1641226030069.xml" value="0.46296296296296297" />
|
<entry key="../../../../../layout/compose-model-1641226030069.xml" value="0.46296296296296297" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1642254311605.xml" value="0.3015202702702703" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1642259770760.xml" value="0.22592592592592592" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1642262177120.xml" value="0.22870370370370371" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1642342907711.xml" value="0.1" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1642342992638.xml" value="0.30833333333333335" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1642342997277.xml" value="0.3015202702702703" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1642873166547.xml" value="0.1" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1643299808590.xml" value="2.0" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1643300369296.xml" value="1.0" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1643319277393.xml" value="2.0" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1643836978788.xml" value="2.0" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1643837012793.xml" value="0.26296296296296295" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1643992156800.xml" value="0.26296296296296295" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1643993639504.xml" value="0.26296296296296295" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1643994288560.xml" value="2.0" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1643996450918.xml" value="0.16554054054054054" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1656272626813.xml" value="2.0" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1657531916246.xml" value="2.0" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1657531926635.xml" value="0.26296296296296295" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1657532602169.xml" value="1.0" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1657534975618.xml" value="0.33" />
|
||||||
|
<entry key="../../../../../layout/compose-model-1657549343896.xml" value="0.1" />
|
||||||
|
<entry key="app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.1" />
|
||||||
|
<entry key="app/src/main/res/drawable/forward.xml" value="0.1" />
|
||||||
|
<entry key="app/src/main/res/drawable/ic_launcher_adaptive_foreground.xml" value="0.1" />
|
||||||
|
<entry key="app/src/main/res/drawable/ic_launcher_background.xml" value="0.1" />
|
||||||
|
<entry key="app/src/main/res/drawable/ic_new_caster_foreground.xml" value="0.1" />
|
||||||
|
<entry key="app/src/main/res/drawable/ic_new_caster_monochrome_foreground.xml" value="0.101" />
|
||||||
|
<entry key="app/src/main/res/drawable/pause.xml" value="0.1" />
|
||||||
|
<entry key="app/src/main/res/drawable/play.xml" value="0.1" />
|
||||||
|
<entry key="app/src/main/res/drawable/rewind.xml" value="0.1" />
|
||||||
|
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml" value="0.1" />
|
||||||
|
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_adaptive.xml" value="0.101" />
|
||||||
|
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_adaptive_round.xml" value="0.101" />
|
||||||
|
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.101" />
|
||||||
|
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_new_caster.xml" value="0.1" />
|
||||||
|
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_new_caster_round.xml" value="0.101" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -8,7 +8,7 @@ plugins {
|
||||||
android {
|
android {
|
||||||
|
|
||||||
|
|
||||||
compileSdk 31
|
compileSdk 33
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "eu.toldi.balazs.caster"
|
applicationId "eu.toldi.balazs.caster"
|
||||||
|
@ -82,6 +82,7 @@ dependencies {
|
||||||
implementation 'com.google.android.material:material:1.6.1'
|
implementation 'com.google.android.material:material:1.6.1'
|
||||||
implementation "androidx.compose.ui:ui:$compose_version"
|
implementation "androidx.compose.ui:ui:$compose_version"
|
||||||
implementation "androidx.compose.material:material:$compose_version"
|
implementation "androidx.compose.material:material:$compose_version"
|
||||||
|
implementation "androidx.compose.material3:material3:1.0.0-beta01"
|
||||||
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
|
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
|
||||||
implementation "androidx.compose.material:material-icons-extended:$compose_version"
|
implementation "androidx.compose.material:material-icons-extended:$compose_version"
|
||||||
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
|
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
|
||||||
|
|
|
@ -10,13 +10,18 @@
|
||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@drawable/ic_caster_logo"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:roundIcon="@drawable/ic_caster_logo"
|
|
||||||
android:supportsRtl="true"
|
|
||||||
android:extractNativeLibs="true"
|
android:extractNativeLibs="true"
|
||||||
|
android:icon="@mipmap/ic_new_caster"
|
||||||
|
android:label="@string/app_name"
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
|
android:roundIcon="@mipmap/ic_new_caster"
|
||||||
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.Caster">
|
android:theme="@style/Theme.Caster">
|
||||||
|
<activity
|
||||||
|
android:name=".FolderViewActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/title_activity_folder_view"
|
||||||
|
android:theme="@style/Theme.Caster.NoActionBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ShareRecieverActivity"
|
android:name=".ShareRecieverActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
@ -24,17 +29,23 @@
|
||||||
android:theme="@style/Theme.Caster.NoActionBar">
|
android:theme="@style/Theme.Caster.NoActionBar">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<data android:mimeType="text/plain" />
|
<data android:mimeType="text/plain" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<data android:mimeType="image/*" />
|
<data android:mimeType="image/*" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<data android:mimeType="video/*" />
|
<data android:mimeType="video/*" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
@ -42,11 +53,10 @@
|
||||||
android:name=".ChromecastManagerActivity"
|
android:name=".ChromecastManagerActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:label="@string/title_activity_chromecast_manager"
|
android:label="@string/title_activity_chromecast_manager"
|
||||||
android:theme="@style/Theme.Caster.NoActionBar" >
|
android:theme="@style/Theme.Caster.NoActionBar">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MEDIA_BUTTON"/>
|
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
@ -59,7 +69,8 @@
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<service android:name=".services.ChromecastManagerService"/>
|
|
||||||
|
<service android:name=".services.ChromecastManagerService" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
BIN
app/src/main/ic_launcher_adaptive-playstore.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
app/src/main/ic_new_caster-playstore.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
app/src/main/ic_new_caster_monochrome-playstore.png
Normal file
After Width: | Height: | Size: 12 KiB |
|
@ -132,6 +132,7 @@ object ChromeCastHelper {
|
||||||
}
|
}
|
||||||
if (_streamInfo == null) {
|
if (_streamInfo == null) {
|
||||||
exitStatus = false
|
exitStatus = false
|
||||||
|
Log.e("Caster_ChromecastHelper","Failed to fetch stream!")
|
||||||
callBack.invoke()
|
callBack.invoke()
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,33 +1,43 @@
|
||||||
package eu.toldi.balazs.caster
|
package eu.toldi.balazs.caster
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.database.Cursor
|
import android.content.SharedPreferences
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.OpenableColumns
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.webkit.MimeTypeMap
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
|
import androidx.compose.material3.ColorScheme
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import coil.compose.AsyncImage
|
||||||
import coil.compose.rememberImagePainter
|
import coil.compose.rememberImagePainter
|
||||||
import com.yausername.ffmpeg.FFmpeg
|
import com.yausername.ffmpeg.FFmpeg
|
||||||
import com.yausername.youtubedl_android.YoutubeDL
|
import com.yausername.youtubedl_android.YoutubeDL
|
||||||
|
@ -36,25 +46,111 @@ import eu.toldi.balazs.caster.helpers.FileCacheHelper
|
||||||
import eu.toldi.balazs.caster.model.ChromecastManageViewmodel
|
import eu.toldi.balazs.caster.model.ChromecastManageViewmodel
|
||||||
import eu.toldi.balazs.caster.services.ChromecastManagerService
|
import eu.toldi.balazs.caster.services.ChromecastManagerService
|
||||||
import eu.toldi.balazs.caster.ui.theme.CasterTheme
|
import eu.toldi.balazs.caster.ui.theme.CasterTheme
|
||||||
|
import eu.toldi.balazs.caster.ui.theme.getColorScheme
|
||||||
import su.litvak.chromecast.api.v2.ChromeCast
|
import su.litvak.chromecast.api.v2.ChromeCast
|
||||||
import su.litvak.chromecast.api.v2.Media
|
import su.litvak.chromecast.api.v2.Media
|
||||||
import su.litvak.chromecast.api.v2.MediaStatus
|
import su.litvak.chromecast.api.v2.MediaStatus
|
||||||
import java.io.BufferedInputStream
|
import java.io.File
|
||||||
import java.io.BufferedOutputStream
|
import java.util.regex.Matcher
|
||||||
import java.io.FileOutputStream
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
|
||||||
|
data class BottomNavItem(
|
||||||
|
val label: String,
|
||||||
|
val icon: ImageVector,
|
||||||
|
val route: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
object Constants {
|
||||||
|
val BottomNavItems = listOf(
|
||||||
|
BottomNavItem(
|
||||||
|
label = "Home",
|
||||||
|
icon = Icons.Filled.Home,
|
||||||
|
route = "home"
|
||||||
|
),
|
||||||
|
BottomNavItem(
|
||||||
|
label = "Gallery",
|
||||||
|
icon = Icons.Filled.Image,
|
||||||
|
route = "gallery"
|
||||||
|
),
|
||||||
|
BottomNavItem(
|
||||||
|
label = "Settings",
|
||||||
|
icon = Icons.Filled.Settings,
|
||||||
|
route = "settings"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
class ChromecastManagerActivity : ComponentActivity() {
|
class ChromecastManagerActivity : ComponentActivity() {
|
||||||
companion object {
|
companion object {
|
||||||
var chromeCast_: ChromeCast? = null
|
var chromeCast_: ChromeCast? = null
|
||||||
const val METADATA_THUMB = "thumb"
|
const val METADATA_THUMB = "thumb"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val pattern =
|
||||||
|
"https?:\\/\\/(?:[0-9A-Z-]+\\.)?(?:youtu\\.be\\/|youtube\\.com\\S*[^\\w\\-\\s])([\\w\\-]{11})(?=[^\\w\\-]|$)(?![?=&+%\\w]*(?:['\"][^<>]*>|<\\/a>))[?=&+%\\w]*"
|
||||||
|
|
||||||
|
val compiledPattern: Pattern =
|
||||||
|
Pattern.compile(pattern, Pattern.CASE_INSENSITIVE)
|
||||||
|
|
||||||
|
lateinit var colorScheme: ColorScheme
|
||||||
|
lateinit var prefs: SharedPreferences
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BottomNavigationBar(navController: NavHostController) {
|
||||||
|
|
||||||
|
BottomNavigation(
|
||||||
|
backgroundColor = colorScheme.secondary,
|
||||||
|
contentColor = colorScheme.onSecondary
|
||||||
|
) {
|
||||||
|
// observe the backstack
|
||||||
|
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||||
|
|
||||||
|
// observe current route to change the icon
|
||||||
|
// color,label color when navigated
|
||||||
|
val currentRoute = navBackStackEntry?.destination?.route
|
||||||
|
|
||||||
|
// Bottom nav items we declared
|
||||||
|
Constants.BottomNavItems.forEach { navItem ->
|
||||||
|
|
||||||
|
// Place the bottom nav items
|
||||||
|
BottomNavigationItem(
|
||||||
|
|
||||||
|
// it currentRoute is equal then its selected route
|
||||||
|
selected = currentRoute == navItem.route,
|
||||||
|
|
||||||
|
// navigate on click
|
||||||
|
onClick = {
|
||||||
|
navController.navigate(navItem.route)
|
||||||
|
},
|
||||||
|
|
||||||
|
// Icon of navItem
|
||||||
|
icon = {
|
||||||
|
Icon(imageVector = navItem.icon, contentDescription = navItem.label)
|
||||||
|
},
|
||||||
|
|
||||||
|
// label
|
||||||
|
label = {
|
||||||
|
Text(text = navItem.label)
|
||||||
|
},
|
||||||
|
alwaysShowLabel = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private lateinit var chromeCast: ChromeCast
|
private lateinit var chromeCast: ChromeCast
|
||||||
private lateinit var viewModel : ChromecastManageViewmodel
|
private lateinit var viewModel: ChromecastManageViewmodel
|
||||||
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
prefs = getSharedPreferences(
|
||||||
|
"eu.toldi.Caster", Context.MODE_PRIVATE
|
||||||
|
)
|
||||||
if (chromeCast_ == null)
|
if (chromeCast_ == null)
|
||||||
finish()
|
finish()
|
||||||
chromeCast = chromeCast_ as ChromeCast
|
chromeCast = chromeCast_ as ChromeCast
|
||||||
|
@ -73,55 +169,73 @@ class ChromecastManagerActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
setContent {
|
setContent {
|
||||||
CasterTheme {
|
CasterTheme {
|
||||||
viewModel = ViewModelProvider(this).get(ChromecastManageViewmodel::class.java)
|
colorScheme = getColorScheme()
|
||||||
viewModel.chromeCast = chromeCast
|
// remember navController so it does not
|
||||||
// A surface container using the 'background' color from the theme
|
// get recreated on recomposition
|
||||||
Surface(color = MaterialTheme.colors.background) {
|
val navController = rememberNavController()
|
||||||
Column {
|
|
||||||
//viewModel.fetchMediaStatus()
|
|
||||||
MenuBar()
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(6.dp)
|
|
||||||
.fillMaxWidth()
|
|
||||||
.fillMaxHeight(),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
var text by remember { mutableStateOf("") }
|
|
||||||
|
|
||||||
OutlinedTextField(
|
Surface(color = colorScheme.background, contentColor = colorScheme.onBackground) {
|
||||||
value = text,
|
|
||||||
onValueChange = {
|
// Scaffold Component
|
||||||
text = it
|
Scaffold(
|
||||||
|
backgroundColor = colorScheme.background,
|
||||||
|
contentColor = colorScheme.onBackground,
|
||||||
|
// Bottom navigation
|
||||||
|
bottomBar = {
|
||||||
|
BottomNavigationBar(navController = navController)
|
||||||
},
|
},
|
||||||
label = { Text(stringResource(id = R.string.cast_url)) },
|
content = { padding ->
|
||||||
modifier = Modifier.padding(vertical = 4.dp)
|
// Navhost: where screens are placed
|
||||||
|
NavHostContainer(navController = navController, padding = padding)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
var castEnabled by remember { mutableStateOf(true) }
|
|
||||||
Button(
|
|
||||||
enabled = castEnabled,
|
|
||||||
onClick = {
|
|
||||||
castEnabled = false
|
|
||||||
viewModel.castLink(text) {
|
|
||||||
castEnabled = true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifier = Modifier.padding(vertical = 4.dp)
|
|
||||||
) {
|
|
||||||
Text(text = stringResource(id = R.string.cast))
|
|
||||||
}
|
|
||||||
if (castEnabled.not()) {
|
|
||||||
CircularProgressIndicator()
|
|
||||||
}
|
|
||||||
PlayBackControl()
|
|
||||||
mediaStatus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun NavHostContainer(
|
||||||
|
navController: NavHostController,
|
||||||
|
padding: PaddingValues
|
||||||
|
) {
|
||||||
|
|
||||||
|
NavHost(
|
||||||
|
navController = navController,
|
||||||
|
|
||||||
|
// set the start destination as home
|
||||||
|
startDestination = "home",
|
||||||
|
|
||||||
|
// Set the padding provided by scaffold
|
||||||
|
modifier = Modifier.padding(paddingValues = padding),
|
||||||
|
|
||||||
|
builder = {
|
||||||
|
|
||||||
|
// route : Home
|
||||||
|
composable("home") {
|
||||||
|
Column {
|
||||||
|
MenuBar()
|
||||||
|
HomeScreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// route : search
|
||||||
|
composable("gallery") {
|
||||||
|
Column {
|
||||||
|
MenuBar()
|
||||||
|
ImageScreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// route : profile
|
||||||
|
composable("settings") {
|
||||||
|
SettingsScreen()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun mediaStatus() {
|
fun mediaStatus() {
|
||||||
|
@ -157,13 +271,13 @@ class ChromecastManagerActivity : ComponentActivity() {
|
||||||
val image =
|
val image =
|
||||||
mediaStatus.media.metadata[METADATA_THUMB] as String
|
mediaStatus.media.metadata[METADATA_THUMB] as String
|
||||||
Log.e("Caster", image)
|
Log.e("Caster", image)
|
||||||
Image(
|
AsyncImage(
|
||||||
painter = rememberImagePainter(image),
|
model = image,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if(mediaStatus.media.contentType.startsWith("video/")) {
|
if (mediaStatus.media.contentType.startsWith("video/")) {
|
||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxWidth(),
|
Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
@ -198,13 +312,11 @@ class ChromecastManagerActivity : ComponentActivity() {
|
||||||
@Composable
|
@Composable
|
||||||
fun MenuBar() {
|
fun MenuBar() {
|
||||||
val pickPictureLauncher = rememberLauncherForActivityResult(
|
val pickPictureLauncher = rememberLauncherForActivityResult(
|
||||||
ActivityResultContracts.OpenDocument()
|
ActivityResultContracts.OpenDocumentTree()
|
||||||
) { fileUri ->
|
) { fileUri ->
|
||||||
if (fileUri != null) {
|
if (fileUri != null) {
|
||||||
val cacheHelper = FileCacheHelper(applicationContext)
|
DocumentFile.fromTreeUri(applicationContext, fileUri)
|
||||||
cacheHelper.cacheThis(listOf(fileUri))
|
?.let { viewModel.addFolder(it) }
|
||||||
viewModel.castFromCache(cacheHelper.tryFileName(fileUri))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
|
@ -222,7 +334,7 @@ class ChromecastManagerActivity : ComponentActivity() {
|
||||||
actions = {
|
actions = {
|
||||||
Row {
|
Row {
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
pickPictureLauncher.launch(arrayOf("image/*", "video/*"))
|
pickPictureLauncher.launch(Uri.EMPTY)
|
||||||
}) {
|
}) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Filled.Folder,
|
Icons.Filled.Folder,
|
||||||
|
@ -230,7 +342,9 @@ class ChromecastManagerActivity : ComponentActivity() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
backgroundColor = colorScheme.primary,
|
||||||
|
contentColor = colorScheme.onPrimary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,4 +475,172 @@ class ChromecastManagerActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
return super.onKeyDown(keyCode, event)
|
return super.onKeyDown(keyCode, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HomeScreen() {
|
||||||
|
// Column Composable,
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize(),
|
||||||
|
// Parameters set to place the items in center
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
viewModel =
|
||||||
|
ViewModelProvider(this@ChromecastManagerActivity).get(ChromecastManageViewmodel::class.java)
|
||||||
|
viewModel.chromeCast = chromeCast
|
||||||
|
// A surface container using the 'background' color from the theme
|
||||||
|
//viewModel.fetchMediaStatus()
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(6.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.fillMaxHeight(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
var text by remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = text,
|
||||||
|
onValueChange = {
|
||||||
|
text = it
|
||||||
|
},
|
||||||
|
label = { Text(stringResource(id = R.string.cast_url)) },
|
||||||
|
modifier = Modifier.padding(vertical = 4.dp),
|
||||||
|
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||||
|
cursorColor = colorScheme.primary,
|
||||||
|
focusedBorderColor =
|
||||||
|
colorScheme.primary.copy(alpha = ContentAlpha.high),
|
||||||
|
focusedLabelColor = colorScheme.primary
|
||||||
|
)
|
||||||
|
)
|
||||||
|
var castEnabled by remember { mutableStateOf(true) }
|
||||||
|
Button(
|
||||||
|
enabled = castEnabled,
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
backgroundColor = colorScheme.primary,
|
||||||
|
contentColor = colorScheme.onPrimary
|
||||||
|
),
|
||||||
|
onClick = {
|
||||||
|
castEnabled = false
|
||||||
|
val link = text
|
||||||
|
viewModel.castLink(link) {
|
||||||
|
if (prefs.getBoolean("sponsorblock_enabled", false)) {
|
||||||
|
val matcher: Matcher = compiledPattern.matcher(link)
|
||||||
|
if (matcher.find()) {
|
||||||
|
val id = matcher.group(1)
|
||||||
|
Log.e("Caster_SB", id)
|
||||||
|
val intent = Intent(
|
||||||
|
applicationContext,
|
||||||
|
ChromecastManagerService::class.java
|
||||||
|
).apply {
|
||||||
|
action = ChromecastManagerService.ACTION_SET_YT_ID
|
||||||
|
putExtra("yt_id", id)
|
||||||
|
putExtra("media_url", link)
|
||||||
|
}
|
||||||
|
application.startService(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
castEnabled = true
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.padding(vertical = 4.dp)
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(id = R.string.cast))
|
||||||
|
}
|
||||||
|
if (castEnabled.not()) {
|
||||||
|
CircularProgressIndicator(color = colorScheme.secondary)
|
||||||
|
}
|
||||||
|
PlayBackControl()
|
||||||
|
mediaStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SettingsScreen() {
|
||||||
|
// Column Composable,
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize(),
|
||||||
|
// parameters set to place the items in center
|
||||||
|
) {
|
||||||
|
Row() {
|
||||||
|
Text("Enable SponsorBlock")
|
||||||
|
var switched by remember {
|
||||||
|
mutableStateOf(
|
||||||
|
prefs.getBoolean(
|
||||||
|
"sponsorblock_enabled",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Switch(checked = switched, onCheckedChange = {
|
||||||
|
switched = !switched
|
||||||
|
prefs.edit().putBoolean("sponsorblock_enabled", switched).apply()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ImageScreen() {
|
||||||
|
// Column Composable,
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
|
||||||
|
val folders = viewModel.folderListState.value
|
||||||
|
val cacheHelper = FileCacheHelper(applicationContext)
|
||||||
|
LazyColumn() {
|
||||||
|
items(folders.size) { index ->
|
||||||
|
Card(modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable {
|
||||||
|
val intent = Intent(applicationContext, FolderViewActivity::class.java)
|
||||||
|
FolderViewActivity.chromecastRecv = chromeCast
|
||||||
|
FolderViewActivity.folderRecv = folders[index]
|
||||||
|
startActivity(intent)
|
||||||
|
}) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize(),
|
||||||
|
// parameters set to place the items in center
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
val image = folders[index].listFiles()
|
||||||
|
.firstOrNull {
|
||||||
|
it.name?.endsWith(".png") ?: false || it.name?.endsWith(
|
||||||
|
".jpg"
|
||||||
|
) ?: false
|
||||||
|
}
|
||||||
|
if (image != null) {
|
||||||
|
cacheHelper.cacheThis(listOf(image.uri))
|
||||||
|
|
||||||
|
val imageFile = File(cacheDir, cacheHelper.tryFileName(image.uri))
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.height(80.dp)
|
||||||
|
.width(80.dp)
|
||||||
|
.padding(10.dp)
|
||||||
|
) {
|
||||||
|
Log.e(null, imageFile.name.toString())
|
||||||
|
Image(
|
||||||
|
painter = rememberImagePainter(imageFile),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text(text = folders[index].name!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
201
app/src/main/java/eu/toldi/balazs/caster/FolderViewActivity.kt
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
package eu.toldi.balazs.caster
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material.*
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.semantics.Role.Companion.Image
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
import coil.compose.AsyncImage
|
||||||
|
import coil.compose.SubcomposeAsyncImage
|
||||||
|
import coil.compose.rememberImagePainter
|
||||||
|
import coil.size.OriginalSize
|
||||||
|
|
||||||
|
import coil.transform.CircleCropTransformation
|
||||||
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
|
import com.google.accompanist.pager.HorizontalPager
|
||||||
|
import com.google.accompanist.pager.rememberPagerState
|
||||||
|
import eu.toldi.balazs.caster.helpers.FileCacheHelper
|
||||||
|
import eu.toldi.balazs.caster.services.ChromecastManagerService
|
||||||
|
import eu.toldi.balazs.caster.ui.theme.CasterTheme
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import su.litvak.chromecast.api.v2.ChromeCast
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class FolderViewActivity : ComponentActivity() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var chromecastRecv: ChromeCast? = null
|
||||||
|
var folderRecv: DocumentFile? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("CoroutineCreationDuringComposition")
|
||||||
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
if (chromecastRecv == null || folderRecv == null) finish()
|
||||||
|
val chromeCast: ChromeCast = chromecastRecv!!
|
||||||
|
val folder: DocumentFile = folderRecv!!
|
||||||
|
val cacheHelper = FileCacheHelper(applicationContext)
|
||||||
|
setContent {
|
||||||
|
CasterTheme {
|
||||||
|
// A surface container using the 'background' color from the theme
|
||||||
|
Surface(color = MaterialTheme.colors.background) {
|
||||||
|
Column(modifier = Modifier.fillMaxSize()) {
|
||||||
|
MenuBar(title = folder.name!!)
|
||||||
|
val images = folder.listFiles()
|
||||||
|
.filter { it.name?.endsWith(".png") ?: false || it.name?.endsWith(".jpg") ?: false }
|
||||||
|
//cacheHelper.cacheThis(images.map { it.uri })
|
||||||
|
Log.e("CastFile", images.size.toString())
|
||||||
|
val row_count = images.size / 3 + 1
|
||||||
|
/*LazyColumn(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
items(row_count) { index ->
|
||||||
|
Row(modifier = Modifier.fillMaxWidth()){
|
||||||
|
for (i in 0 until 3){
|
||||||
|
if(index+i >= images.size) break
|
||||||
|
val imageFile = if (images[index+i].exists()) {
|
||||||
|
cacheHelper.cacheThis(listOf(images[index+i].uri))
|
||||||
|
File(cacheDir, cacheHelper.tryFileName(images[index+i].uri))
|
||||||
|
} else {
|
||||||
|
File(cacheDir, cacheHelper.tryFileName(images[index+i].uri))
|
||||||
|
}
|
||||||
|
Card(modifier = Modifier
|
||||||
|
.padding(all = 5.dp)
|
||||||
|
.weight(1f)) {
|
||||||
|
Column {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.height(100.dp)
|
||||||
|
.width(100.dp)
|
||||||
|
.padding(10.dp)
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = rememberImagePainter(
|
||||||
|
data = imageFile
|
||||||
|
|
||||||
|
),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
//Text(imageFile.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
val pagerState = rememberPagerState()
|
||||||
|
if (!pagerState.isScrollInProgress) {
|
||||||
|
val currentPage = pagerState.currentPage
|
||||||
|
Log.e("Caster_IMG", currentPage.toString())
|
||||||
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
Thread.sleep(1000)
|
||||||
|
if (pagerState.currentPage == currentPage && !pagerState.isScrollInProgress) {
|
||||||
|
val imageFile = if (images[currentPage].exists()) {
|
||||||
|
cacheHelper.cacheThis(listOf(images[currentPage].uri))
|
||||||
|
File(cacheDir, cacheHelper.tryFileName(images[currentPage].uri))
|
||||||
|
} else {
|
||||||
|
File(cacheDir, cacheHelper.tryFileName(images[currentPage].uri))
|
||||||
|
}
|
||||||
|
val link = "http:/${ChromeCastHelper.getIPv4Address()}:${ChromecastManagerService.PORT}/assets/${
|
||||||
|
imageFile.name
|
||||||
|
}"
|
||||||
|
Log.e("Caster_IMG","Casting image with url: $link")
|
||||||
|
val result = ChromeCastHelper.castLink(
|
||||||
|
link
|
||||||
|
){ Log.e("Caster_IMG","Casting done")}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HorizontalPager(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
count = images.size,
|
||||||
|
state = pagerState
|
||||||
|
|
||||||
|
) { index ->
|
||||||
|
val imageFile = if (images[index].exists()) {
|
||||||
|
cacheHelper.cacheThis(listOf(images[index].uri))
|
||||||
|
File(cacheDir, cacheHelper.tryFileName(images[index].uri))
|
||||||
|
} else {
|
||||||
|
File(cacheDir, cacheHelper.tryFileName(images[index].uri))
|
||||||
|
}
|
||||||
|
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(all = 5.dp)
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(240.dp)
|
||||||
|
// .padding(10.dp)
|
||||||
|
) {
|
||||||
|
Log.e(
|
||||||
|
"Caster_IMG",
|
||||||
|
"http:/${ChromeCastHelper.getIPv4Address()}:${ChromecastManagerService.PORT}/assets/${imageFile.name}"
|
||||||
|
)
|
||||||
|
SubcomposeAsyncImage(
|
||||||
|
model = imageFile,
|
||||||
|
contentDescription = imageFile.name,
|
||||||
|
loading = {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
Text(imageFile.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MenuBar(title: String) {
|
||||||
|
TopAppBar(
|
||||||
|
title = {
|
||||||
|
Text(title)
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = { finish() }) {
|
||||||
|
Icon(
|
||||||
|
Icons.Filled.ArrowBack,
|
||||||
|
contentDescription = stringResource(id = R.string.back)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Composable
|
||||||
|
fun DefaultPreview() {
|
||||||
|
CasterTheme {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,24 +2,39 @@ package eu.toldi.balazs.caster
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.ColorSpace.adapt
|
||||||
import android.net.wifi.WifiManager
|
import android.net.wifi.WifiManager
|
||||||
import android.net.wifi.WifiManager.MulticastLock
|
import android.net.wifi.WifiManager.MulticastLock
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
|
import androidx.compose.material.Button
|
||||||
|
import androidx.compose.material.ButtonColors
|
||||||
|
import androidx.compose.material.ButtonDefaults
|
||||||
|
import androidx.compose.material.Card
|
||||||
|
import androidx.compose.material.CircularProgressIndicator
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.OutlinedTextField
|
||||||
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material.icons.filled.Refresh
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
@ -29,14 +44,17 @@ import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||||
import eu.toldi.balazs.caster.model.ChromeCastViewModel
|
import eu.toldi.balazs.caster.model.ChromeCastViewModel
|
||||||
import eu.toldi.balazs.caster.ui.theme.CasterTheme
|
import eu.toldi.balazs.caster.ui.theme.CasterTheme
|
||||||
|
import eu.toldi.balazs.caster.ui.theme.getColorScheme
|
||||||
import su.litvak.chromecast.api.v2.ChromeCast
|
import su.litvak.chromecast.api.v2.ChromeCast
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
open class MainActivity : ComponentActivity() {
|
open class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
protected lateinit var viewModel: ChromeCastViewModel
|
protected lateinit var viewModel: ChromeCastViewModel
|
||||||
protected var multicastLock: MulticastLock? = null
|
protected var multicastLock: MulticastLock? = null
|
||||||
|
protected lateinit var colorScheme: ColorScheme
|
||||||
|
|
||||||
protected fun isViewModelInitialised() = ::viewModel.isInitialized
|
protected fun isViewModelInitialised() = ::viewModel.isInitialized
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
|
@ -67,13 +85,13 @@ open class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
CasterTheme {
|
CasterTheme {
|
||||||
|
colorScheme = getColorScheme()
|
||||||
viewModel = ViewModelProvider(this).get(ChromeCastViewModel::class.java)
|
viewModel = ViewModelProvider(this).get(ChromeCastViewModel::class.java)
|
||||||
val chromeCastState = viewModel.chromeCasts.observeAsState(initial = emptyList())
|
val chromeCastState = viewModel.chromeCasts.observeAsState(initial = emptyList())
|
||||||
val chromeCasts = chromeCastState.value
|
val chromeCasts = chromeCastState.value
|
||||||
Log.e(null, chromeCasts.toString())
|
Log.e(null, chromeCasts.toString())
|
||||||
// A surface container using the 'background' color from the theme
|
// A surface container using the 'background' color from the theme
|
||||||
Surface(color = MaterialTheme.colors.background) {
|
Surface(color = colorScheme.background, contentColor = colorScheme.onBackground) {
|
||||||
Column(modifier = Modifier
|
Column(modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.fillMaxHeight()) {
|
.fillMaxHeight()) {
|
||||||
|
@ -123,14 +141,14 @@ open class MainActivity : ComponentActivity() {
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
if (chromeCasts.isNotEmpty())
|
if (chromeCasts.isNotEmpty())
|
||||||
Text(text = stringResource(id = R.string.available_chromecasts))
|
Text(text = stringResource(id = R.string.available_chromecasts),color =colorScheme.onBackground)
|
||||||
else {
|
else {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Text(stringResource(id = R.string.looking_for_devices))
|
Text(text = stringResource(id = R.string.looking_for_devices),color= colorScheme.onBackground)
|
||||||
CircularProgressIndicator()
|
CircularProgressIndicator(color = colorScheme.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,7 +170,7 @@ open class MainActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
AlertDialog(onDismissRequest = dismiss,
|
AlertDialog(onDismissRequest = dismiss,
|
||||||
title = {
|
title = {
|
||||||
Text(text = stringResource(id = R.string.add_chromecast))
|
Text(text = stringResource(id = R.string.add_chromecast),color= colorScheme.onBackground)
|
||||||
},
|
},
|
||||||
text = {
|
text = {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
|
@ -160,7 +178,7 @@ open class MainActivity : ComponentActivity() {
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
ipaddress = it
|
ipaddress = it
|
||||||
},
|
},
|
||||||
label = { Text(stringResource(id = R.string.ip_address)) },
|
label = { Text(stringResource(id = R.string.ip_address),color= colorScheme.onBackground) },
|
||||||
modifier = Modifier.padding(vertical = 4.dp)
|
modifier = Modifier.padding(vertical = 4.dp)
|
||||||
)
|
)
|
||||||
}, buttons = {
|
}, buttons = {
|
||||||
|
@ -173,11 +191,16 @@ open class MainActivity : ComponentActivity() {
|
||||||
add(ipaddress)
|
add(ipaddress)
|
||||||
dismiss()
|
dismiss()
|
||||||
},
|
},
|
||||||
modifier = Modifier.padding(all = 8.dp)
|
colors = ButtonDefaults.buttonColors(backgroundColor = colorScheme.primary, contentColor = colorScheme.onPrimary),
|
||||||
|
modifier = Modifier.padding(all = 8.dp),
|
||||||
|
|
||||||
) {
|
) {
|
||||||
Text(text = stringResource(id = R.string.add_chromecast))
|
Text(text = stringResource(id = R.string.add_chromecast))
|
||||||
}
|
}
|
||||||
Button(onClick = dismiss, modifier = Modifier.padding(all = 8.dp)) {
|
Button(
|
||||||
|
onClick = dismiss,
|
||||||
|
colors = ButtonDefaults.buttonColors(backgroundColor = colorScheme.primary, contentColor = colorScheme.onPrimary),
|
||||||
|
modifier = Modifier.padding(all = 8.dp)) {
|
||||||
Text(stringResource(id = R.string.cancel))
|
Text(stringResource(id = R.string.cancel))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,7 +244,9 @@ open class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Column(modifier = Modifier.fillMaxWidth().height(80.dp),
|
Column(modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(80.dp),
|
||||||
verticalArrangement = Arrangement.Center) {
|
verticalArrangement = Arrangement.Center) {
|
||||||
Text(
|
Text(
|
||||||
text = "Name: " + when {
|
text = "Name: " + when {
|
||||||
|
@ -265,7 +290,9 @@ open class MainActivity : ComponentActivity() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
backgroundColor = colorScheme.primary,
|
||||||
|
contentColor = colorScheme.onPrimary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.net.Uri
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import eu.toldi.balazs.caster.ChromeCastHelper
|
import eu.toldi.balazs.caster.ChromeCastHelper
|
||||||
|
@ -20,6 +21,9 @@ class ChromecastManageViewmodel : ViewModel() {
|
||||||
private val _mediaState = mutableStateOf<MediaStatus?>(null)
|
private val _mediaState = mutableStateOf<MediaStatus?>(null)
|
||||||
val mediaStatus: State<MediaStatus?>
|
val mediaStatus: State<MediaStatus?>
|
||||||
get() = _mediaState
|
get() = _mediaState
|
||||||
|
private val _folderListState = mutableStateOf<List<DocumentFile>>(listOf())
|
||||||
|
val folderListState : State<List<DocumentFile>>
|
||||||
|
get() = _folderListState
|
||||||
lateinit var chromeCast: ChromeCast
|
lateinit var chromeCast: ChromeCast
|
||||||
|
|
||||||
|
|
||||||
|
@ -107,6 +111,12 @@ class ChromecastManageViewmodel : ViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addFolder(folder: DocumentFile){
|
||||||
|
if(folder.isDirectory){
|
||||||
|
_folderListState.value += folder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getIPv4Address(): InetAddress? {
|
fun getIPv4Address(): InetAddress? {
|
||||||
NetworkInterface.getNetworkInterfaces().toList().forEach { interf ->
|
NetworkInterface.getNetworkInterfaces().toList().forEach { interf ->
|
||||||
interf.inetAddresses.toList().forEach { inetAddress ->
|
interf.inetAddresses.toList().forEach { inetAddress ->
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package eu.toldi.balazs.caster.model
|
||||||
|
|
||||||
|
import com.beust.klaxon.*
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
private val klaxon = Klaxon()
|
||||||
|
|
||||||
|
class SponsorBlock(elements: Collection<SponsorBlockElement>) : ArrayList<SponsorBlockElement>(elements) {
|
||||||
|
fun toJson() = klaxon.toJsonString(this)
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromJson(json: String) = SponsorBlock(klaxon.parseArray<SponsorBlockElement>(json)!!)
|
||||||
|
fun fromID(id: String) = SponsorBlock(klaxon.parseArray<SponsorBlockElement>(URL(url+id).readText())!!)
|
||||||
|
private const val url = "https://sponsor.ajay.app/api/skipSegments?videoID="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class SponsorBlockElement (
|
||||||
|
val category: String,
|
||||||
|
val actionType: String,
|
||||||
|
val segment: List<Double>,
|
||||||
|
|
||||||
|
@Json(name = "UUID")
|
||||||
|
val uuid: String,
|
||||||
|
|
||||||
|
val locked: Long,
|
||||||
|
val votes: Long,
|
||||||
|
val videoDuration: Double,
|
||||||
|
val userID: String,
|
||||||
|
val description: String
|
||||||
|
)
|
|
@ -4,6 +4,7 @@ import android.app.Notification
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.media.MediaPlayer
|
import android.media.MediaPlayer
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
@ -15,7 +16,9 @@ import androidx.annotation.RequiresApi
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import eu.toldi.balazs.caster.App.Companion.CHANNEL_ID
|
import eu.toldi.balazs.caster.App.Companion.CHANNEL_ID
|
||||||
import eu.toldi.balazs.caster.ChromeCastHelper
|
import eu.toldi.balazs.caster.ChromeCastHelper
|
||||||
|
import eu.toldi.balazs.caster.ChromecastManagerActivity
|
||||||
import eu.toldi.balazs.caster.R
|
import eu.toldi.balazs.caster.R
|
||||||
|
import eu.toldi.balazs.caster.model.SponsorBlock
|
||||||
import io.ktor.application.*
|
import io.ktor.application.*
|
||||||
import io.ktor.features.*
|
import io.ktor.features.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
|
@ -40,6 +43,7 @@ class ChromecastManagerService : Service() {
|
||||||
const val ACTION_PAUSE = "action_pause"
|
const val ACTION_PAUSE = "action_pause"
|
||||||
const val ACTION_REWIND = "action_rewind"
|
const val ACTION_REWIND = "action_rewind"
|
||||||
const val ACTION_FAST_FORWARD = "action_fast_foward"
|
const val ACTION_FAST_FORWARD = "action_fast_foward"
|
||||||
|
const val ACTION_SET_YT_ID = "action_set_yt_id"
|
||||||
const val ACTION_NEXT = "action_next"
|
const val ACTION_NEXT = "action_next"
|
||||||
const val ACTION_PREVIOUS = "action_previous"
|
const val ACTION_PREVIOUS = "action_previous"
|
||||||
const val ACTION_STOP = "action_stop"
|
const val ACTION_STOP = "action_stop"
|
||||||
|
@ -54,6 +58,7 @@ class ChromecastManagerService : Service() {
|
||||||
private lateinit var pendingIntent : PendingIntent
|
private lateinit var pendingIntent : PendingIntent
|
||||||
private var mediaStatus: MediaStatus? = null
|
private var mediaStatus: MediaStatus? = null
|
||||||
private var file : File? = null
|
private var file : File? = null
|
||||||
|
private var yt_video :YoutubeVideo? = null
|
||||||
|
|
||||||
override fun onBind(p0: Intent?): IBinder? = null
|
override fun onBind(p0: Intent?): IBinder? = null
|
||||||
private lateinit var chromeCast: ChromeCast
|
private lateinit var chromeCast: ChromeCast
|
||||||
|
@ -89,11 +94,25 @@ class ChromecastManagerService : Service() {
|
||||||
ACTION_PAUSE -> mController.transportControls.pause()
|
ACTION_PAUSE -> mController.transportControls.pause()
|
||||||
ACTION_FAST_FORWARD -> mController.transportControls.fastForward()
|
ACTION_FAST_FORWARD -> mController.transportControls.fastForward()
|
||||||
ACTION_REWIND -> mController.transportControls.rewind()
|
ACTION_REWIND -> mController.transportControls.rewind()
|
||||||
|
ACTION_SET_YT_ID -> setYTID(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
return START_REDELIVER_INTENT
|
return START_REDELIVER_INTENT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setYTID(intent: Intent) {
|
||||||
|
CoroutineScope(IO).launch {
|
||||||
|
try {
|
||||||
|
val id = intent.getStringExtra("yt_id")!!
|
||||||
|
val url = intent.getStringExtra("media_url")!!
|
||||||
|
yt_video = YoutubeVideo(id,url, SponsorBlock.fromID(id))
|
||||||
|
Log.e(null,yt_video.toString())
|
||||||
|
}catch (e: Exception) {
|
||||||
|
Log.e("CasterService",e.stackTraceToString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun initAction(intent: Intent?){
|
private fun initAction(intent: Intent?){
|
||||||
initMediaSessions()
|
initMediaSessions()
|
||||||
|
@ -104,39 +123,88 @@ class ChromecastManagerService : Service() {
|
||||||
Log.e(null, address)
|
Log.e(null, address)
|
||||||
chromeCast = ChromeCast(address)
|
chromeCast = ChromeCast(address)
|
||||||
chromeCast.name = name
|
chromeCast.name = name
|
||||||
|
ChromecastManagerActivity.chromeCast_ = chromeCast
|
||||||
val notificationIntent = Intent(this, ChromecastManagerService::class.java)
|
val notificationIntent = Intent(this, ChromecastManagerService::class.java)
|
||||||
pendingIntent =
|
pendingIntent =
|
||||||
PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_MUTABLE)
|
PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
|
||||||
val notification: Notification = NotificationCompat.Builder(this, CHANNEL_ID)
|
val notification: Notification = NotificationCompat.Builder(this, CHANNEL_ID)
|
||||||
.setContentTitle("Caster for ${chromeCast.name}@${address}")
|
.setContentTitle("Caster for ${chromeCast.name}@${address}")
|
||||||
.setSmallIcon(R.drawable.ic_caster_logo)
|
.setSmallIcon(R.drawable.ic_new_caster_monochrome_foreground)
|
||||||
.setContentIntent(pendingIntent).build()
|
.setContentIntent(pendingIntent).build()
|
||||||
|
|
||||||
startForeground(1, notification)
|
startForeground(1, notification)
|
||||||
CoroutineScope(IO).launch {
|
CoroutineScope(IO).launch {
|
||||||
|
var started = false
|
||||||
|
while (!started) {
|
||||||
|
try {
|
||||||
server.start(wait = true)
|
server.start(wait = true)
|
||||||
|
started = true
|
||||||
|
Thread.sleep(1000)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(null,e.stackTraceToString())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val prefs = getSharedPreferences(
|
||||||
|
"eu.toldi.Caster", Context.MODE_PRIVATE)
|
||||||
CoroutineScope(IO).launch {
|
CoroutineScope(IO).launch {
|
||||||
|
var errorCount = 0
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
ChromeCastHelper.chromeCast = chromeCast
|
ChromeCastHelper.chromeCast = chromeCast
|
||||||
var status = chromeCast.status
|
var status = chromeCast.status
|
||||||
Log.d(null, status.applications.toString())
|
Log.d(null, status.applications.toString())
|
||||||
|
if(status.runningApp.id == ChromeCastHelper.APP_BACKDROP)
|
||||||
|
errorCount++
|
||||||
val status_message = if (status.runningApp != null) {
|
val status_message = if (status.runningApp != null) {
|
||||||
ChromeCastHelper.getApplicationDisplayName(status.runningApp.id)
|
ChromeCastHelper.getApplicationDisplayName(status.runningApp.id)
|
||||||
} else {
|
} else {
|
||||||
"${chromeCast.name} is ready for casting"
|
"${chromeCast.name} is ready for casting"
|
||||||
}
|
}
|
||||||
mediaStatus = ChromeCastHelper.fetchMediaStatus()
|
mediaStatus = ChromeCastHelper.fetchMediaStatus()
|
||||||
|
Log.e("Caster_Service","Sb enabled: "+prefs.getBoolean("sponsorblock_enabled",false).toString()+ " yt: ${yt_video.toString()}")
|
||||||
|
if(prefs.getBoolean("sponsorblock_enabled",false) && yt_video != null){
|
||||||
|
try {
|
||||||
|
if(yt_video!!.url.startsWith("https://youtu.be/")){
|
||||||
|
yt_video!!.url = mediaStatus!!.media.url
|
||||||
|
}
|
||||||
|
if (mediaStatus!!.media.url != yt_video!!.url) {
|
||||||
|
yt_video = null
|
||||||
|
}
|
||||||
|
checkForSponsorBlock()
|
||||||
|
} catch (e:Exception){
|
||||||
|
//yt_video = null
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
yt_video = null
|
||||||
|
}
|
||||||
|
|
||||||
|
errorCount = 0
|
||||||
buildNotification()
|
buildNotification()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(null,e.stackTraceToString())
|
Log.e(null,e.stackTraceToString())
|
||||||
|
errorCount++
|
||||||
}
|
}
|
||||||
Thread.sleep(10500)
|
if(errorCount > 120){
|
||||||
|
stopForeground(true)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
Thread.sleep(1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkForSponsorBlock() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
|
||||||
|
yt_video!!.sponsorBlock.forEach {
|
||||||
|
if (mediaStatus!!.currentTime >= it.segment[0] && mediaStatus!!.currentTime <= it.segment[1])
|
||||||
|
chromeCast.seek(it.segment[1])
|
||||||
|
}
|
||||||
|
}catch (e: Exception) {
|
||||||
|
Log.e(null,e.stackTraceToString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +215,7 @@ class ChromecastManagerService : Service() {
|
||||||
): NotificationCompat.Action {
|
): NotificationCompat.Action {
|
||||||
val intent = Intent(applicationContext, ChromecastManagerService::class.java)
|
val intent = Intent(applicationContext, ChromecastManagerService::class.java)
|
||||||
intent.action = intentAction
|
intent.action = intentAction
|
||||||
val pendingIntent = PendingIntent.getService(applicationContext, 1, intent, 0)
|
val pendingIntent = PendingIntent.getService(applicationContext, 1, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||||
return NotificationCompat.Action(icon,title,pendingIntent)
|
return NotificationCompat.Action(icon,title,pendingIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,4 +381,10 @@ class ChromecastManagerService : Service() {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class YoutubeVideo(
|
||||||
|
val id: String,
|
||||||
|
var url: String,
|
||||||
|
val sponsorBlock: SponsorBlock
|
||||||
|
)
|
||||||
}
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package eu.toldi.balazs.caster.ui.theme
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun getColorScheme(): ColorScheme {
|
||||||
|
val LightColorScheme = lightColorScheme(
|
||||||
|
primary = MaterialTheme.colors.primary,
|
||||||
|
secondary = MaterialTheme.colors.secondary,
|
||||||
|
tertiary = MaterialTheme.colors.secondaryVariant,
|
||||||
|
// error, primaryContainer, onSecondary, etc.
|
||||||
|
)
|
||||||
|
val DarkColorScheme = darkColorScheme(
|
||||||
|
primary = MaterialTheme.colors.primary,
|
||||||
|
secondary = MaterialTheme.colors.secondary,
|
||||||
|
tertiary = MaterialTheme.colors.secondaryVariant,
|
||||||
|
// error, primaryContainer, onSecondary, etc.
|
||||||
|
)
|
||||||
|
|
||||||
|
val darkTheme = isSystemInDarkTheme()
|
||||||
|
|
||||||
|
val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
||||||
|
return when {
|
||||||
|
dynamicColor && darkTheme -> dynamicDarkColorScheme(LocalContext.current)
|
||||||
|
dynamicColor && !darkTheme -> dynamicLightColorScheme(LocalContext.current)
|
||||||
|
darkTheme -> DarkColorScheme
|
||||||
|
else -> LightColorScheme
|
||||||
|
}
|
||||||
|
}
|
53
app/src/main/res/drawable/ic_new_caster_foreground.xml
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<group android:scaleX="0.101828575"
|
||||||
|
android:scaleY="0.101828575"
|
||||||
|
android:translateX="27.835714"
|
||||||
|
android:translateY="29.485714">
|
||||||
|
<path
|
||||||
|
android:pathData="M125.67,37.64h361.61v220.93h-361.61z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="20"
|
||||||
|
android:fillColor="#ffe680"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M217.94,298.36h177.75v54.12h-177.75z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="20"
|
||||||
|
android:fillColor="#b3b3b3"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M282.15,261.97h48.97v36.25h-48.97z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="20"
|
||||||
|
android:fillColor="#999999"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m27.62,354.13a63.86,63.86 0,0 1,82.15 3.48,63.86 63.86,0 0,1 10.64,81.53"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="25"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m34.3,401.3a27.68,30.75 90,0 1,39.56 1.51,27.68 30.75,90 0,1 5.12,35.34"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="25"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m31.57,307.13a97.94,97.94 45,0 1,125.99 5.33,97.94 97.94,0 0,1 16.32,125.04"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="25"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
|
@ -0,0 +1,53 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<group android:scaleX="0.101828575"
|
||||||
|
android:scaleY="0.101828575"
|
||||||
|
android:translateX="27.835714"
|
||||||
|
android:translateY="29.485714">
|
||||||
|
<path
|
||||||
|
android:pathData="M125.67,37.64h361.61v220.93h-361.61z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="20"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M217.94,298.36h177.75v54.12h-177.75z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="20"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M282.15,261.97h48.97v36.25h-48.97z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="20"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m27.62,354.13a63.86,63.86 0,0 1,82.15 3.48,63.86 63.86,0 0,1 10.64,81.53"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="25"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m34.3,401.3a27.68,30.75 90,0 1,39.56 1.51,27.68 30.75,90 0,1 5.12,35.34"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="25"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m31.57,307.13a97.94,97.94 45,0 1,125.99 5.33,97.94 97.94,0 0,1 16.32,125.04"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="25"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
|
@ -1,5 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
6
app/src/main/res/mipmap-anydpi-v26/ic_new_caster.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_new_caster_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_new_caster_foreground"/>
|
||||||
|
<monochrome android:drawable="@drawable/ic_new_caster_monochrome_foreground" />
|
||||||
|
</adaptive-icon>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_new_caster_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_new_caster_foreground"/>
|
||||||
|
<monochrome android:drawable="@drawable/ic_new_caster_monochrome_foreground" />
|
||||||
|
</adaptive-icon>
|
BIN
app/src/main/res/mipmap-hdpi/ic_new_caster.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_new_caster_round.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_new_caster.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_new_caster_round.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_new_caster.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_new_caster_round.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_new_caster.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_new_caster_round.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_new_caster.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_new_caster_round.png
Normal file
After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_adaptive_background">#AAD400</color>
|
||||||
|
</resources>
|
4
app/src/main/res/values/ic_new_caster_background.xml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_new_caster_background">#AAD400</color>
|
||||||
|
</resources>
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_new_caster_monochrome_background">#AAD400</color>
|
||||||
|
</resources>
|
|
@ -18,5 +18,6 @@
|
||||||
<string name="back">Back</string>
|
<string name="back">Back</string>
|
||||||
<string name="now_playing">Now playing</string>
|
<string name="now_playing">Now playing</string>
|
||||||
<string name="cast_url">Cast URL</string>
|
<string name="cast_url">Cast URL</string>
|
||||||
|
<string name="title_activity_folder_view">FolderViewActivity</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|