.
15
.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/caches
|
||||||
|
/.idea/libraries
|
||||||
|
/.idea/modules.xml
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/navEditor.xml
|
||||||
|
/.idea/assetWizardSettings.xml
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
local.properties
|
3
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
6
.idea/compiler.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel target="11" />
|
||||||
|
</component>
|
||||||
|
</project>
|
19
.idea/gradle.xml
generated
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="testRunner" value="GRADLE" />
|
||||||
|
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
52
.idea/misc.xml
generated
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DesignSurface">
|
||||||
|
<option name="filePathToZoomLevelMap">
|
||||||
|
<map>
|
||||||
|
<entry key="..\:/code/VlcJellyfin/app/src/main/res/drawable/ic_outline_play_circle_outline_128.xml" value="0.1555" />
|
||||||
|
<entry key="..\:/code/VlcJellyfin/app/src/main/res/drawable/played_percentage.xml" value="0.1765" />
|
||||||
|
<entry key="..\:/code/VlcJellyfin/app/src/main/res/drawable/popmenu_focus.xml" value="0.1555" />
|
||||||
|
<entry key="..\:/code/VlcJellyfin/app/src/main/res/drawable/shape_user_focus_vholder.xml" value="0.1555" />
|
||||||
|
<entry key="..\:/code/VlcJellyfin/app/src/main/res/layout/activity_detail.xml" value="0.14583333333333334" />
|
||||||
|
<entry key="..\:/code/VlcJellyfin/app/src/main/res/layout/activity_search.xml" value="0.14583333333333334" />
|
||||||
|
<entry key="..\:/code/VlcJellyfin/app/src/main/res/layout/activity_vlc_player.xml" value="0.286231884057971" />
|
||||||
|
<entry key="..\:/code/VlcJellyfin/app/src/main/res/layout/dialog_login.xml" value="0.14479166666666668" />
|
||||||
|
<entry key="..\:/code/VlcJellyfin/app/src/main/res/layout/item_h.xml" value="0.16770833333333332" />
|
||||||
|
<entry key="..\:/code/VlcJellyfin/app/src/main/res/layout/item_v.xml" value="0.14583333333333334" />
|
||||||
|
<entry key="..\:/code/VlcJellyfin/app/src/main/res/layout/popmenu.xml" value="0.14479166666666668" />
|
||||||
|
<entry key="..\:/code/VlcJellyfin/app/src/main/res/layout/popmenu_item.xml" value="0.14479166666666668" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/drawable/img_loading_placeholder.xml" value="0.1625" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/drawable/played_percentage.xml" value="0.1615" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/drawable/popmenu_focus.xml" value="0.1625" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/drawable/shape_user_focus.xml" value="0.1625" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/drawable/shape_user_focus_VH.xml" value="0.1625" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/drawable/shape_user_focus_vholder.xml" value="0.1625" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/activebar_custom.xml" value="0.14895833333333333" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/activity_collection.xml" value="0.14895833333333333" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/activity_detail.xml" value="0.1" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/activity_main.xml" value="0.2957427536231884" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/activity_search.xml" value="0.14791666666666667" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/activity_vlc_player.xml" value="0.14895833333333333" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/dialog_login.xml" value="0.4" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/home_horizon_tvrecycler.xml" value="0.14791666666666667" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/item_h.xml" value="0.14895833333333333" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/item_title.xml" value="0.14895833333333333" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/item_v.xml" value="0.14895833333333333" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/jellyfin_home.xml" value="0.14895833333333333" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/loading.xml" value="0.2957427536231884" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/loading_alert.xml" value="0.14895833333333333" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/menu_item.xml" value="0.2957427536231884" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/popmenu.xml" value="0.14895833333333333" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/layout/popmenu_item.xml" value="0.14895833333333333" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/menu/activebar_menu.xml" value="0.14895833333333333" />
|
||||||
|
<entry key="..\:/work/VlcJellyfin/app/src/main/res/menu/activebar_sort_menu.xml" value="0.14895833333333333" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
5
README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
jellyfin安卓TV客户端,支持安卓4.2以上。
|
||||||
|
|
||||||
|
在安卓5.1及以下环境中运行时,集成的VLC播放器可能无法播放HTTPS,可在右上角菜单勾选"调用外部播放器"选项,使用外部播放器播放。
|
||||||
|
|
||||||
|
支持jellyfin8.1以上
|
1
app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
53
app/build.gradle
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
plugins {
|
||||||
|
id 'com.android.application'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdk 32
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "org.sifacai.vlcjellyfin"
|
||||||
|
minSdk 17
|
||||||
|
targetSdk 32
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0.5"
|
||||||
|
|
||||||
|
multiDexEnabled true
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
|
||||||
|
splits {
|
||||||
|
abi {
|
||||||
|
enable true
|
||||||
|
include 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
//implementation files('libs/libvlc-all-3.4.9.aar')
|
||||||
|
implementation 'org.videolan.android:libvlc-all:3.5.1'
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.5.0'
|
||||||
|
implementation 'com.google.android.material:material:1.6.1'
|
||||||
|
implementation 'com.lzy.net:okgo:3.0.4'
|
||||||
|
testImplementation 'junit:junit:4.13.2'
|
||||||
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||||
|
implementation 'com.google.code.gson:gson:2.9.1'
|
||||||
|
implementation 'com.squareup.okhttp3:okhttp:3.12.13'
|
||||||
|
implementation 'com.owen:tv-recyclerview:3.0.0'
|
||||||
|
implementation 'com.squareup.picasso:picasso:2.8'
|
||||||
|
implementation 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1'
|
||||||
|
}
|
21
app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
@ -0,0 +1,26 @@
|
|||||||
|
package org.sifacai.vlcjellyfin;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
public void useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||||
|
assertEquals("org.sifacai.vlcjellyfin", appContext.getPackageName());
|
||||||
|
}
|
||||||
|
}
|
53
app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="org.sifacai.vlcjellyfin">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
|
android:icon="@drawable/touchicon144"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@drawable/touchicon144"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/jellyfin"
|
||||||
|
android:usesCleartextTraffic="true"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
tools:targetApi="31">
|
||||||
|
<activity
|
||||||
|
android:name=".HomeActivity"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".DetailActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
<activity
|
||||||
|
android:name=".CollectionActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
<activity
|
||||||
|
android:name=".SearchActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
<activity
|
||||||
|
android:name=".VlcPlayerActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/Theme.AppCompat.NoActionBar"/>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="design_width_in_dp"
|
||||||
|
android:value="1280" />
|
||||||
|
<meta-data
|
||||||
|
android:name="design_height_in_dp"
|
||||||
|
android:value="720" />
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
236
app/src/main/java/org/sifacai/vlcjellyfin/BaseActivity.java
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
package org.sifacai.vlcjellyfin;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.PopupMenu;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import me.jessyan.autosize.internal.CustomAdapt;
|
||||||
|
|
||||||
|
public class BaseActivity extends AppCompatActivity implements CustomAdapt {
|
||||||
|
public AppCompatActivity mAA = this;
|
||||||
|
private ProgressDialog progressDialog;
|
||||||
|
private ImageView activeBarBack;
|
||||||
|
private ImageView actionBarSetBtn;
|
||||||
|
private PopupMenu settingMenu;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
if (null != actionBar) {
|
||||||
|
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
|
||||||
|
actionBar.setCustomView(R.layout.activebar_custom);
|
||||||
|
|
||||||
|
activeBarBack = findViewById(R.id.actionBar_back);
|
||||||
|
activeBarBack.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
mAA.finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ImageView activeBarSearch = findViewById(R.id.actionBar_searchBtn);
|
||||||
|
activeBarSearch.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
Intent intent = new Intent(mAA,SearchActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//actionBar设置菜单
|
||||||
|
actionBarSetBtn = findViewById(R.id.actionBar_setBtn);
|
||||||
|
actionBarSetBtn.setOnClickListener(actionBarSetBtnOnclick);
|
||||||
|
settingMenu = new PopupMenu(this,actionBarSetBtn);
|
||||||
|
settingMenu.getMenuInflater().inflate(R.menu.activebar_menu,settingMenu.getMenu());
|
||||||
|
settingMenu.setOnMenuItemClickListener(settingMenuItemOnclick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private View.OnClickListener actionBarSetBtnOnclick = new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
settingMenu.show();
|
||||||
|
Menu setmenu = settingMenu.getMenu();
|
||||||
|
setmenu.findItem(R.id.actionBar_option_PlayStartInBegin).setChecked(JfClient.config.isPlayStartInBegin());
|
||||||
|
setmenu.findItem(R.id.actionBar_option_HAACC).setChecked(JfClient.config.isHAACC());
|
||||||
|
setmenu.findItem(R.id.actionBar_option_FORCE_HAACC).setChecked(JfClient.config.isFORCE_HAACC());
|
||||||
|
setmenu.findItem(R.id.actionBar_option_ExtensionPlayer).setChecked(JfClient.config.isExtensionPlayer());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private PopupMenu.OnMenuItemClickListener settingMenuItemOnclick = new PopupMenu.OnMenuItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||||
|
int menuid = menuItem.getItemId();
|
||||||
|
switch (menuid){
|
||||||
|
case R.id.activeBar_option_logout:
|
||||||
|
logout();
|
||||||
|
break;
|
||||||
|
case R.id.actionBar_option_HAACC:
|
||||||
|
JfClient.config.setHAACC(!JfClient.config.isHAACC());
|
||||||
|
break;
|
||||||
|
case R.id.actionBar_option_FORCE_HAACC:
|
||||||
|
JfClient.config.setFORCE_HAACC(!JfClient.config.isFORCE_HAACC());
|
||||||
|
break;
|
||||||
|
case R.id.actionBar_option_PlayStartInBegin:
|
||||||
|
JfClient.config.setPlayStartInBegin(!JfClient.config.isPlayStartInBegin());
|
||||||
|
break;
|
||||||
|
case R.id.actionBar_option_ExtensionPlayer:
|
||||||
|
JfClient.config.setExtensionPlayer(!JfClient.config.isExtensionPlayer());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public boolean onCreateOptionsMenu(@NonNull Menu menu) {
|
||||||
|
// getMenuInflater().inflate(R.menu.activebar_menu, menu);
|
||||||
|
// return super.onCreateOptionsMenu(menu);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
// if (item.getItemId() == R.id.activeBar_option_logout) {
|
||||||
|
// logout();
|
||||||
|
// }
|
||||||
|
// return super.onOptionsItemSelected(item);
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 禁用标题栏返回按钮
|
||||||
|
*/
|
||||||
|
public void disableActiveBarBack() {
|
||||||
|
if(null != activeBarBack)
|
||||||
|
activeBarBack.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否按照宽度进行等比例适配 (为了保证在高宽比不同的屏幕上也能正常适配, 所以只能在宽度和高度之中选择一个作为基准进行适配)
|
||||||
|
*
|
||||||
|
* @return {@code true} 为按照宽度进行适配, {@code false} 为按照高度进行适配
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isBaseOnWidth() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回设计图上的设计尺寸
|
||||||
|
* {@link #getSizeInDp} 须配合 {@link #isBaseOnWidth()} 使用, 规则如下:
|
||||||
|
* 如果 {@link #isBaseOnWidth()} 返回 {@code true}, {@link #getSizeInDp} 则应该返回设计图的总宽度
|
||||||
|
* 如果 {@link #isBaseOnWidth()} 返回 {@code false}, {@link #getSizeInDp} 则应该返回设计图的总高度
|
||||||
|
* 如果您不需要自定义设计图上的设计尺寸, 想继续使用在 AndroidManifest 中填写的设计图尺寸, {@link #getSizeInDp} 则返回 {@code 0}
|
||||||
|
*
|
||||||
|
* @return 设计图上的设计尺寸
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public float getSizeInDp() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showLoadingDialog() {
|
||||||
|
showLoadingDialog("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示加载动画框
|
||||||
|
*/
|
||||||
|
public void showLoadingDialog(String title) {
|
||||||
|
mAA.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if(null != progressDialog){
|
||||||
|
if(progressDialog.isShowing()){
|
||||||
|
progressDialog.dismiss();
|
||||||
|
}
|
||||||
|
progressDialog = null;
|
||||||
|
}
|
||||||
|
progressDialog = new ProgressDialog(mAA);
|
||||||
|
progressDialog.setMessage(title);
|
||||||
|
progressDialog.show();
|
||||||
|
progressDialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onKey(DialogInterface dialogInterface, int i, KeyEvent keyEvent) {
|
||||||
|
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
||||||
|
dismissLoadingDialog();
|
||||||
|
mAA.finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 隐藏加载框
|
||||||
|
*/
|
||||||
|
public void dismissLoadingDialog() {
|
||||||
|
mAA.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (null != progressDialog && progressDialog.isShowing()) {
|
||||||
|
progressDialog.dismiss();
|
||||||
|
progressDialog = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置加载框文字
|
||||||
|
*
|
||||||
|
* @param text
|
||||||
|
*/
|
||||||
|
public void setLoadingText(String text) {
|
||||||
|
mAA.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (null != progressDialog && progressDialog.isShowing()) {
|
||||||
|
progressDialog.setMessage(text);
|
||||||
|
}else{
|
||||||
|
showLoadingDialog(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowToask(String msg) {
|
||||||
|
mAA.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(mAA, msg, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登 出
|
||||||
|
*/
|
||||||
|
private void logout() {
|
||||||
|
JfClient.config.clear();
|
||||||
|
JfClient.UserId = "";
|
||||||
|
JfClient.AccessToken = "";
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
39
app/src/main/java/org/sifacai/vlcjellyfin/Bean/Chapters.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2022 json.cn
|
||||||
|
*/
|
||||||
|
package org.sifacai.vlcjellyfin.Bean;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated: 2022-09-01 14:6:30
|
||||||
|
*
|
||||||
|
* @author json.cn (i@json.cn)
|
||||||
|
* @website http://www.json.cn/java2pojo/
|
||||||
|
*/
|
||||||
|
public class Chapters {
|
||||||
|
|
||||||
|
private long StartPositionTicks;
|
||||||
|
private String Name;
|
||||||
|
private String ImageDateModified;
|
||||||
|
public void setStartPositionTicks(long StartPositionTicks) {
|
||||||
|
this.StartPositionTicks = StartPositionTicks;
|
||||||
|
}
|
||||||
|
public long getStartPositionTicks() {
|
||||||
|
return StartPositionTicks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String Name) {
|
||||||
|
this.Name = Name;
|
||||||
|
}
|
||||||
|
public String getName() {
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageDateModified(String ImageDateModified) {
|
||||||
|
this.ImageDateModified = ImageDateModified;
|
||||||
|
}
|
||||||
|
public String getImageDateModified() {
|
||||||
|
return ImageDateModified;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2022 json.cn
|
||||||
|
*/
|
||||||
|
package org.sifacai.vlcjellyfin.Bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated: 2022-09-01 14:6:30
|
||||||
|
*
|
||||||
|
* @author json.cn (i@json.cn)
|
||||||
|
* @website http://www.json.cn/java2pojo/
|
||||||
|
*/
|
||||||
|
public class ExternalUrls {
|
||||||
|
|
||||||
|
private String Name;
|
||||||
|
private String Url;
|
||||||
|
public void setName(String Name) {
|
||||||
|
this.Name = Name;
|
||||||
|
}
|
||||||
|
public String getName() {
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(String Url) {
|
||||||
|
this.Url = Url;
|
||||||
|
}
|
||||||
|
public String getUrl() {
|
||||||
|
return Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2022 json.cn
|
||||||
|
*/
|
||||||
|
package org.sifacai.vlcjellyfin.Bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated: 2022-09-01 14:6:30
|
||||||
|
*
|
||||||
|
* @author json.cn (i@json.cn)
|
||||||
|
* @website http://www.json.cn/java2pojo/
|
||||||
|
*/
|
||||||
|
public class GenreItems {
|
||||||
|
|
||||||
|
private String Name;
|
||||||
|
private String Id;
|
||||||
|
public void setName(String Name) {
|
||||||
|
this.Name = Name;
|
||||||
|
}
|
||||||
|
public String getName() {
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String Id) {
|
||||||
|
this.Id = Id;
|
||||||
|
}
|
||||||
|
public String getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2022 json.cn
|
||||||
|
*/
|
||||||
|
package org.sifacai.vlcjellyfin.Bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated: 2022-09-01 14:6:30
|
||||||
|
*
|
||||||
|
* @author json.cn (i@json.cn)
|
||||||
|
* @website http://www.json.cn/java2pojo/
|
||||||
|
*/
|
||||||
|
public class ImageTags {
|
||||||
|
|
||||||
|
private String Primary;
|
||||||
|
private String Logo;
|
||||||
|
private String Thumb;
|
||||||
|
public void setPrimary(String Primary) {
|
||||||
|
this.Primary = Primary;
|
||||||
|
}
|
||||||
|
public String getPrimary() {
|
||||||
|
return Primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogo(String Logo) {
|
||||||
|
this.Logo = Logo;
|
||||||
|
}
|
||||||
|
public String getLogo() {
|
||||||
|
return Logo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThumb(String thumb) {
|
||||||
|
Thumb = thumb;
|
||||||
|
}
|
||||||
|
public String getThumb() {
|
||||||
|
return Thumb;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
502
app/src/main/java/org/sifacai/vlcjellyfin/Bean/Item.java
Normal file
@ -0,0 +1,502 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2022 json.cn
|
||||||
|
*/
|
||||||
|
package org.sifacai.vlcjellyfin.Bean;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated: 2022-09-01 14:6:30
|
||||||
|
*
|
||||||
|
* @author json.cn (i@json.cn)
|
||||||
|
* @website http://www.json.cn/java2pojo/
|
||||||
|
*/
|
||||||
|
public class Item {
|
||||||
|
|
||||||
|
private String Name;
|
||||||
|
private String OriginalTitle;
|
||||||
|
private String ServerId;
|
||||||
|
private String Id;
|
||||||
|
private String Etag;
|
||||||
|
private Date DateCreated;
|
||||||
|
private boolean CanDelete;
|
||||||
|
private boolean CanDownload;
|
||||||
|
private boolean HasSubtitles;
|
||||||
|
private String Container;
|
||||||
|
private String SortName;
|
||||||
|
private String PremiereDate;
|
||||||
|
private List<ExternalUrls> ExternalUrls;
|
||||||
|
private List<MediaSources> MediaSources;
|
||||||
|
private List<String> ProductionLocations;
|
||||||
|
private String Path;
|
||||||
|
private boolean EnableMediaSourceDisplay;
|
||||||
|
private String OfficialRating;
|
||||||
|
private String ChannelId;
|
||||||
|
private String Overview;
|
||||||
|
private List<String> Taglines;
|
||||||
|
private List<String> Genres;
|
||||||
|
private String CommunityRating;
|
||||||
|
private long RunTimeTicks;
|
||||||
|
private String PlayAccess;
|
||||||
|
private String ProductionYear;
|
||||||
|
// private List<String> RemoteTrailers;
|
||||||
|
private ProviderIds ProviderIds;
|
||||||
|
private boolean IsHD;
|
||||||
|
private boolean IsFolder;
|
||||||
|
private String ParentId;
|
||||||
|
private String Type;
|
||||||
|
private List<People> People;
|
||||||
|
private List<Studios> Studios;
|
||||||
|
private List<GenreItems> GenreItems;
|
||||||
|
private int LocalTrailerCount;
|
||||||
|
private UserData UserData;
|
||||||
|
private int SpecialFeatureCount;
|
||||||
|
private String DisplayPreferencesId;
|
||||||
|
private List<String> Tags;
|
||||||
|
private double PrimaryImageAspectRatio;
|
||||||
|
private List<MediaStreams> MediaStreams;
|
||||||
|
private String VideoType;
|
||||||
|
private ImageTags ImageTags;
|
||||||
|
private List<String> BackdropImageTags;
|
||||||
|
private List<Chapters> Chapters;
|
||||||
|
private String LocationType;
|
||||||
|
private String MediaType;
|
||||||
|
private List<String> LockedFields;
|
||||||
|
private boolean LockData;
|
||||||
|
private int Width;
|
||||||
|
private int Height;
|
||||||
|
|
||||||
|
public int getPartCount() {
|
||||||
|
return PartCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPartCount(int partCount) {
|
||||||
|
PartCount = partCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int PartCount;
|
||||||
|
private String SeriesId;
|
||||||
|
private String SeriesName;
|
||||||
|
private String SeasonId;
|
||||||
|
//private List<String> ProductionLocations;
|
||||||
|
//private
|
||||||
|
|
||||||
|
public String getSeriesId() {
|
||||||
|
return SeriesId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSeriesId(String seriesId) {
|
||||||
|
SeriesId = seriesId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSeasonId() {
|
||||||
|
return SeasonId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSeasonId(String seasonId) {
|
||||||
|
SeasonId = seasonId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String SeasonName;
|
||||||
|
|
||||||
|
public boolean isHasSubtitles() {
|
||||||
|
return HasSubtitles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHD() {
|
||||||
|
return IsHD;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHD(boolean HD) {
|
||||||
|
IsHD = HD;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFolder() {
|
||||||
|
return IsFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFolder(boolean folder) {
|
||||||
|
IsFolder = folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSeriesName() {
|
||||||
|
return SeriesName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSeriesName(String seriesName) {
|
||||||
|
SeriesName = seriesName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSeasonName() {
|
||||||
|
return SeasonName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSeasonName(String seasonName) {
|
||||||
|
SeasonName = seasonName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String Name) {
|
||||||
|
this.Name = Name;
|
||||||
|
}
|
||||||
|
public String getName() {
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOriginalTitle(String OriginalTitle) {
|
||||||
|
this.OriginalTitle = OriginalTitle;
|
||||||
|
}
|
||||||
|
public String getOriginalTitle() {
|
||||||
|
return OriginalTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerId(String ServerId) {
|
||||||
|
this.ServerId = ServerId;
|
||||||
|
}
|
||||||
|
public String getServerId() {
|
||||||
|
return ServerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String Id) {
|
||||||
|
this.Id = Id;
|
||||||
|
}
|
||||||
|
public String getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEtag(String Etag) {
|
||||||
|
this.Etag = Etag;
|
||||||
|
}
|
||||||
|
public String getEtag() {
|
||||||
|
return Etag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDateCreated(Date DateCreated) {
|
||||||
|
this.DateCreated = DateCreated;
|
||||||
|
}
|
||||||
|
public Date getDateCreated() {
|
||||||
|
return DateCreated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCanDelete(boolean CanDelete) {
|
||||||
|
this.CanDelete = CanDelete;
|
||||||
|
}
|
||||||
|
public boolean getCanDelete() {
|
||||||
|
return CanDelete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCanDownload(boolean CanDownload) {
|
||||||
|
this.CanDownload = CanDownload;
|
||||||
|
}
|
||||||
|
public boolean getCanDownload() {
|
||||||
|
return CanDownload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasSubtitles(boolean HasSubtitles) {
|
||||||
|
this.HasSubtitles = HasSubtitles;
|
||||||
|
}
|
||||||
|
public boolean getHasSubtitles() {
|
||||||
|
return HasSubtitles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContainer(String Container) {
|
||||||
|
this.Container = Container;
|
||||||
|
}
|
||||||
|
public String getContainer() {
|
||||||
|
return Container;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSortName(String SortName) {
|
||||||
|
this.SortName = SortName;
|
||||||
|
}
|
||||||
|
public String getSortName() {
|
||||||
|
return SortName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPremiereDate(String PremiereDate) {
|
||||||
|
this.PremiereDate = PremiereDate;
|
||||||
|
}
|
||||||
|
public String getPremiereDate() {
|
||||||
|
return PremiereDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExternalUrls(List<ExternalUrls> ExternalUrls) {
|
||||||
|
this.ExternalUrls = ExternalUrls;
|
||||||
|
}
|
||||||
|
public List<ExternalUrls> getExternalUrls() {
|
||||||
|
return ExternalUrls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMediaSources(List<MediaSources> MediaSources) {
|
||||||
|
this.MediaSources = MediaSources;
|
||||||
|
}
|
||||||
|
public List<MediaSources> getMediaSources() {
|
||||||
|
return MediaSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProductionLocations(List<String> ProductionLocations) {
|
||||||
|
this.ProductionLocations = ProductionLocations;
|
||||||
|
}
|
||||||
|
public List<String> getProductionLocations() {
|
||||||
|
return ProductionLocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPath(String Path) {
|
||||||
|
this.Path = Path;
|
||||||
|
}
|
||||||
|
public String getPath() {
|
||||||
|
return Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnableMediaSourceDisplay(boolean EnableMediaSourceDisplay) {
|
||||||
|
this.EnableMediaSourceDisplay = EnableMediaSourceDisplay;
|
||||||
|
}
|
||||||
|
public boolean getEnableMediaSourceDisplay() {
|
||||||
|
return EnableMediaSourceDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfficialRating(String OfficialRating) {
|
||||||
|
this.OfficialRating = OfficialRating;
|
||||||
|
}
|
||||||
|
public String getOfficialRating() {
|
||||||
|
return OfficialRating;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChannelId(String ChannelId) {
|
||||||
|
this.ChannelId = ChannelId;
|
||||||
|
}
|
||||||
|
public String getChannelId() {
|
||||||
|
return ChannelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOverview(String Overview) {
|
||||||
|
this.Overview = Overview;
|
||||||
|
}
|
||||||
|
public String getOverview() {
|
||||||
|
return Overview;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaglines(List<String> Taglines) {
|
||||||
|
this.Taglines = Taglines;
|
||||||
|
}
|
||||||
|
public List<String> getTaglines() {
|
||||||
|
return Taglines;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGenres(List<String> Genres) {
|
||||||
|
this.Genres = Genres;
|
||||||
|
}
|
||||||
|
public List<String> getGenres() {
|
||||||
|
return Genres;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCommunityRating(String CommunityRating) {
|
||||||
|
this.CommunityRating = CommunityRating;
|
||||||
|
}
|
||||||
|
public String getCommunityRating() {
|
||||||
|
return CommunityRating;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRunTimeTicks(long RunTimeTicks) {
|
||||||
|
this.RunTimeTicks = RunTimeTicks;
|
||||||
|
}
|
||||||
|
public long getRunTimeTicks() {
|
||||||
|
return RunTimeTicks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlayAccess(String PlayAccess) {
|
||||||
|
this.PlayAccess = PlayAccess;
|
||||||
|
}
|
||||||
|
public String getPlayAccess() {
|
||||||
|
return PlayAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProductionYear(String ProductionYear) {
|
||||||
|
this.ProductionYear = ProductionYear;
|
||||||
|
}
|
||||||
|
public String getProductionYear() {
|
||||||
|
return ProductionYear;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public void setRemoteTrailers(List<String> RemoteTrailers) {
|
||||||
|
// this.RemoteTrailers = RemoteTrailers;
|
||||||
|
// }
|
||||||
|
// public List<String> getRemoteTrailers() {
|
||||||
|
// return RemoteTrailers;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public void setProviderIds(ProviderIds ProviderIds) {
|
||||||
|
this.ProviderIds = ProviderIds;
|
||||||
|
}
|
||||||
|
public ProviderIds getProviderIds() {
|
||||||
|
return ProviderIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsHD(boolean IsHD) {
|
||||||
|
this.IsHD = IsHD;
|
||||||
|
}
|
||||||
|
public boolean getIsHD() {
|
||||||
|
return IsHD;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsFolder(boolean IsFolder) {
|
||||||
|
this.IsFolder = IsFolder;
|
||||||
|
}
|
||||||
|
public boolean getIsFolder() {
|
||||||
|
return IsFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParentId(String ParentId) {
|
||||||
|
this.ParentId = ParentId;
|
||||||
|
}
|
||||||
|
public String getParentId() {
|
||||||
|
return ParentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String Type) {
|
||||||
|
this.Type = Type;
|
||||||
|
}
|
||||||
|
public String getType() {
|
||||||
|
return Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPeople(List<People> People) {
|
||||||
|
this.People = People;
|
||||||
|
}
|
||||||
|
public List<People> getPeople() {
|
||||||
|
return People;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStudios(List<Studios> Studios) {
|
||||||
|
this.Studios = Studios;
|
||||||
|
}
|
||||||
|
public List<Studios> getStudios() {
|
||||||
|
return Studios;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGenreItems(List<GenreItems> GenreItems) {
|
||||||
|
this.GenreItems = GenreItems;
|
||||||
|
}
|
||||||
|
public List<GenreItems> getGenreItems() {
|
||||||
|
return GenreItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocalTrailerCount(int LocalTrailerCount) {
|
||||||
|
this.LocalTrailerCount = LocalTrailerCount;
|
||||||
|
}
|
||||||
|
public int getLocalTrailerCount() {
|
||||||
|
return LocalTrailerCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserData(UserData UserData) {
|
||||||
|
this.UserData = UserData;
|
||||||
|
}
|
||||||
|
public UserData getUserData() {
|
||||||
|
return UserData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpecialFeatureCount(int SpecialFeatureCount) {
|
||||||
|
this.SpecialFeatureCount = SpecialFeatureCount;
|
||||||
|
}
|
||||||
|
public int getSpecialFeatureCount() {
|
||||||
|
return SpecialFeatureCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisplayPreferencesId(String DisplayPreferencesId) {
|
||||||
|
this.DisplayPreferencesId = DisplayPreferencesId;
|
||||||
|
}
|
||||||
|
public String getDisplayPreferencesId() {
|
||||||
|
return DisplayPreferencesId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTags(List<String> Tags) {
|
||||||
|
this.Tags = Tags;
|
||||||
|
}
|
||||||
|
public List<String> getTags() {
|
||||||
|
return Tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrimaryImageAspectRatio(double PrimaryImageAspectRatio) {
|
||||||
|
this.PrimaryImageAspectRatio = PrimaryImageAspectRatio;
|
||||||
|
}
|
||||||
|
public double getPrimaryImageAspectRatio() {
|
||||||
|
return PrimaryImageAspectRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMediaStreams(List<MediaStreams> MediaStreams) {
|
||||||
|
this.MediaStreams = MediaStreams;
|
||||||
|
}
|
||||||
|
public List<MediaStreams> getMediaStreams() {
|
||||||
|
return MediaStreams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoType(String VideoType) {
|
||||||
|
this.VideoType = VideoType;
|
||||||
|
}
|
||||||
|
public String getVideoType() {
|
||||||
|
return VideoType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageTags(ImageTags ImageTags) {
|
||||||
|
this.ImageTags = ImageTags;
|
||||||
|
}
|
||||||
|
public ImageTags getImageTags() {
|
||||||
|
return ImageTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBackdropImageTags(List<String> BackdropImageTags) {
|
||||||
|
this.BackdropImageTags = BackdropImageTags;
|
||||||
|
}
|
||||||
|
public List<String> getBackdropImageTags() {
|
||||||
|
return BackdropImageTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChapters(List<Chapters> Chapters) {
|
||||||
|
this.Chapters = Chapters;
|
||||||
|
}
|
||||||
|
public List<Chapters> getChapters() {
|
||||||
|
return Chapters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocationType(String LocationType) {
|
||||||
|
this.LocationType = LocationType;
|
||||||
|
}
|
||||||
|
public String getLocationType() {
|
||||||
|
return LocationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMediaType(String MediaType) {
|
||||||
|
this.MediaType = MediaType;
|
||||||
|
}
|
||||||
|
public String getMediaType() {
|
||||||
|
return MediaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLockedFields(List<String> LockedFields) {
|
||||||
|
this.LockedFields = LockedFields;
|
||||||
|
}
|
||||||
|
public List<String> getLockedFields() {
|
||||||
|
return LockedFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLockData(boolean LockData) {
|
||||||
|
this.LockData = LockData;
|
||||||
|
}
|
||||||
|
public boolean getLockData() {
|
||||||
|
return LockData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWidth(int Width) {
|
||||||
|
this.Width = Width;
|
||||||
|
}
|
||||||
|
public int getWidth() {
|
||||||
|
return Width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeight(int Height) {
|
||||||
|
this.Height = Height;
|
||||||
|
}
|
||||||
|
public int getHeight() {
|
||||||
|
return Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
app/src/main/java/org/sifacai/vlcjellyfin/Bean/Items.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package org.sifacai.vlcjellyfin.Bean;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Items {
|
||||||
|
private List<Item> Items;
|
||||||
|
private int StartIndex;
|
||||||
|
private int TotalRecordCount;
|
||||||
|
|
||||||
|
public List<Item> getItems() {
|
||||||
|
return Items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItems(List<Item> items) {
|
||||||
|
Items = items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddItems(List<Item> items) {
|
||||||
|
Items.addAll(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStartIndex() {
|
||||||
|
return StartIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStartIndex(int startIndex) {
|
||||||
|
StartIndex = startIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalRecordCount() {
|
||||||
|
return TotalRecordCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalRecordCount(int totalRecordCount) {
|
||||||
|
TotalRecordCount = totalRecordCount;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package org.sifacai.vlcjellyfin.Bean;
|
||||||
|
|
||||||
|
public class MediaAttachments {
|
||||||
|
private String Codec;
|
||||||
|
private String CodecTag;
|
||||||
|
private int Index;
|
||||||
|
private String FileName;
|
||||||
|
private String MimeType;
|
||||||
|
|
||||||
|
public String getCodec() {
|
||||||
|
return Codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCodec(String codec) {
|
||||||
|
Codec = codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCodecTag() {
|
||||||
|
return CodecTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCodecTag(String codecTag) {
|
||||||
|
CodecTag = codecTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndex() {
|
||||||
|
return Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(int index) {
|
||||||
|
Index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileName() {
|
||||||
|
return FileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFileName(String fileName) {
|
||||||
|
FileName = fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMimeType() {
|
||||||
|
return MimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMimeType(String mimeType) {
|
||||||
|
MimeType = mimeType;
|
||||||
|
}
|
||||||
|
}
|
247
app/src/main/java/org/sifacai/vlcjellyfin/Bean/MediaSources.java
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2022 json.cn
|
||||||
|
*/
|
||||||
|
package org.sifacai.vlcjellyfin.Bean;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated: 2022-09-01 14:6:30
|
||||||
|
*
|
||||||
|
* @author json.cn (i@json.cn)
|
||||||
|
* @website http://www.json.cn/java2pojo/
|
||||||
|
*/
|
||||||
|
public class MediaSources {
|
||||||
|
|
||||||
|
private String Protocol;
|
||||||
|
private String Id;
|
||||||
|
private String Path;
|
||||||
|
private String Type;
|
||||||
|
private String Container;
|
||||||
|
private long Size;
|
||||||
|
private String Name;
|
||||||
|
private boolean IsRemote;
|
||||||
|
private String ETag;
|
||||||
|
private long RunTimeTicks;
|
||||||
|
private boolean ReadAtNativeFramerate;
|
||||||
|
private boolean IgnoreDts;
|
||||||
|
private boolean IgnoreIndex;
|
||||||
|
private boolean GenPtsInput;
|
||||||
|
private boolean SupportsTranscoding;
|
||||||
|
private boolean SupportsDirectStream;
|
||||||
|
private boolean SupportsDirectPlay;
|
||||||
|
private boolean IsInfiniteStream;
|
||||||
|
private boolean RequiresOpening;
|
||||||
|
private boolean RequiresClosing;
|
||||||
|
private boolean RequiresLooping;
|
||||||
|
private boolean SupportsProbing;
|
||||||
|
private String VideoType;
|
||||||
|
private List<MediaStreams> MediaStreams;
|
||||||
|
private List<MediaAttachments> MediaAttachments;
|
||||||
|
private List<String> Formats;
|
||||||
|
private long Bitrate;
|
||||||
|
private int DefaultAudioStreamIndex;
|
||||||
|
private int DefaultSubtitleStreamIndex;
|
||||||
|
public void setProtocol(String Protocol) {
|
||||||
|
this.Protocol = Protocol;
|
||||||
|
}
|
||||||
|
public String getProtocol() {
|
||||||
|
return Protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String Id) {
|
||||||
|
this.Id = Id;
|
||||||
|
}
|
||||||
|
public String getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPath(String Path) {
|
||||||
|
this.Path = Path;
|
||||||
|
}
|
||||||
|
public String getPath() {
|
||||||
|
return Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String Type) {
|
||||||
|
this.Type = Type;
|
||||||
|
}
|
||||||
|
public String getType() {
|
||||||
|
return Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContainer(String Container) {
|
||||||
|
this.Container = Container;
|
||||||
|
}
|
||||||
|
public String getContainer() {
|
||||||
|
return Container;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSize(long Size) {
|
||||||
|
this.Size = Size;
|
||||||
|
}
|
||||||
|
public long getSize() {
|
||||||
|
return Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String Name) {
|
||||||
|
this.Name = Name;
|
||||||
|
}
|
||||||
|
public String getName() {
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsRemote(boolean IsRemote) {
|
||||||
|
this.IsRemote = IsRemote;
|
||||||
|
}
|
||||||
|
public boolean getIsRemote() {
|
||||||
|
return IsRemote;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setETag(String ETag) {
|
||||||
|
this.ETag = ETag;
|
||||||
|
}
|
||||||
|
public String getETag() {
|
||||||
|
return ETag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRunTimeTicks(long RunTimeTicks) {
|
||||||
|
this.RunTimeTicks = RunTimeTicks;
|
||||||
|
}
|
||||||
|
public long getRunTimeTicks() {
|
||||||
|
return RunTimeTicks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReadAtNativeFramerate(boolean ReadAtNativeFramerate) {
|
||||||
|
this.ReadAtNativeFramerate = ReadAtNativeFramerate;
|
||||||
|
}
|
||||||
|
public boolean getReadAtNativeFramerate() {
|
||||||
|
return ReadAtNativeFramerate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIgnoreDts(boolean IgnoreDts) {
|
||||||
|
this.IgnoreDts = IgnoreDts;
|
||||||
|
}
|
||||||
|
public boolean getIgnoreDts() {
|
||||||
|
return IgnoreDts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIgnoreIndex(boolean IgnoreIndex) {
|
||||||
|
this.IgnoreIndex = IgnoreIndex;
|
||||||
|
}
|
||||||
|
public boolean getIgnoreIndex() {
|
||||||
|
return IgnoreIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGenPtsInput(boolean GenPtsInput) {
|
||||||
|
this.GenPtsInput = GenPtsInput;
|
||||||
|
}
|
||||||
|
public boolean getGenPtsInput() {
|
||||||
|
return GenPtsInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSupportsTranscoding(boolean SupportsTranscoding) {
|
||||||
|
this.SupportsTranscoding = SupportsTranscoding;
|
||||||
|
}
|
||||||
|
public boolean getSupportsTranscoding() {
|
||||||
|
return SupportsTranscoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSupportsDirectStream(boolean SupportsDirectStream) {
|
||||||
|
this.SupportsDirectStream = SupportsDirectStream;
|
||||||
|
}
|
||||||
|
public boolean getSupportsDirectStream() {
|
||||||
|
return SupportsDirectStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSupportsDirectPlay(boolean SupportsDirectPlay) {
|
||||||
|
this.SupportsDirectPlay = SupportsDirectPlay;
|
||||||
|
}
|
||||||
|
public boolean getSupportsDirectPlay() {
|
||||||
|
return SupportsDirectPlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsInfiniteStream(boolean IsInfiniteStream) {
|
||||||
|
this.IsInfiniteStream = IsInfiniteStream;
|
||||||
|
}
|
||||||
|
public boolean getIsInfiniteStream() {
|
||||||
|
return IsInfiniteStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequiresOpening(boolean RequiresOpening) {
|
||||||
|
this.RequiresOpening = RequiresOpening;
|
||||||
|
}
|
||||||
|
public boolean getRequiresOpening() {
|
||||||
|
return RequiresOpening;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequiresClosing(boolean RequiresClosing) {
|
||||||
|
this.RequiresClosing = RequiresClosing;
|
||||||
|
}
|
||||||
|
public boolean getRequiresClosing() {
|
||||||
|
return RequiresClosing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequiresLooping(boolean RequiresLooping) {
|
||||||
|
this.RequiresLooping = RequiresLooping;
|
||||||
|
}
|
||||||
|
public boolean getRequiresLooping() {
|
||||||
|
return RequiresLooping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSupportsProbing(boolean SupportsProbing) {
|
||||||
|
this.SupportsProbing = SupportsProbing;
|
||||||
|
}
|
||||||
|
public boolean getSupportsProbing() {
|
||||||
|
return SupportsProbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoType(String VideoType) {
|
||||||
|
this.VideoType = VideoType;
|
||||||
|
}
|
||||||
|
public String getVideoType() {
|
||||||
|
return VideoType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMediaStreams(List<MediaStreams> MediaStreams) {
|
||||||
|
this.MediaStreams = MediaStreams;
|
||||||
|
}
|
||||||
|
public List<MediaStreams> getMediaStreams() {
|
||||||
|
return MediaStreams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMediaAttachments(List<MediaAttachments> MediaAttachments) {
|
||||||
|
this.MediaAttachments = MediaAttachments;
|
||||||
|
}
|
||||||
|
public List<MediaAttachments> getMediaAttachments() {
|
||||||
|
return MediaAttachments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFormats(List<String> Formats) {
|
||||||
|
this.Formats = Formats;
|
||||||
|
}
|
||||||
|
public List<String> getFormats() {
|
||||||
|
return Formats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBitrate(long Bitrate) {
|
||||||
|
this.Bitrate = Bitrate;
|
||||||
|
}
|
||||||
|
public long getBitrate() {
|
||||||
|
return Bitrate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultAudioStreamIndex(int DefaultAudioStreamIndex) {
|
||||||
|
this.DefaultAudioStreamIndex = DefaultAudioStreamIndex;
|
||||||
|
}
|
||||||
|
public int getDefaultAudioStreamIndex() {
|
||||||
|
return DefaultAudioStreamIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultSubtitleStreamIndex(int DefaultSubtitleStreamIndex) {
|
||||||
|
this.DefaultSubtitleStreamIndex = DefaultSubtitleStreamIndex;
|
||||||
|
}
|
||||||
|
public int getDefaultSubtitleStreamIndex() {
|
||||||
|
return DefaultSubtitleStreamIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
247
app/src/main/java/org/sifacai/vlcjellyfin/Bean/MediaStreams.java
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2022 json.cn
|
||||||
|
*/
|
||||||
|
package org.sifacai.vlcjellyfin.Bean;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated: 2022-09-01 14:6:30
|
||||||
|
*
|
||||||
|
* @author json.cn (i@json.cn)
|
||||||
|
* @website http://www.json.cn/java2pojo/
|
||||||
|
*/
|
||||||
|
public class MediaStreams {
|
||||||
|
|
||||||
|
private String Codec;
|
||||||
|
private String Language;
|
||||||
|
private String ColorSpace;
|
||||||
|
private String ColorTransfer;
|
||||||
|
private String ColorPrimaries;
|
||||||
|
private String TimeBase;
|
||||||
|
private String Title;
|
||||||
|
private String VideoRange;
|
||||||
|
private String VideoRangeType;
|
||||||
|
private String DisplayTitle;
|
||||||
|
private boolean IsInterlaced;
|
||||||
|
private long BitRate;
|
||||||
|
private int BitDepth;
|
||||||
|
private int RefFrames;
|
||||||
|
private boolean IsDefault;
|
||||||
|
private boolean IsForced;
|
||||||
|
private int Height;
|
||||||
|
private int Width;
|
||||||
|
private double AverageFrameRate;
|
||||||
|
private double RealFrameRate;
|
||||||
|
private String Profile;
|
||||||
|
private String Type;
|
||||||
|
private String AspectRatio;
|
||||||
|
private int Index;
|
||||||
|
private boolean IsExternal;
|
||||||
|
private boolean IsTextSubtitleStream;
|
||||||
|
private boolean SupportsExternalStream;
|
||||||
|
private String PixelFormat;
|
||||||
|
private int Level;
|
||||||
|
public void setCodec(String Codec) {
|
||||||
|
this.Codec = Codec;
|
||||||
|
}
|
||||||
|
public String getCodec() {
|
||||||
|
return Codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLanguage(String Language) {
|
||||||
|
this.Language = Language;
|
||||||
|
}
|
||||||
|
public String getLanguage() {
|
||||||
|
return Language;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColorSpace(String ColorSpace) {
|
||||||
|
this.ColorSpace = ColorSpace;
|
||||||
|
}
|
||||||
|
public String getColorSpace() {
|
||||||
|
return ColorSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColorTransfer(String ColorTransfer) {
|
||||||
|
this.ColorTransfer = ColorTransfer;
|
||||||
|
}
|
||||||
|
public String getColorTransfer() {
|
||||||
|
return ColorTransfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColorPrimaries(String ColorPrimaries) {
|
||||||
|
this.ColorPrimaries = ColorPrimaries;
|
||||||
|
}
|
||||||
|
public String getColorPrimaries() {
|
||||||
|
return ColorPrimaries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeBase(String TimeBase) {
|
||||||
|
this.TimeBase = TimeBase;
|
||||||
|
}
|
||||||
|
public String getTimeBase() {
|
||||||
|
return TimeBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String Title) {
|
||||||
|
this.Title = Title;
|
||||||
|
}
|
||||||
|
public String getTitle() {
|
||||||
|
return Title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoRange(String VideoRange) {
|
||||||
|
this.VideoRange = VideoRange;
|
||||||
|
}
|
||||||
|
public String getVideoRange() {
|
||||||
|
return VideoRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoRangeType(String VideoRangeType) {
|
||||||
|
this.VideoRangeType = VideoRangeType;
|
||||||
|
}
|
||||||
|
public String getVideoRangeType() {
|
||||||
|
return VideoRangeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisplayTitle(String DisplayTitle) {
|
||||||
|
this.DisplayTitle = DisplayTitle;
|
||||||
|
}
|
||||||
|
public String getDisplayTitle() {
|
||||||
|
return DisplayTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsInterlaced(boolean IsInterlaced) {
|
||||||
|
this.IsInterlaced = IsInterlaced;
|
||||||
|
}
|
||||||
|
public boolean getIsInterlaced() {
|
||||||
|
return IsInterlaced;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBitRate(long BitRate) {
|
||||||
|
this.BitRate = BitRate;
|
||||||
|
}
|
||||||
|
public long getBitRate() {
|
||||||
|
return BitRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBitDepth(int BitDepth) {
|
||||||
|
this.BitDepth = BitDepth;
|
||||||
|
}
|
||||||
|
public int getBitDepth() {
|
||||||
|
return BitDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRefFrames(int RefFrames) {
|
||||||
|
this.RefFrames = RefFrames;
|
||||||
|
}
|
||||||
|
public int getRefFrames() {
|
||||||
|
return RefFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsDefault(boolean IsDefault) {
|
||||||
|
this.IsDefault = IsDefault;
|
||||||
|
}
|
||||||
|
public boolean getIsDefault() {
|
||||||
|
return IsDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsForced(boolean IsForced) {
|
||||||
|
this.IsForced = IsForced;
|
||||||
|
}
|
||||||
|
public boolean getIsForced() {
|
||||||
|
return IsForced;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeight(int Height) {
|
||||||
|
this.Height = Height;
|
||||||
|
}
|
||||||
|
public int getHeight() {
|
||||||
|
return Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWidth(int Width) {
|
||||||
|
this.Width = Width;
|
||||||
|
}
|
||||||
|
public int getWidth() {
|
||||||
|
return Width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAverageFrameRate(double AverageFrameRate) {
|
||||||
|
this.AverageFrameRate = AverageFrameRate;
|
||||||
|
}
|
||||||
|
public double getAverageFrameRate() {
|
||||||
|
return AverageFrameRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealFrameRate(double RealFrameRate) {
|
||||||
|
this.RealFrameRate = RealFrameRate;
|
||||||
|
}
|
||||||
|
public double getRealFrameRate() {
|
||||||
|
return RealFrameRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProfile(String Profile) {
|
||||||
|
this.Profile = Profile;
|
||||||
|
}
|
||||||
|
public String getProfile() {
|
||||||
|
return Profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String Type) {
|
||||||
|
this.Type = Type;
|
||||||
|
}
|
||||||
|
public String getType() {
|
||||||
|
return Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAspectRatio(String AspectRatio) {
|
||||||
|
this.AspectRatio = AspectRatio;
|
||||||
|
}
|
||||||
|
public String getAspectRatio() {
|
||||||
|
return AspectRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(int Index) {
|
||||||
|
this.Index = Index;
|
||||||
|
}
|
||||||
|
public int getIndex() {
|
||||||
|
return Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsExternal(boolean IsExternal) {
|
||||||
|
this.IsExternal = IsExternal;
|
||||||
|
}
|
||||||
|
public boolean getIsExternal() {
|
||||||
|
return IsExternal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsTextSubtitleStream(boolean IsTextSubtitleStream) {
|
||||||
|
this.IsTextSubtitleStream = IsTextSubtitleStream;
|
||||||
|
}
|
||||||
|
public boolean getIsTextSubtitleStream() {
|
||||||
|
return IsTextSubtitleStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSupportsExternalStream(boolean SupportsExternalStream) {
|
||||||
|
this.SupportsExternalStream = SupportsExternalStream;
|
||||||
|
}
|
||||||
|
public boolean getSupportsExternalStream() {
|
||||||
|
return SupportsExternalStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPixelFormat(String PixelFormat) {
|
||||||
|
this.PixelFormat = PixelFormat;
|
||||||
|
}
|
||||||
|
public String getPixelFormat() {
|
||||||
|
return PixelFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLevel(int Level) {
|
||||||
|
this.Level = Level;
|
||||||
|
}
|
||||||
|
public int getLevel() {
|
||||||
|
return Level;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
54
app/src/main/java/org/sifacai/vlcjellyfin/Bean/People.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2022 json.cn
|
||||||
|
*/
|
||||||
|
package org.sifacai.vlcjellyfin.Bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated: 2022-09-01 14:6:30
|
||||||
|
*
|
||||||
|
* @author json.cn (i@json.cn)
|
||||||
|
* @website http://www.json.cn/java2pojo/
|
||||||
|
*/
|
||||||
|
public class People {
|
||||||
|
|
||||||
|
private String Name;
|
||||||
|
private String Id;
|
||||||
|
private String Role;
|
||||||
|
private String Type;
|
||||||
|
private String PrimaryImageTag;
|
||||||
|
public void setName(String Name) {
|
||||||
|
this.Name = Name;
|
||||||
|
}
|
||||||
|
public String getName() {
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String Id) {
|
||||||
|
this.Id = Id;
|
||||||
|
}
|
||||||
|
public String getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRole(String Role) {
|
||||||
|
this.Role = Role;
|
||||||
|
}
|
||||||
|
public String getRole() {
|
||||||
|
return Role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String Type) {
|
||||||
|
this.Type = Type;
|
||||||
|
}
|
||||||
|
public String getType() {
|
||||||
|
return Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrimaryImageTag(String PrimaryImageTag) {
|
||||||
|
this.PrimaryImageTag = PrimaryImageTag;
|
||||||
|
}
|
||||||
|
public String getPrimaryImageTag() {
|
||||||
|
return PrimaryImageTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2022 json.cn
|
||||||
|
*/
|
||||||
|
package org.sifacai.vlcjellyfin.Bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated: 2022-09-01 14:6:30
|
||||||
|
*
|
||||||
|
* @author json.cn (i@json.cn)
|
||||||
|
* @website http://www.json.cn/java2pojo/
|
||||||
|
*/
|
||||||
|
public class ProviderIds {
|
||||||
|
|
||||||
|
private String Tmdb;
|
||||||
|
private String Imdb;
|
||||||
|
public void setTmdb(String Tmdb) {
|
||||||
|
this.Tmdb = Tmdb;
|
||||||
|
}
|
||||||
|
public String getTmdb() {
|
||||||
|
return Tmdb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImdb(String Imdb) {
|
||||||
|
this.Imdb = Imdb;
|
||||||
|
}
|
||||||
|
public String getImdb() {
|
||||||
|
return Imdb;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
app/src/main/java/org/sifacai/vlcjellyfin/Bean/Studios.java
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2022 json.cn
|
||||||
|
*/
|
||||||
|
package org.sifacai.vlcjellyfin.Bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated: 2022-09-01 14:6:30
|
||||||
|
*
|
||||||
|
* @author json.cn (i@json.cn)
|
||||||
|
* @website http://www.json.cn/java2pojo/
|
||||||
|
*/
|
||||||
|
public class Studios {
|
||||||
|
|
||||||
|
private String Name;
|
||||||
|
private String Id;
|
||||||
|
public void setName(String Name) {
|
||||||
|
this.Name = Name;
|
||||||
|
}
|
||||||
|
public String getName() {
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String Id) {
|
||||||
|
this.Id = Id;
|
||||||
|
}
|
||||||
|
public String getId() {
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
85
app/src/main/java/org/sifacai/vlcjellyfin/Bean/UserData.java
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2022 json.cn
|
||||||
|
*/
|
||||||
|
package org.sifacai.vlcjellyfin.Bean;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated: 2022-09-01 14:6:30
|
||||||
|
*
|
||||||
|
* @author json.cn (i@json.cn)
|
||||||
|
* @website http://www.json.cn/java2pojo/
|
||||||
|
*/
|
||||||
|
public class UserData {
|
||||||
|
|
||||||
|
private long PlaybackPositionTicks;
|
||||||
|
private int PlayCount;
|
||||||
|
private boolean IsFavorite;
|
||||||
|
private Date LastPlayedDate;
|
||||||
|
private boolean Played;
|
||||||
|
private String Key;
|
||||||
|
private double PlayedPercentage;
|
||||||
|
|
||||||
|
public boolean isFavorite() {
|
||||||
|
return IsFavorite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFavorite(boolean favorite) {
|
||||||
|
IsFavorite = favorite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPlayed() {
|
||||||
|
return Played;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getPlayedPercentage() {
|
||||||
|
return PlayedPercentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlayedPercentage(double playedPercentage) {
|
||||||
|
PlayedPercentage = playedPercentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlaybackPositionTicks(long PlaybackPositionTicks) {
|
||||||
|
this.PlaybackPositionTicks = PlaybackPositionTicks;
|
||||||
|
}
|
||||||
|
public long getPlaybackPositionTicks() {
|
||||||
|
return PlaybackPositionTicks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlayCount(int PlayCount) {
|
||||||
|
this.PlayCount = PlayCount;
|
||||||
|
}
|
||||||
|
public int getPlayCount() {
|
||||||
|
return PlayCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsFavorite(boolean IsFavorite) {
|
||||||
|
this.IsFavorite = IsFavorite;
|
||||||
|
}
|
||||||
|
public boolean getIsFavorite() {
|
||||||
|
return IsFavorite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastPlayedDate(Date LastPlayedDate) {
|
||||||
|
this.LastPlayedDate = LastPlayedDate;
|
||||||
|
}
|
||||||
|
public Date getLastPlayedDate() {
|
||||||
|
return LastPlayedDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlayed(boolean Played) {
|
||||||
|
this.Played = Played;
|
||||||
|
}
|
||||||
|
public boolean getPlayed() {
|
||||||
|
return Played;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKey(String Key) {
|
||||||
|
this.Key = Key;
|
||||||
|
}
|
||||||
|
public String getKey() {
|
||||||
|
return Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,212 @@
|
|||||||
|
package org.sifacai.vlcjellyfin;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.ActionMode;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.SubMenu;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.PopupMenu;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.owen.tvrecyclerview.widget.TvRecyclerView;
|
||||||
|
import com.owen.tvrecyclerview.widget.V7GridLayoutManager;
|
||||||
|
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.Item;
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.Items;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CollectionActivity extends BaseActivity {
|
||||||
|
private String TAG = "CollectionActivity";
|
||||||
|
private TvRecyclerView mGridContiner = null;
|
||||||
|
private TextView tvTitleTip = null;
|
||||||
|
private String ItemId = "";
|
||||||
|
private int currentPage = 1; //当前页码
|
||||||
|
private int countPage = 1; //总页数
|
||||||
|
private int limit = 60; //每页条目
|
||||||
|
private int totalCount = 0; //总条目数
|
||||||
|
private String Type = "";
|
||||||
|
private Item currObj = null;
|
||||||
|
private List<Item> currItems = null;
|
||||||
|
private JAdapter currAdapter = null;
|
||||||
|
|
||||||
|
private TextView sortMenuBtn;
|
||||||
|
|
||||||
|
private PopupMenu SortByMenu;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_collection);
|
||||||
|
|
||||||
|
if (JfClient.UserId.equals("") || JfClient.AccessToken.equals("")) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
mGridContiner = findViewById(R.id.mGridView);
|
||||||
|
tvTitleTip = findViewById(R.id.actionBar_titleTip);
|
||||||
|
V7GridLayoutManager v7GridLayoutManager = new V7GridLayoutManager(this,6);
|
||||||
|
mGridContiner.setLayoutManager(v7GridLayoutManager);
|
||||||
|
mGridContiner.setItemAnimator(null); //防崩溃
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
Intent intent = getIntent();
|
||||||
|
ItemId = intent.getStringExtra("itemId");
|
||||||
|
if(ItemId.equals("")){
|
||||||
|
finish();
|
||||||
|
}else{
|
||||||
|
currItems = new ArrayList<>();
|
||||||
|
currAdapter = getJAdapter(currItems);
|
||||||
|
mGridContiner.setAdapter(currAdapter);
|
||||||
|
|
||||||
|
initData();
|
||||||
|
|
||||||
|
initSortByMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initData() {
|
||||||
|
showLoadingDialog("加载中…………");
|
||||||
|
JfClient.GetItemInfo(ItemId,new JfClient.JJCallBack(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Item Collection) {
|
||||||
|
currObj = Collection;
|
||||||
|
Type = Collection.getType();
|
||||||
|
fillItems();
|
||||||
|
setLoadMore();
|
||||||
|
}
|
||||||
|
},null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载条目数据
|
||||||
|
*/
|
||||||
|
private void fillItems(){
|
||||||
|
JfClient.GetCollection(ItemId,Type,
|
||||||
|
JfClient.config.getSortBy(),
|
||||||
|
JfClient.config.getSortOrder(),
|
||||||
|
limit,currentPage,
|
||||||
|
new JfClient.JJCallBack(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Items items) {
|
||||||
|
totalCount = items.getTotalRecordCount();
|
||||||
|
countPage = (int) Math.ceil((double) totalCount / limit);
|
||||||
|
List<Item> Items = items.getItems();
|
||||||
|
dismissLoadingDialog();
|
||||||
|
currAdapter.addItems(Items);
|
||||||
|
setTitleTip();
|
||||||
|
mGridContiner.finishLoadMore();
|
||||||
|
}
|
||||||
|
},null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JAdapter getJAdapter(List<Item> items){
|
||||||
|
JAdapter jAdapter = new JAdapter(items,false);
|
||||||
|
jAdapter.setOnItemClickListener(new JAdapter.OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(Item item) {
|
||||||
|
String type = item.getType();
|
||||||
|
String itemId = item.getId();
|
||||||
|
Intent intent = null;
|
||||||
|
if(type.equals("Folder") || type.equals("CollectionFolder")){
|
||||||
|
intent = new Intent(mAA,CollectionActivity.class);
|
||||||
|
}else{
|
||||||
|
intent = new Intent(mAA,DetailActivity.class);
|
||||||
|
}
|
||||||
|
intent.putExtra("itemId",itemId);
|
||||||
|
mAA.startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return jAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setLoadMore(){
|
||||||
|
mGridContiner.setOnLoadMoreListener(new TvRecyclerView.OnLoadMoreListener() {
|
||||||
|
@Override
|
||||||
|
public void onLoadMore() {
|
||||||
|
if(currentPage < countPage){
|
||||||
|
currentPage += 1;
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
fillItems();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTitleTip(){
|
||||||
|
String tip = "共 "+ totalCount +" ," + countPage + " 页,已加载" + currentPage + "页";
|
||||||
|
tvTitleTip.setText(tip);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initSortByMenu(){
|
||||||
|
sortMenuBtn = findViewById(R.id.actionBar_sortBtn);
|
||||||
|
sortMenuBtn.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
int i = 0;
|
||||||
|
for(Config.SortByType sbt : Config.SortByType.values()){
|
||||||
|
if(sbt.value.equals(JfClient.config.getSortBy())){
|
||||||
|
i = sbt.ordinal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SortByMenu.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sortMenuBtn.setVisibility(View.VISIBLE);
|
||||||
|
setSortMenuBtnText();
|
||||||
|
SortByMenu = new PopupMenu(this,sortMenuBtn);
|
||||||
|
Menu menu = SortByMenu.getMenu();
|
||||||
|
Config.SortByType[] Ss = Config.SortByType.values();
|
||||||
|
for (Config.SortByType sortby:Ss) {
|
||||||
|
menu.add(0,sortby.ordinal(),sortby.ordinal(),sortby.name());
|
||||||
|
}
|
||||||
|
for (Config.SotrOrderType sot:Config.SotrOrderType.values()){
|
||||||
|
menu.add(1,sot.ordinal() + Ss.length,sot.ordinal() + Ss.length,sot.name());
|
||||||
|
}
|
||||||
|
SortByMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||||
|
if(menuItem.getGroupId() == 0) {
|
||||||
|
JfClient.config.setSortBy(Config.SortByType.valueOf(menuItem.getTitle().toString()).value);
|
||||||
|
}else{
|
||||||
|
JfClient.config.setSortOrder(Config.SotrOrderType.valueOf(menuItem.getTitle().toString()).value);
|
||||||
|
}
|
||||||
|
setSortMenuBtnText();
|
||||||
|
currAdapter.clearItems();
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
initData();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSortMenuBtnText(){
|
||||||
|
String s = Config.SortByType.findName(JfClient.config.getSortBy());
|
||||||
|
s += "-";
|
||||||
|
s += Config.SotrOrderType.findName(JfClient.config.getSortOrder());
|
||||||
|
sortMenuBtn.setText(s);
|
||||||
|
}
|
||||||
|
}
|
196
app/src/main/java/org/sifacai/vlcjellyfin/Config.java
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
package org.sifacai.vlcjellyfin;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
public class Config {
|
||||||
|
private Context context;
|
||||||
|
private String JellyfinUrl = "";
|
||||||
|
private String UserName = "";
|
||||||
|
private String PassWord = "";
|
||||||
|
private String SortBy = "DateCreated";
|
||||||
|
private String SortOrder = "Descending";
|
||||||
|
private boolean PlayStartInBegin; //是否从头开始播放 (接上次播放进度)
|
||||||
|
private boolean HAACC; //硬解
|
||||||
|
private boolean FORCE_HAACC; //强制硬解
|
||||||
|
private boolean ExtensionPlayer; //调用外部播放器
|
||||||
|
|
||||||
|
public boolean isExtensionPlayer() {
|
||||||
|
return ExtensionPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtensionPlayer(boolean extensionPlayer) {
|
||||||
|
ExtensionPlayer = extensionPlayer;
|
||||||
|
saveConfigToSP("ExtensionPlayer",extensionPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPlayStartInBegin() {
|
||||||
|
return PlayStartInBegin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlayStartInBegin(boolean playStartInBegin) {
|
||||||
|
PlayStartInBegin = playStartInBegin;
|
||||||
|
saveConfigToSP("PlayStartInBegin",playStartInBegin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Config(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
getConfigFromSP();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJellyfinUrl() {
|
||||||
|
return JellyfinUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJellyfinUrl(String jellyfinUrl) {
|
||||||
|
JellyfinUrl = jellyfinUrl;
|
||||||
|
saveConfigToSP("url",jellyfinUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserName() {
|
||||||
|
return UserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserName(String userName) {
|
||||||
|
UserName = userName;
|
||||||
|
saveConfigToSP("username",userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassWord() {
|
||||||
|
return PassWord;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassWord(String passWord) {
|
||||||
|
PassWord = passWord;
|
||||||
|
saveConfigToSP("password",passWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSortBy() {
|
||||||
|
return SortBy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSortBy(String sortBy) {
|
||||||
|
SortBy = sortBy;
|
||||||
|
saveConfigToSP("sortby",sortBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSortOrder() {
|
||||||
|
return SortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSortOrder(String sortOrder) {
|
||||||
|
SortOrder = sortOrder;
|
||||||
|
saveConfigToSP("sortorder",sortOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHAACC() {
|
||||||
|
return HAACC;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHAACC(boolean HAACC) {
|
||||||
|
this.HAACC = HAACC;
|
||||||
|
saveConfigToSP("HAACC",HAACC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFORCE_HAACC() {
|
||||||
|
return FORCE_HAACC;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFORCE_HAACC(boolean FORCE_HAACC) {
|
||||||
|
this.FORCE_HAACC = FORCE_HAACC;
|
||||||
|
saveConfigToSP("FORCE_HAACC",FORCE_HAACC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取配置
|
||||||
|
*/
|
||||||
|
public void getConfigFromSP() {
|
||||||
|
SharedPreferences sp = context.getSharedPreferences("Jellyfin", context.MODE_PRIVATE);
|
||||||
|
this.JellyfinUrl = sp.getString("url", "http://");
|
||||||
|
this.UserName = sp.getString("username", "");
|
||||||
|
this.PassWord = sp.getString("password", "");
|
||||||
|
this.SortBy = sp.getString("sortby","DateCreated");
|
||||||
|
this.SortOrder = sp.getString("sortorder","Descending");
|
||||||
|
this.HAACC = sp.getBoolean("HAACC",true);
|
||||||
|
this.FORCE_HAACC = sp.getBoolean("FORCE_HAACC",false);
|
||||||
|
this.PlayStartInBegin = sp.getBoolean("PlayStartInBegin",true);
|
||||||
|
this.ExtensionPlayer = sp.getBoolean("ExtensionPlayer",false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存单项配置
|
||||||
|
*/
|
||||||
|
public void saveConfigToSP(String key, String value) {
|
||||||
|
SharedPreferences sp = context.getSharedPreferences("Jellyfin", context.MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor editor = sp.edit();
|
||||||
|
editor.putString(key, value);
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存单项配置
|
||||||
|
*/
|
||||||
|
public void saveConfigToSP(String key, boolean value) {
|
||||||
|
SharedPreferences sp = context.getSharedPreferences("Jellyfin", context.MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor editor = sp.edit();
|
||||||
|
editor.putBoolean(key, value);
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
SharedPreferences sp = context.getSharedPreferences("Jellyfin", context.MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor editor = sp.edit();
|
||||||
|
editor.clear();
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SortByType {
|
||||||
|
评分("CommunityRating"),
|
||||||
|
加入日期("DateCreated"),
|
||||||
|
播放日期("DatePlayed"),
|
||||||
|
家长分级("OfficialRating"),
|
||||||
|
播放次数("PlayCount"),
|
||||||
|
发行日期("PremiereDate"),
|
||||||
|
播放时长("Runtime");
|
||||||
|
|
||||||
|
public String value;
|
||||||
|
|
||||||
|
SortByType(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String findName(String value){
|
||||||
|
String name = "";
|
||||||
|
for (SortByType sbt:SortByType.values()) {
|
||||||
|
if(sbt.value.equals(value)){
|
||||||
|
name = sbt.name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SotrOrderType{
|
||||||
|
升序("Ascending"),
|
||||||
|
降序("Descending");
|
||||||
|
|
||||||
|
public String value;
|
||||||
|
|
||||||
|
SotrOrderType(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String findName(String value){
|
||||||
|
String name = "";
|
||||||
|
for (SotrOrderType sot:SotrOrderType.values()) {
|
||||||
|
if(sot.value.equals(value)){
|
||||||
|
name = sot.name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
372
app/src/main/java/org/sifacai/vlcjellyfin/DetailActivity.java
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
package org.sifacai.vlcjellyfin;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.UriMatcher;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.animation.BounceInterpolator;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.owen.tvrecyclerview.widget.V7GridLayoutManager;
|
||||||
|
import com.owen.tvrecyclerview.widget.V7LinearLayoutManager;
|
||||||
|
import com.squareup.picasso.Picasso;
|
||||||
|
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.ImageTags;
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.Item;
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.Items;
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.MediaStreams;
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.People;
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.UserData;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DetailActivity extends BaseActivity implements JAdapter.OnItemClickListener {
|
||||||
|
private String TAG = "详情:";
|
||||||
|
private String ItemId;
|
||||||
|
private ImageView tvCover;
|
||||||
|
private TextView tvTitle;
|
||||||
|
private TextView tvDetails;
|
||||||
|
private ImageView tvPlay;
|
||||||
|
private JRecyclerView mGridView;
|
||||||
|
private JRecyclerView mPeopleGridView;
|
||||||
|
private LinearLayout tvPeopleLayout;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_detail);
|
||||||
|
getSupportActionBar().hide();
|
||||||
|
|
||||||
|
if (JfClient.UserId.equals("") || JfClient.AccessToken.equals("")) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
tvCover = findViewById(R.id.tvCover);
|
||||||
|
tvTitle = findViewById(R.id.tvTitle);
|
||||||
|
tvDetails = findViewById(R.id.tvDetails);
|
||||||
|
tvPlay = findViewById(R.id.tvPlay);
|
||||||
|
mGridView = findViewById(R.id.mGridView);
|
||||||
|
tvPeopleLayout = findViewById(R.id.tvPersonLayout);
|
||||||
|
mPeopleGridView = findViewById(R.id.mPersonGridView);
|
||||||
|
|
||||||
|
Intent intent = getIntent();
|
||||||
|
ItemId = intent.getStringExtra("itemId");
|
||||||
|
if (ItemId.equals("")) {
|
||||||
|
finish();
|
||||||
|
} else {
|
||||||
|
showLoadingDialog("加载中……");
|
||||||
|
initData(ItemId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initData(String itemId) {
|
||||||
|
JfClient.GetItemInfo(itemId, new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Item item) {
|
||||||
|
fillDetails(item);
|
||||||
|
}
|
||||||
|
}, new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onError(String str) {
|
||||||
|
errcb.onError(str);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillDetails(Item details) {
|
||||||
|
String Id = details.getId();
|
||||||
|
String Name = details.getName();
|
||||||
|
String imgurl = JfClient.GetImgUrl(Id, details.getImageTags() == null ? "" : details.getImageTags().getPrimary());
|
||||||
|
Picasso.get()
|
||||||
|
.load(imgurl)
|
||||||
|
.placeholder(R.drawable.img_loading_placeholder)
|
||||||
|
.error(R.drawable.img_loading_placeholder)
|
||||||
|
.into(tvCover);
|
||||||
|
|
||||||
|
String Genres = String.join(",", details.getGenres());
|
||||||
|
|
||||||
|
tvTitle.setText(Name);
|
||||||
|
tvDetails.append(details.getProductionYear() == null ? "" : "年份:" + details.getProductionYear() + " ");
|
||||||
|
tvDetails.append(Genres.equals("") ? "" : "风格:" + Genres + "\n");
|
||||||
|
tvDetails.append(details.getCommunityRating() == null ? "" : "评分:" + details.getCommunityRating() + " ");
|
||||||
|
tvDetails.append(details.getOfficialRating() == null ? "" : "评级:" + details.getOfficialRating() + "\n");
|
||||||
|
|
||||||
|
if (details.getMediaStreams() != null) {
|
||||||
|
String video = "";
|
||||||
|
String audio = "";
|
||||||
|
String subtitle = "";
|
||||||
|
for (int i = 0; i < details.getMediaStreams().size(); i++) {
|
||||||
|
MediaStreams ms = details.getMediaStreams().get(i);
|
||||||
|
String mstype = ms.getType();
|
||||||
|
if (mstype.equals("Video")) {
|
||||||
|
video += ms.getDisplayTitle();
|
||||||
|
} else if (mstype.equals("Audio")) {
|
||||||
|
if (ms.getLanguage()!=null && !ms.getLanguage().equals("")) audio += ms.getLanguage() + "、";
|
||||||
|
else audio += ms.getCodec() + ";";
|
||||||
|
} else if (mstype.equals("Subtitle")) {
|
||||||
|
if (ms.getLanguage() != null && !ms.getLanguage().equals("")) subtitle += ms.getLanguage() + "、";
|
||||||
|
else subtitle += ms.getCodec() + ";";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tvDetails.append(video.equals("") ? "" : "视频:" + video + "\n");
|
||||||
|
tvDetails.append(audio.equals("") ? "" : "音频:" + audio + "\n");
|
||||||
|
tvDetails.append(subtitle.equals("") ? "" : "字幕:" + subtitle + "\n");
|
||||||
|
}
|
||||||
|
String overview = details.getOverview() == null ? "" : details.getOverview();
|
||||||
|
tvDetails.append("简介: " + Html.fromHtml(overview));
|
||||||
|
|
||||||
|
//填充列表
|
||||||
|
String type = details.getType();
|
||||||
|
if (type.equals("Series")) {
|
||||||
|
fillSeason(ItemId);
|
||||||
|
} else if (type.equals("Season")) {
|
||||||
|
String SeriesName = details.getSeriesName() == null ? "" : details.getSeriesName() + "-";
|
||||||
|
tvTitle.setText(SeriesName + details.getName());
|
||||||
|
String SeriesId = details.getSeriesId();
|
||||||
|
fillEpisodes(SeriesId, ItemId);
|
||||||
|
} else if (type.equals("Episode")) {
|
||||||
|
String SeriesName = details.getSeriesName() == null ? "" : details.getSeriesName() + "-";
|
||||||
|
String SeasonName = details.getSeasonName() == null ? "" : details.getSeasonName();
|
||||||
|
tvTitle.setText(SeriesName + SeasonName);
|
||||||
|
String SeriesId = details.getSeriesId();
|
||||||
|
String SeasonId = details.getSeasonId();
|
||||||
|
fillEpisodes(SeriesId, SeasonId);
|
||||||
|
} else if (type.equals("Movie")) {
|
||||||
|
fillMovie(details);
|
||||||
|
} else if (type.equals("Person")) {
|
||||||
|
tvDetails.append("\n出生日期:" + Utils.UtcToLocal(details.getPremiereDate()) + "\n");
|
||||||
|
if(null != details.getProductionLocations()) {
|
||||||
|
tvDetails.append("出生地:" + String.join(",", details.getProductionLocations()));
|
||||||
|
}
|
||||||
|
fillItemsByPerson(Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<People> Peoples = details.getPeople();
|
||||||
|
if (Peoples != null) {
|
||||||
|
if (Peoples.size() > 0) {
|
||||||
|
fillPeople(Peoples);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillMovie(Item item) {
|
||||||
|
|
||||||
|
item.setName("播放: " + item.getName());
|
||||||
|
List<Item> plist = new ArrayList<>();
|
||||||
|
plist.add(item);
|
||||||
|
if (item.getPartCount() > 0) {
|
||||||
|
JfClient.GetAddPart(item.getId(), new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Items parts) {
|
||||||
|
plist.addAll(parts.getItems());
|
||||||
|
fillItems(plist);
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
} else {
|
||||||
|
fillItems(plist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 填充季
|
||||||
|
*
|
||||||
|
* @param SeriesId
|
||||||
|
*/
|
||||||
|
private void fillSeason(String SeriesId) {
|
||||||
|
JfClient.GetSeasons(SeriesId, new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Items seasons) {
|
||||||
|
fillItems(seasons.getItems());
|
||||||
|
}
|
||||||
|
}, errcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 填充集
|
||||||
|
*
|
||||||
|
* @param SeriesId
|
||||||
|
* @param SeasonId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private void fillEpisodes(String SeriesId, String SeasonId) {
|
||||||
|
JfClient.GetEpisodes(SeriesId, SeasonId, new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Items episodes) {
|
||||||
|
fillItems(episodes.getItems());
|
||||||
|
}
|
||||||
|
}, errcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillItems(List<Item> items) {
|
||||||
|
JAdapter jAdapter = new JAdapter(items, false);
|
||||||
|
V7LinearLayoutManager layoutManager = new V7LinearLayoutManager(mGridView.getContext());
|
||||||
|
layoutManager.setOrientation(V7LinearLayoutManager.HORIZONTAL);
|
||||||
|
jAdapter.setOnItemClickListener(this);
|
||||||
|
mGridView.setVisibility(View.VISIBLE);
|
||||||
|
mGridView.setLayoutManager(layoutManager);
|
||||||
|
mGridView.setAdapter(jAdapter);
|
||||||
|
dismissLoadingDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 填充演员表
|
||||||
|
*
|
||||||
|
* @param Peoples
|
||||||
|
*/
|
||||||
|
private void fillPeople(List<People> Peoples) {
|
||||||
|
List<Item> Pitems = new ArrayList<>();
|
||||||
|
for (People p : Peoples) {
|
||||||
|
Item it = new Item();
|
||||||
|
it.setId(p.getId());
|
||||||
|
if (p.getType().equals("Director")) {
|
||||||
|
it.setName("导演:" + p.getName());
|
||||||
|
} else if (p.getType().equals("Actor")) {
|
||||||
|
it.setName("演员:" + p.getName());
|
||||||
|
} else {
|
||||||
|
it.setName(p.getName());
|
||||||
|
}
|
||||||
|
it.setType(p.getType());
|
||||||
|
it.setImageTags(new ImageTags());
|
||||||
|
it.getImageTags().setPrimary(p.getPrimaryImageTag());
|
||||||
|
Pitems.add(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
tvPeopleLayout.setVisibility(View.VISIBLE);
|
||||||
|
JAdapter jAdapter = new JAdapter(Pitems, false);
|
||||||
|
V7LinearLayoutManager layoutManager = new V7LinearLayoutManager(mPeopleGridView.getContext());
|
||||||
|
layoutManager.setOrientation(V7LinearLayoutManager.HORIZONTAL);
|
||||||
|
jAdapter.setOnItemClickListener(this);
|
||||||
|
mPeopleGridView.setLayoutManager(layoutManager);
|
||||||
|
mPeopleGridView.setAdapter(jAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 填充演员作品
|
||||||
|
*
|
||||||
|
* @param personid
|
||||||
|
*/
|
||||||
|
private void fillItemsByPerson(String personid) {
|
||||||
|
String Term = "&SortBy=DateCreated&SortOrder=Descending&PersonIds=" + personid;
|
||||||
|
Term += "&IncludeItemTypes=Movie,Series";
|
||||||
|
JfClient.GetItemsByTerm(Term, new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Items iitems) {
|
||||||
|
((TextView) findViewById(R.id.tvListTitle)).setText("演员作品:");
|
||||||
|
List<Item> items = iitems.getItems();
|
||||||
|
JAdapter jAdapter = new JAdapter(items, false);
|
||||||
|
V7GridLayoutManager layoutManager = new V7GridLayoutManager(mGridView.getContext(), 4);
|
||||||
|
jAdapter.setOnItemClickListener(new JAdapter.OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(Item item) {
|
||||||
|
String itemId = item.getId();
|
||||||
|
Intent intent = new Intent(DetailActivity.this, DetailActivity.class);
|
||||||
|
intent.putExtra("itemId", itemId);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mGridView.setVisibility(View.VISIBLE);
|
||||||
|
mGridView.setLayoutManager(layoutManager);
|
||||||
|
mGridView.setAdapter(jAdapter);
|
||||||
|
dismissLoadingDialog();
|
||||||
|
}
|
||||||
|
}, errcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(Item item) {
|
||||||
|
String itemId = item.getId();
|
||||||
|
String type = item.getType();
|
||||||
|
Intent intent = null;
|
||||||
|
if (type.equals("Season")) {
|
||||||
|
intent = new Intent(this, DetailActivity.class);
|
||||||
|
intent.putExtra("itemId", itemId);
|
||||||
|
startActivity(intent);
|
||||||
|
} else if (type.equals("Episode")) {
|
||||||
|
JfClient.playList.clear();
|
||||||
|
JAdapter JA = (JAdapter) mGridView.getAdapter();
|
||||||
|
List<Item> ja = JA.getData();
|
||||||
|
if (ja != null) {
|
||||||
|
for (int i = 0; i < ja.size(); i++) {
|
||||||
|
Video media = getMedia(ja.get(i));
|
||||||
|
JfClient.playList.add(media);
|
||||||
|
if (itemId.equals(media.Id)) {
|
||||||
|
JfClient.playIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toVlcPlayer();
|
||||||
|
}
|
||||||
|
} else if (type.equals("Movie") || type.equals("Video")) {
|
||||||
|
JfClient.playList.clear();
|
||||||
|
JfClient.playList.add(getMedia(item));
|
||||||
|
JfClient.playIndex = 0;
|
||||||
|
toVlcPlayer();
|
||||||
|
} else if (type.equals("Actor") || type.equals("Director")) {
|
||||||
|
intent = new Intent(this, DetailActivity.class);
|
||||||
|
intent.putExtra("itemId", itemId);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组合播放媒体
|
||||||
|
*
|
||||||
|
* @param item
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Video getMedia(Item item) {
|
||||||
|
Video media = new Video();
|
||||||
|
media.Id = item.getId();
|
||||||
|
media.Name = item.getName();
|
||||||
|
media.cover = "";
|
||||||
|
media.Url = JfClient.GetPlayUrl(media.Id);
|
||||||
|
if (item.getUserData() != null) {
|
||||||
|
UserData userdata = item.getUserData();
|
||||||
|
media.startPositionTicks = userdata.getPlaybackPositionTicks();
|
||||||
|
}
|
||||||
|
return media;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toVlcPlayer() {
|
||||||
|
Intent intent;
|
||||||
|
if(JfClient.config.isExtensionPlayer()){
|
||||||
|
String videourl = JfClient.playList.get(JfClient.playIndex).Url;
|
||||||
|
Uri uri = Uri.parse(videourl);
|
||||||
|
intent = new Intent();
|
||||||
|
intent.setAction(Intent.ACTION_VIEW);
|
||||||
|
intent.setDataAndType(uri,"video/mp4");
|
||||||
|
}else{
|
||||||
|
intent = new Intent(this, VlcPlayerActivity.class);
|
||||||
|
}
|
||||||
|
this.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JfClient.JJCallBack errcb = new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onError(String str) {
|
||||||
|
ShowToask(str);
|
||||||
|
dismissLoadingDialog();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
235
app/src/main/java/org/sifacai/vlcjellyfin/HomeActivity.java
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
package org.sifacai.vlcjellyfin;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.owen.tvrecyclerview.widget.V7LinearLayoutManager;
|
||||||
|
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.Item;
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.Items;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HomeActivity extends BaseActivity {
|
||||||
|
private final String TAG = "HomeActivity";
|
||||||
|
private LinearLayout tvContiner;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.jellyfin_home);
|
||||||
|
|
||||||
|
disableActiveBarBack();
|
||||||
|
|
||||||
|
tvContiner = findViewById(R.id.tvItems);
|
||||||
|
|
||||||
|
JfClient.init(getApplication());
|
||||||
|
}
|
||||||
|
|
||||||
|
private JfClient.JJCallBack connErr = new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onError(String str) {
|
||||||
|
dismissLoadingDialog();
|
||||||
|
ShowToask(str);
|
||||||
|
showLoginDialog();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
initData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录框
|
||||||
|
*/
|
||||||
|
private void showLoginDialog() {
|
||||||
|
Log.d(TAG, "showLoginDialog: 跳出登录框");
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
AlertDialog alert = builder.setTitle("登录")
|
||||||
|
.setMessage("请输入登录信息")
|
||||||
|
.setView(R.layout.dialog_login)
|
||||||
|
.show();
|
||||||
|
alert.setCanceledOnTouchOutside(false);
|
||||||
|
EditText urlbox = alert.findViewById(R.id.dialog_login_url);
|
||||||
|
EditText unbox = alert.findViewById(R.id.dialog_login_un);
|
||||||
|
EditText pwbox = alert.findViewById(R.id.dialog_login_pw);
|
||||||
|
CheckBox saveBox = alert.findViewById(R.id.dialog_login_save);
|
||||||
|
TextView submit = alert.findViewById(R.id.dialog_login_submit);
|
||||||
|
TextView cancel = alert.findViewById(R.id.dialog_login_cancel);
|
||||||
|
urlbox.setText(JfClient.config.getJellyfinUrl());
|
||||||
|
unbox.setText(JfClient.config.getUserName());
|
||||||
|
pwbox.setText(JfClient.config.getPassWord());
|
||||||
|
cancel.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
submit.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
showLoadingDialog("正在验证服务器地址!");
|
||||||
|
JfClient.VerityServerUrl(urlbox.getText().toString(), new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Boolean bool) {
|
||||||
|
setLoadingText("正在验证用户名和密码!");
|
||||||
|
JfClient.AuthenticateByName(unbox.getText().toString(), pwbox.getText().toString(), new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Boolean bool) {
|
||||||
|
dismissLoadingDialog();
|
||||||
|
alert.dismiss();
|
||||||
|
initView(); //加载首页
|
||||||
|
}
|
||||||
|
}, new JfClient.JJCallBack(){
|
||||||
|
@Override
|
||||||
|
public void onError(String str) {
|
||||||
|
dismissLoadingDialog();
|
||||||
|
ShowToask("用户名密码验证失败!");
|
||||||
|
}
|
||||||
|
}, saveBox.isChecked());
|
||||||
|
|
||||||
|
}
|
||||||
|
}, new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onError(String str) {
|
||||||
|
dismissLoadingDialog();
|
||||||
|
ShowToask("服务器地址不正确!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initData(){
|
||||||
|
if (JfClient.AccessToken.equals("") || JfClient.UserId.equals("")) {
|
||||||
|
showLoadingDialog("正在验证服务器地址!");
|
||||||
|
JfClient.VerityServerUrl(JfClient.config.getJellyfinUrl(), new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Boolean bool) {
|
||||||
|
setLoadingText("正在验证用户名和密码!");
|
||||||
|
JfClient.AuthenticateByName(JfClient.config.getUserName(), JfClient.config.getPassWord(), new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Boolean bool) {
|
||||||
|
dismissLoadingDialog();
|
||||||
|
initView();
|
||||||
|
}
|
||||||
|
}, connErr, false);
|
||||||
|
}
|
||||||
|
}, connErr);
|
||||||
|
}else{
|
||||||
|
Log.d(TAG, "initData: 跳出");
|
||||||
|
initView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initView() {
|
||||||
|
showLoadingDialog("正在加载首页…………");
|
||||||
|
tvContiner.removeAllViews();
|
||||||
|
JfClient.GetViews(new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Items views) {
|
||||||
|
List<Item> items = views.getItems();
|
||||||
|
addRowTvRecyclerView("我的媒体", items, true);
|
||||||
|
for (int i = 0; i < items.size(); i++) {
|
||||||
|
Item item = items.get(i);
|
||||||
|
JfClient.GetLatest(item.getId(), new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Items latests) {
|
||||||
|
addRowTvRecyclerView("新的 " + item.getName(), latests.getItems(), false);
|
||||||
|
}
|
||||||
|
}, errcb);
|
||||||
|
}
|
||||||
|
dismissLoadingDialog();
|
||||||
|
}
|
||||||
|
}, errcb);
|
||||||
|
JfClient.GetResume(new JfClient.JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Items resumes) {
|
||||||
|
for(Item it : resumes.getItems()){
|
||||||
|
String SeriesName = it.getSeriesName() == null ? "" : it.getSeriesName() + "-";
|
||||||
|
String SeasonName = it.getSeasonName() == null ? "" : it.getSeasonName() + "-";
|
||||||
|
it.setName(SeriesName+SeasonName+it.getName());
|
||||||
|
}
|
||||||
|
addRowTvRecyclerView("最近播放", resumes.getItems(), false);
|
||||||
|
}
|
||||||
|
}, errcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加类别行
|
||||||
|
*/
|
||||||
|
private void addRowTvRecyclerView(String title, List<Item> items, boolean horizon) {
|
||||||
|
JRecyclerView tvRecyclerView = (JRecyclerView) LayoutInflater.from(this)
|
||||||
|
.inflate(R.layout.home_horizon_tvrecycler, null);
|
||||||
|
((V7LinearLayoutManager) tvRecyclerView.getLayoutManager()).setOrientation(V7LinearLayoutManager.HORIZONTAL);
|
||||||
|
|
||||||
|
JAdapter jAdapter = new JAdapter(items, horizon);
|
||||||
|
jAdapter.setOnItemClickListener(new JAdapter.OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(Item item) {
|
||||||
|
String type = item.getType();
|
||||||
|
String itemId = item.getId();
|
||||||
|
Intent intent = null;
|
||||||
|
if (type.equals("Folder") || type.equals("CollectionFolder")) {
|
||||||
|
intent = new Intent(mAA, CollectionActivity.class);
|
||||||
|
} else {
|
||||||
|
intent = new Intent(mAA, DetailActivity.class);
|
||||||
|
}
|
||||||
|
intent.putExtra("itemId", itemId);
|
||||||
|
mAA.startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tvRecyclerView.setAdapter(jAdapter);
|
||||||
|
|
||||||
|
TextView titleView = new TextView(tvRecyclerView.getContext());
|
||||||
|
titleView.setText(title);
|
||||||
|
tvContiner.addView(titleView);
|
||||||
|
tvContiner.addView(tvRecyclerView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
|
exit();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return super.onKeyDown(keyCode, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long exitTime = 0;
|
||||||
|
|
||||||
|
public void exit() {
|
||||||
|
if ((System.currentTimeMillis() - exitTime) > 2000) {
|
||||||
|
Toast.makeText(getApplicationContext(), "再按一次退出程序",
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
exitTime = System.currentTimeMillis();
|
||||||
|
} else {
|
||||||
|
finish();
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JfClient.JJCallBack errcb = new JfClient.JJCallBack(){
|
||||||
|
@Override
|
||||||
|
public void onError(String str) {
|
||||||
|
dismissLoadingDialog();
|
||||||
|
ShowToask(str);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
147
app/src/main/java/org/sifacai/vlcjellyfin/JAdapter.java
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
package org.sifacai.vlcjellyfin;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.owen.tvrecyclerview.widget.TvRecyclerView;
|
||||||
|
import com.squareup.picasso.Picasso;
|
||||||
|
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.Item;
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.Items;
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.People;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class JAdapter extends RecyclerView.Adapter {
|
||||||
|
private String TAG = "JAdapter:";
|
||||||
|
private List<Item> items;
|
||||||
|
private boolean horizon;
|
||||||
|
|
||||||
|
class VH extends RecyclerView.ViewHolder{
|
||||||
|
public String id;
|
||||||
|
public String type;
|
||||||
|
public TextView tvName;
|
||||||
|
public ImageView tvCover;
|
||||||
|
public SeekBar tvPlayedPercentage;
|
||||||
|
|
||||||
|
public VH(View v) {
|
||||||
|
super(v);
|
||||||
|
id = "";
|
||||||
|
type = "";
|
||||||
|
tvName = v.findViewById(R.id.tvName);
|
||||||
|
tvCover = v.findViewById(R.id.ivThumb);
|
||||||
|
tvPlayedPercentage = v.findViewById(R.id.tvPlayedPercentage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JAdapter(List<Item> items) {
|
||||||
|
this.items = items;
|
||||||
|
this.horizon = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JAdapter(List<Item> items,Boolean horizon) {
|
||||||
|
this.items = items;
|
||||||
|
this.horizon = horizon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View v = null;
|
||||||
|
if(horizon){
|
||||||
|
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_h,parent,false);
|
||||||
|
}else{
|
||||||
|
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_v,parent,false);
|
||||||
|
}
|
||||||
|
return new VH(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||||
|
VH v = (VH)holder;
|
||||||
|
Item item = items.get(position);
|
||||||
|
v.id = item.getId();
|
||||||
|
v.tvName.setText(item.getName());
|
||||||
|
|
||||||
|
if(item.getUserData() != null){
|
||||||
|
int pp = (int) Math.round(item.getUserData().getPlayedPercentage());
|
||||||
|
v.tvPlayedPercentage.setProgress(pp);
|
||||||
|
v.tvPlayedPercentage.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
v.type = item.getType();
|
||||||
|
String imgUrl = JfClient.GetImgUrl(item.getId(),item.getImageTags().getPrimary());
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(imgUrl)) {
|
||||||
|
Picasso.get()
|
||||||
|
.load(imgUrl)
|
||||||
|
.placeholder(R.drawable.img_loading_placeholder)
|
||||||
|
.error(R.drawable.img_loading_placeholder)
|
||||||
|
.into(v.tvCover);
|
||||||
|
} else {
|
||||||
|
v.tvCover.setImageResource(R.drawable.img_loading_placeholder);
|
||||||
|
}
|
||||||
|
v.itemView.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
listener.onClick(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Item> getData(){
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
int count = items.size();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置数据
|
||||||
|
* @param items
|
||||||
|
*/
|
||||||
|
public void setItems(List<Item> items){
|
||||||
|
this.items = items;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加数据
|
||||||
|
* @param items
|
||||||
|
*/
|
||||||
|
public void addItems(List<Item> items){
|
||||||
|
int c = this.items.size();
|
||||||
|
this.items.addAll(items);
|
||||||
|
notifyItemRangeInserted(c,items.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearItems(){
|
||||||
|
this.items = new ArrayList<>();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
//定义OnItemClickListener接口
|
||||||
|
public interface OnItemClickListener {
|
||||||
|
void onClick(Item item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OnItemClickListener listener;
|
||||||
|
|
||||||
|
public void setOnItemClickListener(OnItemClickListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
}
|
44
app/src/main/java/org/sifacai/vlcjellyfin/JRecyclerView.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package org.sifacai.vlcjellyfin;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.animation.BounceInterpolator;
|
||||||
|
|
||||||
|
import com.owen.tvrecyclerview.widget.TvRecyclerView;
|
||||||
|
|
||||||
|
public class JRecyclerView extends TvRecyclerView {
|
||||||
|
public JRecyclerView(Context context) {
|
||||||
|
super(context);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JRecyclerView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JRecyclerView(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(Context context){
|
||||||
|
setOnItemListener(new TvRecyclerView.OnItemListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemPreSelected(TvRecyclerView parent, View itemView, int position) {
|
||||||
|
itemView.animate().scaleX(1.0f).scaleY(1.0f).setDuration(300).setInterpolator(new BounceInterpolator()).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(TvRecyclerView parent, View itemView, int position) {
|
||||||
|
itemView.animate().scaleX(1.05f).scaleY(1.05f).setDuration(300).setInterpolator(new BounceInterpolator()).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemClick(TvRecyclerView parent, View itemView, int position) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
713
app/src/main/java/org/sifacai/vlcjellyfin/JfClient.java
Normal file
@ -0,0 +1,713 @@
|
|||||||
|
package org.sifacai.vlcjellyfin;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.lzy.okgo.OkGo;
|
||||||
|
import com.lzy.okgo.callback.AbsCallback;
|
||||||
|
import com.lzy.okgo.model.HttpHeaders;
|
||||||
|
import com.lzy.okgo.model.Response;
|
||||||
|
import com.squareup.picasso.OkHttp3Downloader;
|
||||||
|
import com.squareup.picasso.Picasso;
|
||||||
|
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.Item;
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.Items;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.net.ssl.HostnameVerifier;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
|
||||||
|
public class JfClient {
|
||||||
|
public static final String TAG = "JellyfinClient";
|
||||||
|
public static final String XEmbyAuthorization = "MediaBrowser Client=\"Vlc_J_TV\", Device=\"Vlc_J_TV\", DeviceId=\"TW96aWxsYS81LjAgKFdpbmRvd3MgTlQgNi4xOyBXa\", Version=\"10.8.1\"";
|
||||||
|
public static HttpHeaders headers;
|
||||||
|
public static Config config;
|
||||||
|
public static String UserId = "";
|
||||||
|
public static String AccessToken = "";
|
||||||
|
|
||||||
|
public static int playIndex = 0; //当前播放
|
||||||
|
public static ArrayList<Video> playList = new ArrayList<>(); //播放列表
|
||||||
|
|
||||||
|
public enum ReportType {
|
||||||
|
playing,
|
||||||
|
Progress,
|
||||||
|
stop
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* description 忽略https证书验证
|
||||||
|
*
|
||||||
|
* @author yanzy
|
||||||
|
* @version 1.0
|
||||||
|
* @date 2021/9/8 14:42
|
||||||
|
*/
|
||||||
|
private static TrustManager[] getTrustManager() {
|
||||||
|
TrustManager[] trustAllCerts = new TrustManager[]{
|
||||||
|
new X509TrustManager() {
|
||||||
|
@Override
|
||||||
|
public void checkClientTrusted(X509Certificate[] chain, String authType) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkServerTrusted(X509Certificate[] chain, String authType) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return new X509Certificate[]{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return trustAllCerts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* description 忽略https证书验证
|
||||||
|
*`在这里插入代码片`
|
||||||
|
* @author yanzy
|
||||||
|
* @version 1.0
|
||||||
|
* @date 2021/9/8 14:42
|
||||||
|
*/
|
||||||
|
public static SSLSocketFactory getSSLSocketFactory() {
|
||||||
|
try {
|
||||||
|
SSLContext sslContext = SSLContext.getInstance("SSL");
|
||||||
|
sslContext.init(null, getTrustManager(), new SecureRandom());
|
||||||
|
return sslContext.getSocketFactory();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* description 忽略https证书验证
|
||||||
|
*
|
||||||
|
* @author yanzy
|
||||||
|
* @version 1.0
|
||||||
|
* @date 2021/9/8 14:42
|
||||||
|
*/
|
||||||
|
public static HostnameVerifier getHostnameVerifier() {
|
||||||
|
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
|
||||||
|
@Override
|
||||||
|
public boolean verify(String s, SSLSession sslSession) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return hostnameVerifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化配置
|
||||||
|
*
|
||||||
|
* @param application
|
||||||
|
*/
|
||||||
|
public static void init(Application application) {
|
||||||
|
config = new Config(application);
|
||||||
|
SetHeaders();
|
||||||
|
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
||||||
|
builder.sslSocketFactory(getSSLSocketFactory());
|
||||||
|
builder.hostnameVerifier(getHostnameVerifier());
|
||||||
|
builder.connectTimeout(5, TimeUnit.SECONDS);
|
||||||
|
builder.readTimeout(3, TimeUnit.SECONDS);
|
||||||
|
builder.writeTimeout(5, TimeUnit.SECONDS);
|
||||||
|
OkGo.getInstance().init(application)
|
||||||
|
.setOkHttpClient(builder.build())
|
||||||
|
.setRetryCount(3);
|
||||||
|
Picasso.setSingletonInstance(new Picasso.Builder(application.getBaseContext())
|
||||||
|
.downloader(new OkHttp3Downloader(builder.build()))
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回放报告
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* @param id
|
||||||
|
* @param PositionTicks
|
||||||
|
*/
|
||||||
|
public static void ReportPlayBackState(ReportType type, String id, long PositionTicks) {
|
||||||
|
String url = config.getJellyfinUrl();
|
||||||
|
if (type == ReportType.playing) {
|
||||||
|
url += "/Sessions/Playing";
|
||||||
|
} else if (type == ReportType.Progress) {
|
||||||
|
url += "/Sessions/Playing/Progress";
|
||||||
|
} else if (type == ReportType.stop) {
|
||||||
|
url += "/Sessions/Playing/Stopped";
|
||||||
|
}
|
||||||
|
String reqstr = "{\"itemId\":\"" + id + "\",\"PositionTicks\":\"" + PositionTicks * 10000 + "\"}";
|
||||||
|
SendPost(url, reqstr, new JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String str) {
|
||||||
|
//回放报告
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取封面图url
|
||||||
|
*
|
||||||
|
* @param itemid
|
||||||
|
* @param tagid
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String GetImgUrl(String itemid, String tagid) {
|
||||||
|
String url = config.getJellyfinUrl() + "/Items/" + itemid + "/Images/Primary";
|
||||||
|
url += "?fillHeight=286&fillWidth=200&quality=96&tag=" + tagid;
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String GetImgUrl(Item item) {
|
||||||
|
if (item.getImageTags() == null) return "";
|
||||||
|
if (item.getImageTags().getPrimary() == null || item.getImageTags().getPrimary().equals(""))
|
||||||
|
return "";
|
||||||
|
return GetImgUrl(item.getId(), item.getImageTags().getPrimary());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取播放url
|
||||||
|
*
|
||||||
|
* @param itemid
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String GetPlayUrl(String itemid) {
|
||||||
|
String playurl = config.getJellyfinUrl() + "/videos/" + itemid + "/stream.mp4?static=true";
|
||||||
|
return playurl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SearchByTerm(String term, int limit, JJCallBack scb, JJCallBack errcb) {
|
||||||
|
String BaseUrl = config.getJellyfinUrl() + "/Users/" + UserId + "/Items?";
|
||||||
|
BaseUrl += "Fields=PrimaryImageAspectRatio,CanDelete,BasicSyncInfo,MediaSourceCount";
|
||||||
|
BaseUrl += "&Recursive=true&EnableTotalRecordCount=false&ImageTypeLimit=1&IncludePeople=false";
|
||||||
|
BaseUrl += "&IncludeMedia=true&IncludeGenres=false&IncludeStudios=false&IncludeArtists=false";
|
||||||
|
BaseUrl += "&Limit=" + limit;
|
||||||
|
|
||||||
|
String PersonUrl = config.getJellyfinUrl() + "/Persons?Fields=PrimaryImageAspectRatio%2CCanDelete%2CBasicSyncInfo%2CMediaSourceCount&Recursive=true";
|
||||||
|
PersonUrl += "&EnableTotalRecordCount=false&ImageTypeLimit=1&IncludePeople=true&IncludeMedia=false";
|
||||||
|
PersonUrl += "&IncludeGenres=false&IncludeStudios=false&IncludeArtists=false&userId=" + UserId;
|
||||||
|
String personUrl = PersonUrl + "&searchTerm=" + term + "&Limit=" + limit;
|
||||||
|
|
||||||
|
String movieUrl = BaseUrl + "&searchTerm=" + term + "&IncludeItemTypes=Movie";
|
||||||
|
String seriesUrl = BaseUrl + "&searchTerm=" + term + "&IncludeItemTypes=Series";
|
||||||
|
String episodeUrl = BaseUrl + "&searchTerm=" + term + "&IncludeItemTypes=Episode";
|
||||||
|
|
||||||
|
JsonArray items = new JsonArray();
|
||||||
|
new Thread(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
Items items = new Items();
|
||||||
|
items.setItems(new ArrayList<>());
|
||||||
|
String jsonstr = SendGet(movieUrl);
|
||||||
|
Items moviejob = Utils.jsonToClass(jsonstr, Items.class);
|
||||||
|
if (moviejob != null) {
|
||||||
|
items.AddItems(moviejob.getItems());
|
||||||
|
items.setTotalRecordCount(items.getTotalRecordCount() + moviejob.getTotalRecordCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonstr = SendGet(seriesUrl);
|
||||||
|
Items seriesobj = Utils.jsonToClass(jsonstr, Items.class);
|
||||||
|
if (seriesobj != null) {
|
||||||
|
items.AddItems(seriesobj.getItems());
|
||||||
|
items.setTotalRecordCount(items.getTotalRecordCount() + seriesobj.getTotalRecordCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonstr = SendGet(personUrl);
|
||||||
|
Items personobj = Utils.jsonToClass(jsonstr, Items.class);
|
||||||
|
if (personobj != null) {
|
||||||
|
items.AddItems(personobj.getItems());
|
||||||
|
items.setTotalRecordCount(items.getTotalRecordCount() + personobj.getTotalRecordCount());
|
||||||
|
}
|
||||||
|
scb.onSuccess(items);
|
||||||
|
} catch (Exception e) {
|
||||||
|
errcb.onError(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目附加部分
|
||||||
|
*
|
||||||
|
* @param itemid
|
||||||
|
* @param cb
|
||||||
|
*/
|
||||||
|
public static void GetAddPart(String itemid, JJCallBack cb, JJCallBack errcb) {
|
||||||
|
String AddPartUrl = config.getJellyfinUrl() + "/Videos/" + itemid + "/AdditionalParts?userId=" + UserId;
|
||||||
|
SendGet(AddPartUrl, new JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String str) {
|
||||||
|
try {
|
||||||
|
Items items = Utils.jsonToClass(str, Items.class);
|
||||||
|
cb.onSuccess(items);
|
||||||
|
} catch (Exception e) {
|
||||||
|
errcb.onError(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, errcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取剧集
|
||||||
|
*
|
||||||
|
* @param seriesId 剧ID
|
||||||
|
* @param seasonId 季ID
|
||||||
|
* @param cb
|
||||||
|
*/
|
||||||
|
public static void GetEpisodes(String seriesId, String seasonId, JJCallBack cb, JJCallBack errcb) {
|
||||||
|
String EpisodesUrl = config.getJellyfinUrl() + "/Shows/" + seriesId + "/Episodes?seasonId=" + seasonId;
|
||||||
|
EpisodesUrl += "&userId=" + UserId;
|
||||||
|
EpisodesUrl += "&Fields=ItemCounts,PrimaryImageAspectRatio,BasicSyncInfo,CanDelete,MediaSourceCount,Overview";
|
||||||
|
|
||||||
|
SendGet(EpisodesUrl, new JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String str) {
|
||||||
|
try {
|
||||||
|
Items items = Utils.jsonToClass(str, Items.class);
|
||||||
|
cb.onSuccess(items);
|
||||||
|
} catch (Exception e) {
|
||||||
|
errcb.onError(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, errcb);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取季(Seasons)数据
|
||||||
|
*
|
||||||
|
* @param seriesId 剧ID
|
||||||
|
* @param cb
|
||||||
|
*/
|
||||||
|
public static void GetSeasons(String seriesId, JJCallBack cb, JJCallBack errcb) {
|
||||||
|
String SeasonsUrl = config.getJellyfinUrl() + "/Shows/" + seriesId + "/Seasons?userId=" + UserId;
|
||||||
|
SeasonsUrl += "&Fields=ItemCounts,PrimaryImageAspectRatio,BasicSyncInfo,MediaSourceCount";
|
||||||
|
|
||||||
|
SendGet(SeasonsUrl, new JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String str) {
|
||||||
|
try {
|
||||||
|
Items items = Utils.jsonToClass(str, Items.class);
|
||||||
|
cb.onSuccess(items);
|
||||||
|
} catch (Exception e) {
|
||||||
|
errcb.onError(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}, errcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据条件获取条目列表
|
||||||
|
*
|
||||||
|
* @param term 可以是 &Limit、&SortBy、&IncludeItemTypes、&PersonIds 等
|
||||||
|
* @param scb
|
||||||
|
* @param errcb
|
||||||
|
*/
|
||||||
|
public static void GetItemsByTerm(String term, JJCallBack scb, JJCallBack errcb) {
|
||||||
|
String BaseUrl = config.getJellyfinUrl() + "/Users/" + UserId + "/Items?Recursive=true&StartIndex=0&CollapseBoxSetItems=false";
|
||||||
|
BaseUrl += term;
|
||||||
|
|
||||||
|
SendGet(BaseUrl, new JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String str) {
|
||||||
|
try {
|
||||||
|
Items items = Utils.jsonToClass(str, Items.class);
|
||||||
|
scb.onSuccess(items);
|
||||||
|
} catch (Exception e) {
|
||||||
|
errcb.onError(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, errcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合集条目
|
||||||
|
*
|
||||||
|
* @param parentId
|
||||||
|
* @param type 类型 movie、tvshows……
|
||||||
|
* @param sortBy 排序条件 评分、加入时间……
|
||||||
|
* @param sortOrder 排序规则 升序、降序
|
||||||
|
* @param limit 每页条数
|
||||||
|
* @param page 页
|
||||||
|
* @param cb
|
||||||
|
*/
|
||||||
|
public static void GetCollection(String parentId, String type, String sortBy, String sortOrder, int limit, int page, JJCallBack cb, JJCallBack errcb) {
|
||||||
|
String itemsUrl = config.getJellyfinUrl() + "/Users/" + UserId + "/Items?ParentId=" + parentId + "&Limit=" + limit;
|
||||||
|
itemsUrl += "&Recursive=true&Fields=PrimaryImageAspectRatio,BasicSyncInfo,Seasons,Episodes&ImageTypeLimit=1";
|
||||||
|
itemsUrl += "&EnableImageTypes=Primary,Backdrop,Banner,Thumb";
|
||||||
|
itemsUrl += "&SortBy=" + sortBy + ",SortName,ProductionYear&SortOrder=" + sortOrder;
|
||||||
|
|
||||||
|
if (type.equals("tvshows")) {
|
||||||
|
itemsUrl += "&IncludeItemTypes=Series";
|
||||||
|
} else if (type.equals("movies")) {
|
||||||
|
itemsUrl += "&IncludeItemTypes=Movie";
|
||||||
|
} else {
|
||||||
|
itemsUrl += "&IncludeItemTypes=Movie,Series";
|
||||||
|
}
|
||||||
|
int startIndex = page * limit - limit;
|
||||||
|
itemsUrl += "&StartIndex=" + startIndex;
|
||||||
|
|
||||||
|
SendGet(itemsUrl, new JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String str) {
|
||||||
|
try {
|
||||||
|
Items items = Utils.jsonToClass(str, Items.class);
|
||||||
|
cb.onSuccess(items);
|
||||||
|
} catch (Exception e) {
|
||||||
|
errcb.onError(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}, errcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最新项目
|
||||||
|
*
|
||||||
|
* @param parentId
|
||||||
|
* @param cb
|
||||||
|
*/
|
||||||
|
public static void GetLatest(String parentId, JJCallBack cb, JJCallBack errcb) {
|
||||||
|
String lastestUrl = config.getJellyfinUrl() + "/Users/" + UserId + "/Items/Latest?";
|
||||||
|
lastestUrl += "Limit=16&Fields=PrimaryImageAspectRatio%2CBasicSyncInfo%2CPath";
|
||||||
|
lastestUrl += "&ImageTypeLimit=1&EnableImageTypes=Primary,Backdrop,Thumb";
|
||||||
|
lastestUrl += "&ParentId=" + parentId;
|
||||||
|
|
||||||
|
SendGet(lastestUrl, new JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String str) {
|
||||||
|
Type type = new TypeToken<List<Item>>() {
|
||||||
|
}.getType();
|
||||||
|
List<Item> rets = Utils.jsonToClass(str, type);
|
||||||
|
if (null != rets) {
|
||||||
|
Items items = new Items();
|
||||||
|
items.setItems(rets);
|
||||||
|
items.setTotalRecordCount(rets.size());
|
||||||
|
cb.onSuccess(items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, errcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最近播放
|
||||||
|
*
|
||||||
|
* @param cb
|
||||||
|
*/
|
||||||
|
public static void GetResume(JJCallBack cb, JJCallBack err) {
|
||||||
|
String resumeUrl = config.getJellyfinUrl() + "/Users/" + UserId + "/Items/Resume?";
|
||||||
|
resumeUrl += "Limit=12&Recursive=true&Fields=PrimaryImageAspectRatio,BasicSyncInfo";
|
||||||
|
resumeUrl += "&ImageTypeLimit=1&EnableImageTypes=Primary,Backdrop,Thumb";
|
||||||
|
resumeUrl += "&EnableTotalRecordCount=false&MediaTypes=Video";
|
||||||
|
|
||||||
|
SendGet(resumeUrl, new JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String str) {
|
||||||
|
try {
|
||||||
|
Items items = Utils.jsonToClass(str, Items.class);
|
||||||
|
cb.onSuccess(items);
|
||||||
|
} catch (Exception e) {
|
||||||
|
err.onError(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取首页
|
||||||
|
*
|
||||||
|
* @param cb
|
||||||
|
*/
|
||||||
|
public static void GetViews(JJCallBack cb, JJCallBack err) {
|
||||||
|
String viewsUrl = config.getJellyfinUrl() + "/Users/" + UserId + "/Views";
|
||||||
|
SendGet(viewsUrl, new JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String str) {
|
||||||
|
try {
|
||||||
|
Items views = Utils.jsonToClass(str, Items.class);
|
||||||
|
cb.onSuccess(views);
|
||||||
|
} catch (Exception e) {
|
||||||
|
err.onError(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目详情
|
||||||
|
*
|
||||||
|
* @param itemid
|
||||||
|
* @param cb
|
||||||
|
*/
|
||||||
|
public static void GetItemInfo(String itemid, JJCallBack cb, JJCallBack err) {
|
||||||
|
String url = config.getJellyfinUrl() + "/Users/" + UserId + "/Items/" + itemid;
|
||||||
|
SendGet(url, new JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String str) {
|
||||||
|
try {
|
||||||
|
Item item = Utils.jsonToClass(str, Item.class);
|
||||||
|
cb.onSuccess(item);
|
||||||
|
} catch (Exception e) {
|
||||||
|
err.onError(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证用户名密码
|
||||||
|
*
|
||||||
|
* @param username
|
||||||
|
* @param password
|
||||||
|
* @param cb
|
||||||
|
*/
|
||||||
|
public static void AuthenticateByName(String username, String password, JJCallBack cb, JJCallBack err, boolean saveUser) {
|
||||||
|
if(username.equals("") || password.equals("")){
|
||||||
|
err.onError("用户名和密码验证失败!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String url = config.getJellyfinUrl() + "/Users/authenticatebyname";
|
||||||
|
String reqjson = "{\"Username\":\"" + username + "\",\"Pw\":\"" + password + "\"}";
|
||||||
|
SendPost(url, reqjson, new JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String str) {
|
||||||
|
try {
|
||||||
|
JsonObject userObj = new Gson().fromJson(str, JsonObject.class);
|
||||||
|
if (userObj.has("User") && userObj.has("AccessToken")) {
|
||||||
|
String userId = userObj.get("User").getAsJsonObject().get("Id").getAsString();
|
||||||
|
String Token = userObj.get("AccessToken").getAsString();
|
||||||
|
if (!Token.equals("")) {
|
||||||
|
UserId = userId;
|
||||||
|
AccessToken = Token;
|
||||||
|
if (saveUser) {
|
||||||
|
config.setUserName(username);
|
||||||
|
config.setPassWord(password);
|
||||||
|
}
|
||||||
|
SetHeaders();
|
||||||
|
cb.onSuccess(true);
|
||||||
|
} else {
|
||||||
|
err.onError("验证失败:" + str);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err.onError("验证失败:" + str);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
err.onError("验证失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户列表
|
||||||
|
*
|
||||||
|
* @param cb
|
||||||
|
*/
|
||||||
|
public static void GetUsers(JJCallBack cb, JJCallBack err) {
|
||||||
|
String url = config.getJellyfinUrl() + "/users/public";
|
||||||
|
SendGet(url, new JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String str) {
|
||||||
|
JsonArray users = new Gson().fromJson(str, JsonArray.class);
|
||||||
|
cb.onSuccess(users);
|
||||||
|
}
|
||||||
|
}, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证服务器地址
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @param cb
|
||||||
|
*/
|
||||||
|
public static void VerityServerUrl(String url, JJCallBack cb, JJCallBack err) {
|
||||||
|
if (url.startsWith("http://") || url.startsWith("https://")) {
|
||||||
|
SendGet(url + "/system/info/public", new JJCallBack() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String str) {
|
||||||
|
try {
|
||||||
|
JsonObject serverInfo = new Gson().fromJson(str, JsonObject.class);
|
||||||
|
String ServerId = "";
|
||||||
|
ServerId = serverInfo.get("Id").getAsString();
|
||||||
|
if (ServerId == null || ServerId.length() == 0) {
|
||||||
|
err.onError("服务器连接失败!");
|
||||||
|
} else {
|
||||||
|
config.setJellyfinUrl(url);
|
||||||
|
cb.onSuccess(true);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
err.onError(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, err);
|
||||||
|
} else {
|
||||||
|
err.onError("服务器地址不正确!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置请求头
|
||||||
|
*/
|
||||||
|
public static void SetHeaders() {
|
||||||
|
headers = new HttpHeaders();
|
||||||
|
String xea = XEmbyAuthorization;
|
||||||
|
if (null != AccessToken && AccessToken.length() > 0) {
|
||||||
|
xea += ", Token=\"" + AccessToken + "\"";
|
||||||
|
}
|
||||||
|
headers.put("Accept", "application/json");
|
||||||
|
headers.put("Accept-Language", "zh-CN,zh;q=0.9");
|
||||||
|
headers.put("X-Emby-Authorization", xea);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @param cb
|
||||||
|
*/
|
||||||
|
public static void SendGet(String url, JJCallBack cb, JJCallBack errcb) {
|
||||||
|
OkGo.<String>get(url).headers(headers).execute(new AbsCallback<String>() {
|
||||||
|
@Override
|
||||||
|
public String convertResponse(okhttp3.Response response) throws IOException {
|
||||||
|
String result = "";
|
||||||
|
if (null != response.body()) {
|
||||||
|
result = response.body().string();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Response<String> response) {
|
||||||
|
cb.onSuccess(response.body());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Response<String> response) {
|
||||||
|
if (errcb != null) {
|
||||||
|
errcb.onError(response.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @param jsonStr
|
||||||
|
* @param cb
|
||||||
|
*/
|
||||||
|
public static void SendPost(String url, String jsonStr, JJCallBack cb, JJCallBack errcb) {
|
||||||
|
OkGo.<String>post(url).headers(headers).upJson(jsonStr).execute(new AbsCallback<String>() {
|
||||||
|
@Override
|
||||||
|
public String convertResponse(okhttp3.Response response) throws Throwable {
|
||||||
|
String result = "";
|
||||||
|
if (null != response.body()) {
|
||||||
|
result = response.body().string();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Response<String> response) {
|
||||||
|
cb.onSuccess(response.body());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Response<String> response) {
|
||||||
|
if (errcb != null) {
|
||||||
|
errcb.onError(response.body());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 阻塞式Get
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
*/
|
||||||
|
public static String SendGet(String url) throws IOException {
|
||||||
|
String response = "";
|
||||||
|
response = OkGo.<String>get(url).headers(headers).execute().body().string();
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class JJCallBack implements JCallBack {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String str) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Boolean bool) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(JsonObject jsonObject) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(JsonArray jsonArray) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Item item) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Items items) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(String str) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface JCallBack {
|
||||||
|
void onSuccess(String str);
|
||||||
|
|
||||||
|
void onSuccess(Boolean bool);
|
||||||
|
|
||||||
|
void onSuccess(JsonObject jsonObject);
|
||||||
|
|
||||||
|
void onSuccess(JsonArray jsonArray);
|
||||||
|
|
||||||
|
void onSuccess(Item item);
|
||||||
|
|
||||||
|
void onSuccess(Items items);
|
||||||
|
|
||||||
|
void onError(String str);
|
||||||
|
}
|
||||||
|
}
|
106
app/src/main/java/org/sifacai/vlcjellyfin/PopMenu.java
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package org.sifacai.vlcjellyfin;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.PopupWindow;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class PopMenu extends PopupWindow {
|
||||||
|
|
||||||
|
public class menu{
|
||||||
|
public int groupid;
|
||||||
|
public int id;
|
||||||
|
public int orderid;
|
||||||
|
public String name;
|
||||||
|
public View v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private ArrayList<menu> items;
|
||||||
|
private View attView;
|
||||||
|
private ScrollView contentView;
|
||||||
|
private LinearLayout itemContiner;
|
||||||
|
|
||||||
|
public PopMenu(Context context, View attView) {
|
||||||
|
super(context);
|
||||||
|
this.context = context;
|
||||||
|
items = new ArrayList<>();
|
||||||
|
|
||||||
|
setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
setOutsideTouchable(true);
|
||||||
|
setFocusable(true);
|
||||||
|
setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||||
|
contentView = (ScrollView) LayoutInflater.from(context).inflate(R.layout.popmenu,null);
|
||||||
|
setContentView(contentView);
|
||||||
|
itemContiner = contentView.findViewById(R.id.popitemContiner);
|
||||||
|
this.attView = attView;
|
||||||
|
setClippingEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public menu add(int groupid,int id,int orderid,String name){
|
||||||
|
menu m = new menu();
|
||||||
|
m.groupid = groupid;
|
||||||
|
m.id = id;
|
||||||
|
m.orderid = orderid;
|
||||||
|
m.name = name;
|
||||||
|
View v = LayoutInflater.from(context).inflate(R.layout.popmenu_item,null);
|
||||||
|
m.v = v;
|
||||||
|
((TextView)v).setText(name);
|
||||||
|
items.add(m);
|
||||||
|
itemContiner.addView(v);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(){
|
||||||
|
if(items.size() > 12){
|
||||||
|
setHeight(500);
|
||||||
|
}
|
||||||
|
contentView.measure(makeDropDownMeasureSpec(getWidth())
|
||||||
|
,makeDropDownMeasureSpec(getHeight()));
|
||||||
|
int offx = 0;
|
||||||
|
int offy = contentView.getMeasuredHeight() + attView.getHeight() + 2;
|
||||||
|
showAsDropDown(attView,offx,-offy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(int index){
|
||||||
|
show();
|
||||||
|
// if(index >=0 && index < items.size()) {
|
||||||
|
// items.get(index).v.requestFocus();
|
||||||
|
// }
|
||||||
|
for (menu m:items) {
|
||||||
|
if(m.id == index){
|
||||||
|
m.v.requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(String name){
|
||||||
|
show();
|
||||||
|
for (menu m:items) {
|
||||||
|
if(m.name.equals(name)){
|
||||||
|
m.v.requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ResourceType")
|
||||||
|
private static int makeDropDownMeasureSpec(int measureSpec) {
|
||||||
|
int mode;
|
||||||
|
if (measureSpec == ViewGroup.LayoutParams.WRAP_CONTENT) {
|
||||||
|
mode = View.MeasureSpec.UNSPECIFIED;
|
||||||
|
} else {
|
||||||
|
mode = View.MeasureSpec.EXACTLY;
|
||||||
|
}
|
||||||
|
return View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(measureSpec), mode);
|
||||||
|
}
|
||||||
|
}
|
128
app/src/main/java/org/sifacai/vlcjellyfin/SearchActivity.java
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package org.sifacai.vlcjellyfin;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.telecom.Call;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.owen.tvrecyclerview.widget.TvRecyclerView;
|
||||||
|
import com.owen.tvrecyclerview.widget.V7GridLayoutManager;
|
||||||
|
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.Item;
|
||||||
|
import org.sifacai.vlcjellyfin.Bean.Items;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SearchActivity extends BaseActivity implements JAdapter.OnItemClickListener {
|
||||||
|
private TvRecyclerView mGridView;
|
||||||
|
private JAdapter adapter;
|
||||||
|
private final int limit = 24;
|
||||||
|
private String BaseUrl;
|
||||||
|
|
||||||
|
private EditText searchTermEdit;
|
||||||
|
private ImageView searchBtn;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_search);
|
||||||
|
|
||||||
|
if (JfClient.UserId.equals("") || JfClient.AccessToken.equals("")) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
if (null != actionBar) {
|
||||||
|
ImageView acb = actionBar.getCustomView().findViewById(R.id.actionBar_searchBtn);
|
||||||
|
if (null != acb) {
|
||||||
|
acb.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mGridView = findViewById(R.id.mGridView);
|
||||||
|
V7GridLayoutManager v7GridLayoutManager = new V7GridLayoutManager(this, 6);
|
||||||
|
mGridView.setLayoutManager(v7GridLayoutManager);
|
||||||
|
mGridView.setItemAnimator(null); //防崩溃
|
||||||
|
|
||||||
|
adapter = new JAdapter(new ArrayList<>());
|
||||||
|
adapter.setOnItemClickListener(this);
|
||||||
|
mGridView.setAdapter(adapter);
|
||||||
|
|
||||||
|
searchTermEdit = findViewById(R.id.searchTermEdit);
|
||||||
|
searchBtn = findViewById(R.id.searchBtn);
|
||||||
|
searchBtn.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
String st = searchTermEdit.getText().toString().trim();
|
||||||
|
if (st.length() > 0) {
|
||||||
|
adapter.clearItems();
|
||||||
|
Search(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchTermEdit.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Search(String searchTerm) {
|
||||||
|
showLoadingDialog("搜索中………………");
|
||||||
|
JfClient.SearchByTerm(searchTerm,16,new JfClient.JJCallBack(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Items items) {
|
||||||
|
mAA.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
adapter.addItems(items.getItems());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dismissLoadingDialog();
|
||||||
|
}
|
||||||
|
},new JfClient.JJCallBack(){
|
||||||
|
@Override
|
||||||
|
public void onError(String str) {
|
||||||
|
ShowToask("搜索时发生错误:" + str);
|
||||||
|
dismissLoadingDialog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillItems(List<Item> items) {
|
||||||
|
mAA.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
adapter.addItems(items);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(Item item) {
|
||||||
|
String itemId = item.getId();
|
||||||
|
String type = item.getType();
|
||||||
|
Intent intent = null;
|
||||||
|
switch (type) {
|
||||||
|
case "Series":
|
||||||
|
case "Season":
|
||||||
|
case "Episode":
|
||||||
|
case "Movie":
|
||||||
|
case "Video":
|
||||||
|
case "Person":
|
||||||
|
intent = new Intent(this, DetailActivity.class);
|
||||||
|
intent.putExtra("itemId", itemId);
|
||||||
|
this.startActivity(intent);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ShowToask("未知类型!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
110
app/src/main/java/org/sifacai/vlcjellyfin/Utils.java
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package org.sifacai.vlcjellyfin;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.TypeAdapter;
|
||||||
|
import com.google.gson.TypeAdapterFactory;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import com.google.gson.stream.JsonToken;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
import com.lzy.okgo.OkGo;
|
||||||
|
import com.lzy.okgo.callback.AbsCallback;
|
||||||
|
import com.lzy.okgo.callback.Callback;
|
||||||
|
import com.lzy.okgo.callback.StringCallback;
|
||||||
|
import com.lzy.okgo.model.HttpHeaders;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import okhttp3.Call;
|
||||||
|
import okhttp3.Headers;
|
||||||
|
import okhttp3.MediaType;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
|
public class Utils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标准时间转换
|
||||||
|
*
|
||||||
|
* @param utcTime
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String UtcToLocal(String utcTime) {
|
||||||
|
if(utcTime == null || utcTime.length() == 0) return "";
|
||||||
|
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z'");
|
||||||
|
df.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
String dt = "";
|
||||||
|
try {
|
||||||
|
Date date = df.parse(utcTime);
|
||||||
|
SimpleDateFormat sdt = new SimpleDateFormat("yyyy-MM-dd");
|
||||||
|
dt = sdt.format(date);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getPixelsFromDp(Activity context, int i) {
|
||||||
|
DisplayMetrics metrics = new DisplayMetrics();
|
||||||
|
context.getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
||||||
|
return (i * metrics.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T jsonToClass(String jsonstr, Type tClass) {
|
||||||
|
if (jsonstr != null && jsonstr.length() > 0) {
|
||||||
|
//Gson gson = new GsonBuilder().registerTypeAdapterFactory(new NullStringToEmptyAdapterFactory()).create();
|
||||||
|
Gson gson = new Gson();
|
||||||
|
return gson.fromJson(jsonstr, tClass);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NullStringToEmptyAdapterFactory<T> implements TypeAdapterFactory {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||||
|
Class<T> rawType = (Class<T>) type.getRawType();
|
||||||
|
if (rawType != String.class) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (TypeAdapter<T>) new StringNullAdapter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class StringNullAdapter extends TypeAdapter<String> {
|
||||||
|
@Override
|
||||||
|
public String read(JsonReader reader) throws IOException {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
if (reader.peek() == JsonToken.NULL) {
|
||||||
|
reader.nextNull();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return reader.nextString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JsonWriter writer, String value) throws IOException {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
if (value == null) {
|
||||||
|
writer.nullValue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
writer.value(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
app/src/main/java/org/sifacai/vlcjellyfin/Video.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package org.sifacai.vlcjellyfin;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
public class Video {
|
||||||
|
public String Id = "";
|
||||||
|
public String Url = null;
|
||||||
|
public String cover = "";
|
||||||
|
public String Name = "";
|
||||||
|
public long startPositionTicks = 0L;
|
||||||
|
public long PositionTicks = 0L;
|
||||||
|
}
|
619
app/src/main/java/org/sifacai/vlcjellyfin/VlcPlayerActivity.java
Normal file
@ -0,0 +1,619 @@
|
|||||||
|
package org.sifacai.vlcjellyfin;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.videolan.libvlc.Dialog;
|
||||||
|
import org.videolan.libvlc.LibVLC;
|
||||||
|
import org.videolan.libvlc.Media;
|
||||||
|
import org.videolan.libvlc.MediaPlayer;
|
||||||
|
import org.videolan.libvlc.util.VLCVideoLayout;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class VlcPlayerActivity extends BaseActivity implements MediaPlayer.EventListener
|
||||||
|
, View.OnClickListener {
|
||||||
|
private static final String TAG = "VLC播放器";
|
||||||
|
public final int Type_SubtitleTrack = 0;
|
||||||
|
public final int Type_AudioTrack = 1;
|
||||||
|
public final int Type_Playlist = 2;
|
||||||
|
public final int Type_Scale = 3;
|
||||||
|
public final int Type_Speed = 4;
|
||||||
|
|
||||||
|
private Activity mActivity;
|
||||||
|
private MediaPlayer mediaPlayer;
|
||||||
|
private LibVLC libVLC;
|
||||||
|
private VLCVideoLayout vlcVideoLayout;
|
||||||
|
|
||||||
|
private FrameLayout Controller; //总控
|
||||||
|
private LinearLayout ControllerTop;
|
||||||
|
private LinearLayout ControllerBottom;
|
||||||
|
private LinearLayout ControllerBottomTop;
|
||||||
|
private LinearLayout ControllerBottomBottom;
|
||||||
|
private TextView videoTitle;
|
||||||
|
private TextView currTime;
|
||||||
|
private TextView countTime;
|
||||||
|
private TextView speedBtn;
|
||||||
|
private TextView scaleBtn;
|
||||||
|
private ImageView playPauseBtn;
|
||||||
|
private ImageView stopBtn;
|
||||||
|
private ImageView subTracksBtn;
|
||||||
|
private ImageView audioTracksBtn;
|
||||||
|
private ImageView playListBtn;
|
||||||
|
private ImageView pauseFlag;
|
||||||
|
private SeekBar currPostion;
|
||||||
|
private ProgressBar loading;
|
||||||
|
|
||||||
|
private PopMenu playListMenu = null; //播放列表
|
||||||
|
private PopMenu subTrackMenu = null; //字幕菜单
|
||||||
|
private PopMenu audioTrackMenu = null; //单轨菜单
|
||||||
|
private PopMenu scaleTypeMenu = null; //缩放菜单
|
||||||
|
private PopMenu speedMenu = null; //播放速率菜单
|
||||||
|
|
||||||
|
private float speedRate[] = {0.5f, 1.0f, 1.5f, 2.0f}; //倍速播放列表
|
||||||
|
|
||||||
|
private int updateTimeCount = 0;
|
||||||
|
private int ReportCount = 30;
|
||||||
|
private Video currItem; //当前播放项目
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_vlc_player);
|
||||||
|
|
||||||
|
mActivity = this;
|
||||||
|
initVlc();
|
||||||
|
initController();
|
||||||
|
play();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化播放器
|
||||||
|
*/
|
||||||
|
private void initVlc() {
|
||||||
|
List<String> vlcoptions = new ArrayList<>();
|
||||||
|
vlcoptions.add("-v");
|
||||||
|
vlcVideoLayout = findViewById(R.id.VideoView);
|
||||||
|
libVLC = new LibVLC(this,vlcoptions);
|
||||||
|
Dialog.setCallbacks(libVLC,callbacks);
|
||||||
|
|
||||||
|
mediaPlayer = new MediaPlayer(libVLC);
|
||||||
|
mediaPlayer.attachViews(vlcVideoLayout, null, true, false);
|
||||||
|
mediaPlayer.setEventListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialog.Callbacks callbacks = new Dialog.Callbacks() {
|
||||||
|
@Override
|
||||||
|
public void onDisplay(Dialog.ErrorMessage dialog) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisplay(Dialog.LoginDialog dialog) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisplay(Dialog.QuestionDialog dialog) {
|
||||||
|
if(dialog.getQuestionType() == 1){
|
||||||
|
dialog.postAction(1);
|
||||||
|
}else {
|
||||||
|
Log.d(TAG, "onDisplay_QuestionDialog: " + dialog.getQuestionType() + " : " + dialog.getTitle() + " : " + dialog.getText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisplay(Dialog.ProgressDialog dialog) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCanceled(Dialog dialog) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProgressUpdate(Dialog.ProgressDialog dialog) {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvent(MediaPlayer.Event event) {
|
||||||
|
switch (event.type) {
|
||||||
|
case MediaPlayer.Event.Playing: //媒体打开成功
|
||||||
|
Hide();
|
||||||
|
pauseFlag.setVisibility(View.GONE);
|
||||||
|
Log.d(TAG, "onEvent: Playing");
|
||||||
|
ReportPlayState(JfClient.ReportType.playing);
|
||||||
|
initMenu();
|
||||||
|
break;
|
||||||
|
case MediaPlayer.Event.Paused: //暂停
|
||||||
|
pauseFlag.setVisibility(View.VISIBLE);
|
||||||
|
break;
|
||||||
|
case MediaPlayer.Event.Stopped:
|
||||||
|
Log.d(TAG, "onEvent: Stopped");
|
||||||
|
ReportPlayState(JfClient.ReportType.stop);
|
||||||
|
playNext();
|
||||||
|
break;
|
||||||
|
case MediaPlayer.Event.Opening: //媒体打开
|
||||||
|
Log.d(TAG, "onEvent: Opening");
|
||||||
|
break;
|
||||||
|
case MediaPlayer.Event.Buffering: //媒体加载public float getBuffering() 获取加载视频流的进度0-100
|
||||||
|
int Buffering = (int) event.getBuffering();
|
||||||
|
if (Buffering >= 100) {
|
||||||
|
loading.setVisibility(View.GONE);
|
||||||
|
}else{
|
||||||
|
if(loading.getVisibility() == View.GONE){
|
||||||
|
loading.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MediaPlayer.Event.EndReached://媒体播放结束
|
||||||
|
Log.d(TAG, "onEvent: EndReached");
|
||||||
|
break;
|
||||||
|
case MediaPlayer.Event.EncounteredError://媒体播放错误
|
||||||
|
Log.d(TAG, "onEvent: EncounteredError");
|
||||||
|
ShowToask("播放错误!");
|
||||||
|
stop();
|
||||||
|
break;
|
||||||
|
case MediaPlayer.Event.TimeChanged://视频时间变化
|
||||||
|
updateTimeCount += event.getTimeChanged() - currItem.PositionTicks;
|
||||||
|
currItem.PositionTicks = event.getTimeChanged();
|
||||||
|
if(updateTimeCount > 20000){
|
||||||
|
updateTimeCount = 0;
|
||||||
|
ReportPlayState(JfClient.ReportType.Progress);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MediaPlayer.Event.PositionChanged://视频总时长的百分比
|
||||||
|
break;
|
||||||
|
case MediaPlayer.Event.SeekableChanged:
|
||||||
|
Log.d(TAG, "onEvent: SeekableChanged:" + event.getSeekable());
|
||||||
|
break;
|
||||||
|
case MediaPlayer.Event.PausableChanged:
|
||||||
|
Log.d(TAG, "onEvent: PausableChanged");
|
||||||
|
break;
|
||||||
|
case MediaPlayer.Event.LengthChanged:
|
||||||
|
Log.d(TAG, "onEvent: LengthChanged");
|
||||||
|
break;
|
||||||
|
case MediaPlayer.Event.Vout://当图像输出
|
||||||
|
Log.d(TAG, "onEvent: Vout");
|
||||||
|
break;
|
||||||
|
case MediaPlayer.Event.ESAdded:
|
||||||
|
case MediaPlayer.Event.ESDeleted:
|
||||||
|
case MediaPlayer.Event.ESSelected:
|
||||||
|
Log.d(TAG, "onEvent: ES:" + event.type + ":" + event.getEsChangedType() + ":" + event.getEsChangedID());
|
||||||
|
break;
|
||||||
|
case MediaPlayer.Event.RecordChanged:
|
||||||
|
Log.d(TAG, "onEvent: RecordChanged");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化控制器
|
||||||
|
*/
|
||||||
|
private void initController() {
|
||||||
|
Controller = findViewById(R.id.Controller); // 总布局
|
||||||
|
ControllerTop = findViewById(R.id.ControllerTop);
|
||||||
|
ControllerBottom = findViewById(R.id.ControllerBottom);
|
||||||
|
ControllerBottomTop = findViewById(R.id.ControllerBottomTop);
|
||||||
|
ControllerBottomBottom = findViewById(R.id.ControllerBottomBottom);
|
||||||
|
videoTitle = findViewById(R.id.videoTitle); // 标题
|
||||||
|
currTime = findViewById(R.id.currTime);
|
||||||
|
countTime = findViewById(R.id.countTime);
|
||||||
|
speedBtn = findViewById(R.id.speedBtn);
|
||||||
|
scaleBtn = findViewById(R.id.scaleBtn);
|
||||||
|
playPauseBtn = findViewById(R.id.playPauseBtn);
|
||||||
|
stopBtn = findViewById(R.id.stopBtn);
|
||||||
|
pauseFlag = findViewById(R.id.pauseFlag);
|
||||||
|
currPostion = findViewById(R.id.currPostion);
|
||||||
|
loading = findViewById(R.id.loading);
|
||||||
|
playPauseBtn.setOnClickListener(this);
|
||||||
|
stopBtn.setOnClickListener(this);
|
||||||
|
pauseFlag.setOnClickListener(this);
|
||||||
|
currPostion.setOnKeyListener(new View.OnKeyListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onKey(View view, int i, KeyEvent keyEvent) {
|
||||||
|
boolean rv = false;
|
||||||
|
int action = keyEvent.getAction();
|
||||||
|
if(action == 1) { //按键up
|
||||||
|
int keycode = keyEvent.getKeyCode();
|
||||||
|
if (keycode == KeyEvent.KEYCODE_DPAD_RIGHT) {
|
||||||
|
rv = setTimeOnSeekBar(currItem.PositionTicks + (long) (mediaPlayer.getLength() * 0.05));
|
||||||
|
} else if (keycode == KeyEvent.KEYCODE_DPAD_LEFT) {
|
||||||
|
rv = setTimeOnSeekBar(currItem.PositionTicks - (long) (mediaPlayer.getLength() * 0.05));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化字幕和音轨菜单
|
||||||
|
*/
|
||||||
|
private void initMenu() {
|
||||||
|
MediaPlayer.TrackDescription[] subTrackList = mediaPlayer.getSpuTracks();
|
||||||
|
MediaPlayer.TrackDescription[] audioTrackList = mediaPlayer.getAudioTracks();
|
||||||
|
|
||||||
|
playListBtn = findViewById(R.id.playListBtn);
|
||||||
|
subTracksBtn = findViewById(R.id.subTracksBtn);
|
||||||
|
audioTracksBtn = findViewById(R.id.audioTracksBtn);
|
||||||
|
if (JfClient.playList.size() > 1) {
|
||||||
|
initPlayListMenu();
|
||||||
|
} else {
|
||||||
|
playListBtn.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null != subTrackList && subTrackList.length > 1) {
|
||||||
|
initSubTrackMenu(subTrackList);
|
||||||
|
} else {
|
||||||
|
subTracksBtn.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null != audioTrackList && audioTrackList.length > 1) {
|
||||||
|
initAudioTrackMenu(audioTrackList);
|
||||||
|
} else {
|
||||||
|
audioTracksBtn.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
//初始化缩放键
|
||||||
|
scaleBtn.setOnClickListener(this);
|
||||||
|
scaleBtn.setText(getVlcScaleTypeName(mediaPlayer.getVideoScale().name()));
|
||||||
|
scaleTypeMenu = new PopMenu(this, scaleBtn);
|
||||||
|
MediaPlayer.ScaleType[] scaleTypes = MediaPlayer.ScaleType.values();
|
||||||
|
for (int i = 0; i < scaleTypes.length; i++) {
|
||||||
|
PopMenu.menu menu = scaleTypeMenu.add(Type_Scale, i, i, getVlcScaleTypeName(scaleTypes[i].name()));
|
||||||
|
final int Si = i;
|
||||||
|
menu.v.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
scaleTypeMenu.dismiss();
|
||||||
|
if (mediaPlayer.getVideoScale() != scaleTypes[Si]) {
|
||||||
|
mediaPlayer.setVideoScale(scaleTypes[Si]);
|
||||||
|
scaleBtn.setText(getVlcScaleTypeName(mediaPlayer.getVideoScale().name()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//初始化速率键
|
||||||
|
speedBtn.setOnClickListener(this);
|
||||||
|
speedBtn.setText(String.valueOf(mediaPlayer.getRate()));
|
||||||
|
speedMenu = new PopMenu(this, speedBtn);
|
||||||
|
for (int i = 0; i < speedRate.length; i++) {
|
||||||
|
PopMenu.menu menu = speedMenu.add(Type_Speed, i, i, String.valueOf(speedRate[i]));
|
||||||
|
final int Fi = i;
|
||||||
|
menu.v.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
speedMenu.dismiss();
|
||||||
|
if (mediaPlayer.getRate() != speedRate[Fi]) {
|
||||||
|
mediaPlayer.setRate(speedRate[Fi]);
|
||||||
|
speedBtn.setText(String.valueOf(mediaPlayer.getRate()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initPlayListMenu() {
|
||||||
|
playListMenu = new PopMenu(this, playListBtn); //new PopupMenu(this,playListBtn);
|
||||||
|
for (int i = 0; i < JfClient.playList.size(); i++) {
|
||||||
|
PopMenu.menu m = playListMenu.add(Type_Playlist, i, i, JfClient.playList.get(i).Name);
|
||||||
|
m.v.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
playListMenu.dismiss();
|
||||||
|
if (m.id != JfClient.playIndex) {
|
||||||
|
ReportPlayState(JfClient.ReportType.stop);
|
||||||
|
JfClient.playIndex = m.id;
|
||||||
|
play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
playListBtn.setOnClickListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化字幕菜单
|
||||||
|
*
|
||||||
|
* @param subTrackList
|
||||||
|
*/
|
||||||
|
private void initSubTrackMenu(MediaPlayer.TrackDescription[] subTrackList) {
|
||||||
|
subTrackMenu = new PopMenu(this, subTracksBtn);
|
||||||
|
for (int i = 0; i < subTrackList.length; i++) {
|
||||||
|
PopMenu.menu m = subTrackMenu.add(Type_SubtitleTrack, subTrackList[i].id, i, subTrackList[i].name);
|
||||||
|
m.v.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
subTrackMenu.dismiss();
|
||||||
|
if (m.id != mediaPlayer.getSpuTrack()) {
|
||||||
|
mediaPlayer.setSpuTrack(m.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
subTracksBtn.setOnClickListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化音轨菜单
|
||||||
|
*
|
||||||
|
* @param audioTrackList
|
||||||
|
*/
|
||||||
|
private void initAudioTrackMenu(MediaPlayer.TrackDescription[] audioTrackList) {
|
||||||
|
audioTrackMenu = new PopMenu(this, audioTracksBtn);
|
||||||
|
for (int i = 0; i < audioTrackList.length; i++) {
|
||||||
|
PopMenu.menu m = audioTrackMenu.add(Type_AudioTrack, audioTrackList[i].id, i, audioTrackList[i].name);
|
||||||
|
m.v.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
audioTrackMenu.dismiss();
|
||||||
|
if (m.id != mediaPlayer.getAudioTrack()) {
|
||||||
|
mediaPlayer.setAudioTrack(m.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
audioTracksBtn.setOnClickListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Handler mhandler = new Handler(Looper.getMainLooper());
|
||||||
|
private Runnable mUpdateSeekBar = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
setSeekBar(currItem.PositionTicks);
|
||||||
|
mhandler.postDelayed(mUpdateSeekBar,1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示控制器
|
||||||
|
*/
|
||||||
|
public void Show() {
|
||||||
|
Show(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示控制器
|
||||||
|
*/
|
||||||
|
public void Show(int sec) {
|
||||||
|
if (ControllerTop.getVisibility() == View.GONE) {
|
||||||
|
ControllerTop.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
if (ControllerBottom.getVisibility() == View.GONE) {
|
||||||
|
ControllerBottom.setVisibility(View.VISIBLE);
|
||||||
|
mhandler.postDelayed(mUpdateSeekBar,0);
|
||||||
|
playPauseBtn.requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 隐藏控制器
|
||||||
|
*/
|
||||||
|
public void Hide() {
|
||||||
|
if (ControllerTop.getVisibility() == View.VISIBLE) {
|
||||||
|
ControllerTop.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
if (ControllerBottom.getVisibility() == View.VISIBLE) {
|
||||||
|
ControllerBottom.setVisibility(View.GONE);
|
||||||
|
mhandler.removeCallbacks(mUpdateSeekBar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置进度条时间
|
||||||
|
*/
|
||||||
|
public void setSeekBar(Long p) {
|
||||||
|
if (ControllerBottom.getVisibility() == View.VISIBLE) {
|
||||||
|
if(null != mediaPlayer && mediaPlayer.getLength() > 0){
|
||||||
|
double i = (double) p / 1000;
|
||||||
|
long duration = mediaPlayer.getLength();
|
||||||
|
if (duration > 0) {
|
||||||
|
long pos = 1000L * p / duration;
|
||||||
|
currPostion.setProgress((int) pos);
|
||||||
|
}
|
||||||
|
currTime.setText(TrickToTime(p));
|
||||||
|
countTime.setText(TrickToTime(duration));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始播放
|
||||||
|
*/
|
||||||
|
public void play() {
|
||||||
|
if (JfClient.playList.size() > 0) {
|
||||||
|
if (JfClient.playIndex < JfClient.playList.size()) {
|
||||||
|
currItem = JfClient.playList.get(JfClient.playIndex);
|
||||||
|
videoTitle.setText(currItem.Name);
|
||||||
|
Media media = getMedia();
|
||||||
|
mediaPlayer.setMedia(media);
|
||||||
|
media.release();
|
||||||
|
mediaPlayer.play();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Media getMedia(){
|
||||||
|
Uri uri = Uri.parse(currItem.Url);
|
||||||
|
Media media = new Media(libVLC,uri);
|
||||||
|
media.setHWDecoderEnabled(JfClient.config.isHAACC(),JfClient.config.isFORCE_HAACC());
|
||||||
|
//media.addOption(":codec=mediacodec_ndk,mediacodec_jni,none"); //硬件加速
|
||||||
|
if(!JfClient.config.isPlayStartInBegin()){
|
||||||
|
long startTime = currItem.startPositionTicks / 10000L / 1000L;
|
||||||
|
if( startTime > 60) media.addOption(":start-time=" + startTime); //设置开始位置
|
||||||
|
}
|
||||||
|
return media;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放下一集
|
||||||
|
*/
|
||||||
|
public void playNext() {
|
||||||
|
if (JfClient.playIndex < (JfClient.playList.size() - 1)) {
|
||||||
|
ReportPlayState(JfClient.ReportType.stop);
|
||||||
|
JfClient.playIndex += 1;
|
||||||
|
play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止播放并结束Activity
|
||||||
|
*/
|
||||||
|
public void stop() {
|
||||||
|
ReportPlayState(JfClient.ReportType.stop);
|
||||||
|
mediaPlayer.stop();
|
||||||
|
mediaPlayer.release();
|
||||||
|
libVLC.release();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放或暂停
|
||||||
|
*/
|
||||||
|
public void playOrpause() {
|
||||||
|
if (mediaPlayer.isPlaying()) {
|
||||||
|
mediaPlayer.pause();
|
||||||
|
pauseFlag.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
mediaPlayer.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置播放器进度
|
||||||
|
*/
|
||||||
|
public boolean setTimeOnSeekBar(Long p) {
|
||||||
|
if (p < mediaPlayer.getLength() && p > 0) {
|
||||||
|
mediaPlayer.setTime(p,true);
|
||||||
|
setSeekBar(p);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进度转时间
|
||||||
|
*
|
||||||
|
* @param trick
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String TrickToTime(long trick) {
|
||||||
|
String time = "";
|
||||||
|
long totalSeconds = trick / 1000;
|
||||||
|
long seconds = totalSeconds % 60;
|
||||||
|
long minutes = (totalSeconds / 60) % 60;
|
||||||
|
long hours = totalSeconds / 3600;
|
||||||
|
|
||||||
|
time = String.format("%02d:%02d:%02d", hours, minutes, seconds);
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||||
|
if (ControllerBottom.getVisibility() == View.GONE) {
|
||||||
|
switch (keyCode) {
|
||||||
|
case KeyEvent.KEYCODE_DPAD_UP:
|
||||||
|
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||||
|
Show();
|
||||||
|
return true;
|
||||||
|
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||||
|
mediaPlayer.setTime(mediaPlayer.getTime() + 30000);
|
||||||
|
return true;
|
||||||
|
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||||
|
mediaPlayer.setTime(mediaPlayer.getTime() - 10000);
|
||||||
|
return true;
|
||||||
|
case KeyEvent.KEYCODE_ENTER:
|
||||||
|
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
|
||||||
|
playOrpause();
|
||||||
|
return true;
|
||||||
|
case KeyEvent.KEYCODE_ESCAPE:
|
||||||
|
case KeyEvent.KEYCODE_BACK:
|
||||||
|
stop();
|
||||||
|
return true;
|
||||||
|
//退出
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (keyCode) {
|
||||||
|
case KeyEvent.KEYCODE_ESCAPE:
|
||||||
|
case KeyEvent.KEYCODE_BACK:
|
||||||
|
Hide();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.onKeyDown(keyCode, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
int id = view.getId();
|
||||||
|
if (id == R.id.playPauseBtn) {
|
||||||
|
playOrpause();
|
||||||
|
} else if (id == R.id.stopBtn) {
|
||||||
|
stop();
|
||||||
|
} else if (id == R.id.subTracksBtn) {
|
||||||
|
subTrackMenu.show(mediaPlayer.getSpuTrack());
|
||||||
|
} else if (id == R.id.audioTracksBtn) {
|
||||||
|
audioTrackMenu.show(mediaPlayer.getAudioTrack());
|
||||||
|
} else if (id == R.id.playListBtn) {
|
||||||
|
playListMenu.show(JfClient.playIndex);
|
||||||
|
} else if (id == R.id.scaleBtn) {
|
||||||
|
scaleTypeMenu.show(getVlcScaleTypeName(mediaPlayer.getVideoScale().name()));
|
||||||
|
} else if (id == R.id.speedBtn) {
|
||||||
|
speedMenu.show(String.valueOf(mediaPlayer.getRate()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReportPlayState(JfClient.ReportType type) {
|
||||||
|
JfClient.ReportPlayBackState(type,currItem.Id,currItem.PositionTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish() {
|
||||||
|
mhandler.removeCallbacksAndMessages(null);
|
||||||
|
super.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据缩放类型取名称
|
||||||
|
*
|
||||||
|
* @param scaleName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getVlcScaleTypeName(String scaleName) {
|
||||||
|
switch (scaleName) {
|
||||||
|
case "SURFACE_BEST_FIT":
|
||||||
|
return "自动";
|
||||||
|
case "SURFACE_FIT_SCREEN":
|
||||||
|
return "适应屏幕";
|
||||||
|
case "SURFACE_FILL":
|
||||||
|
return "满屏";
|
||||||
|
case "SURFACE_16_9":
|
||||||
|
return "16:9";
|
||||||
|
case "SURFACE_4_3":
|
||||||
|
return "4:3";
|
||||||
|
case "SURFACE_ORIGINAL":
|
||||||
|
return "原始";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
30
app/src/main/res/drawable-v24/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="85.84757"
|
||||||
|
android:endY="92.4963"
|
||||||
|
android:startX="42.9492"
|
||||||
|
android:startY="49.59793"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#44000000"
|
||||||
|
android:offset="0.0" />
|
||||||
|
<item
|
||||||
|
android:color="#00000000"
|
||||||
|
android:offset="1.0" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#00000000" />
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/anim_loading.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:drawable="@drawable/icon_loading"
|
||||||
|
android:pivotX="50%"
|
||||||
|
android:pivotY="50%" />
|
BIN
app/src/main/res/drawable/avatar.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
app/src/main/res/drawable/banner.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
app/src/main/res/drawable/bg_blueradiance.png
Normal file
After Width: | Height: | Size: 2.7 MiB |
BIN
app/src/main/res/drawable/bg_purplehaze.png
Normal file
After Width: | Height: | Size: 878 KiB |
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="48dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M9,16h2L11,8L9,8v8zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM13,16h2L15,8h-2v8z"/>
|
||||||
|
</vector>
|
170
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:fillColor="#3DDC84"
|
||||||
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M9,0L9,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,0L19,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,0L29,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,0L39,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,0L49,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,0L59,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,0L69,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,0L79,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M89,0L89,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M99,0L99,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,9L108,9"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,19L108,19"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,29L108,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,39L108,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,49L108,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,59L108,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,69L108,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,79L108,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,89L108,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,99L108,99"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,29L89,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,39L89,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,49L89,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,59L89,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,69L89,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,79L89,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,19L29,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,19L39,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,19L49,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,19L59,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,19L69,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,19L79,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
</vector>
|
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="48dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M17.51,3.87L15.73,2.1 5.84,12l9.9,9.9 1.77,-1.77L9.38,12l8.13,-8.13z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/ic_outline_audiotrack_48.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="48dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,3v10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55 -2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4L14,7h4L18,3h-6zM10,19c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/ic_outline_pause_48.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="48dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="48dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M9,16h2L11,8L9,8v8zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM13,16h2L15,8h-2v8z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/ic_outline_play_arrow_48.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="48dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M10,8.64L15.27,12 10,15.36V8.64M8,5v14l11,-7L8,5z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="96dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="96dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M10,16.5l6,-4.5 -6,-4.5zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="48dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M4,10h12v2L4,12zM4,6h12v2L4,8zM4,14h8v2L4,16zM14,14v6l5,-3z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/ic_outline_search_48.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="48dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/ic_outline_skip_next_48.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="48dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M6,18l8.5,-6L6,6v12zM8,9.86L11.03,12 8,14.14L8,9.86zM16,6h2v12h-2z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="48dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M6,6h2v12L6,18zM9.5,12l8.5,6L18,6l-8.5,6zM16,14.14L12.97,12 16,9.86v4.28z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/ic_outline_stop_48.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="48dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M16,8v8H8V8h8m2,-2H6v12h12V6z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/ic_outline_subtitles_48.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="48dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M20,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,6h16v12zM6,10h2v2L6,12zM6,14h8v2L6,16zM16,14h2v2h-2zM10,10h8v2h-8z"/>
|
||||||
|
</vector>
|
BIN
app/src/main/res/drawable/icon_img_placeholder.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/main/res/drawable/icon_loading.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
15
app/src/main/res/drawable/img_loading_placeholder.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#36ffffff" />
|
||||||
|
<corners android:radius="1dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<bitmap
|
||||||
|
android:antialias="true"
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@drawable/icon_img_placeholder" />
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
15
app/src/main/res/drawable/played_percentage.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@android:id/background">
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#00ffffff" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item android:id="@android:id/progress">
|
||||||
|
<clip>
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#1890ff" />
|
||||||
|
</shape>
|
||||||
|
</clip>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
16
app/src/main/res/drawable/popmenu_focus.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_focused="true">
|
||||||
|
<shape>
|
||||||
|
<corners android:radius="0dp" />
|
||||||
|
<stroke android:width="1dp" android:color="@color/color_0CADE2" />
|
||||||
|
<solid android:color="@color/color_3D3D3D" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<!-- <item android:state_focused="false">-->
|
||||||
|
<!-- <shape>-->
|
||||||
|
<!-- <corners android:radius="0dp" />-->
|
||||||
|
<!-- <solid android:color="@color/color_6C3D3D3D"/>-->
|
||||||
|
<!-- </shape>-->
|
||||||
|
<!-- </item>-->
|
||||||
|
</selector>
|
16
app/src/main/res/drawable/shape_user_focus.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_focused="true">
|
||||||
|
<shape>
|
||||||
|
<corners android:radius="1dp" />
|
||||||
|
<stroke android:width="1dp" android:color="@color/color_BD0CADE2" />
|
||||||
|
<solid android:color="@color/color_6CFFFFFF" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item android:state_focused="false">
|
||||||
|
<shape>
|
||||||
|
<corners android:radius="1dp" />
|
||||||
|
<solid android:color="#00000000"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</selector>
|
16
app/src/main/res/drawable/shape_user_focus_vholder.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_focused="true">
|
||||||
|
<shape>
|
||||||
|
<corners android:radius="1dp" />
|
||||||
|
<stroke android:width="2dp" android:color="@color/color_0CADE2" />
|
||||||
|
<solid android:color="@color/color_6CFFFFFF" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item android:state_focused="false">
|
||||||
|
<shape>
|
||||||
|
<corners android:radius="1dp" />
|
||||||
|
<solid android:color="@color/color_6C3D3D3D"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</selector>
|
BIN
app/src/main/res/drawable/touchicon144.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
84
app/src/main/res/layout/activebar_custom.xml
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/title_height"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/actionBar_back"
|
||||||
|
android:layout_width="20dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/shape_user_focus"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:padding="6dp"
|
||||||
|
android:src="@drawable/ic_outline_arrow_back_ios_48" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/actionBar_banner"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="fitStart"
|
||||||
|
app:srcCompat="@drawable/banner" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/actionBar_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginLeft="@dimen/button_margin_left"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text=""
|
||||||
|
android:textSize="18dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/actionBar_titleTip"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginLeft="@dimen/button_margin_left"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text=""
|
||||||
|
android:textSize="18dp" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/actionBar_searchBtn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:padding="6dp"
|
||||||
|
android:background="@drawable/shape_user_focus"
|
||||||
|
android:src="@drawable/ic_outline_search_48" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/actionBar_sortBtn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginLeft="@dimen/button_margin_left"
|
||||||
|
android:background="@drawable/shape_user_focus"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:gravity="center"
|
||||||
|
android:minWidth="100dp"
|
||||||
|
android:text="排序"
|
||||||
|
android:textSize="18dp"
|
||||||
|
android:paddingLeft="8dp"
|
||||||
|
android:paddingRight="8dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/actionBar_setBtn"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginLeft="@dimen/button_margin_left"
|
||||||
|
android:padding="6dp"
|
||||||
|
android:src="@drawable/avatar"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:background="@drawable/shape_user_focus" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
25
app/src/main/res/layout/activity_collection.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:context=".CollectionActivity">
|
||||||
|
|
||||||
|
<org.sifacai.vlcjellyfin.JRecyclerView
|
||||||
|
android:id="@+id/mGridView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingLeft="26dp"
|
||||||
|
android:paddingTop="@dimen/padding_border_10"
|
||||||
|
android:paddingRight="@dimen/padding_border_10"
|
||||||
|
android:paddingBottom="@dimen/padding_border_10"
|
||||||
|
app:tv_horizontalSpacingWithMargins="@dimen/vh_space_width"
|
||||||
|
app:tv_selectedItemIsCentered="true"
|
||||||
|
app:tv_verticalSpacingWithMargins="@dimen/vh_space_width" />
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
125
app/src/main/res/layout/activity_detail.xml
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="@dimen/padding_border"
|
||||||
|
tools:context=".DetailActivity">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fadingEdge="vertical"
|
||||||
|
android:scrollbars="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/tvCover"
|
||||||
|
android:layout_width="420dp"
|
||||||
|
android:layout_height="620dp"
|
||||||
|
android:scaleType="fitXY"
|
||||||
|
app:srcCompat="@drawable/icon_img_placeholder" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="@dimen/padding_border"
|
||||||
|
android:paddingTop="6dp"
|
||||||
|
android:paddingRight="@dimen/padding_border"
|
||||||
|
android:paddingBottom="6dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvTitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="38dp"
|
||||||
|
android:text="标题:"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="30dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDetails"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="200dp"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:textSize="20dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvListTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="30dp"
|
||||||
|
android:text="播放列表:" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/tvPlay"
|
||||||
|
android:layout_width="160dp"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:layout_marginLeft="50dp"
|
||||||
|
android:background="@drawable/shape_user_focus_vholder"
|
||||||
|
android:focusable="true"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_outline_play_circle_outline_128"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<org.sifacai.vlcjellyfin.JRecyclerView
|
||||||
|
android:id="@+id/mGridView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:focusableInTouchMode="false"
|
||||||
|
android:padding="@dimen/padding_border"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:tv_horizontalSpacingWithMargins="@dimen/vh_space_width"
|
||||||
|
app:tv_selectedItemIsCentered="true"
|
||||||
|
app:tv_verticalSpacingWithMargins="@dimen/vh_space_width" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/tvPersonLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:text="演员表:"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"/>
|
||||||
|
|
||||||
|
<org.sifacai.vlcjellyfin.JRecyclerView
|
||||||
|
android:id="@+id/mPersonGridView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="312dp"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:focusableInTouchMode="false"
|
||||||
|
android:padding="@dimen/padding_border"
|
||||||
|
app:tv_horizontalSpacingWithMargins="@dimen/vh_space_width"
|
||||||
|
app:tv_selectedItemIsCentered="true"
|
||||||
|
app:tv_verticalSpacingWithMargins="@dimen/vh_space_width" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
49
app/src/main/res/layout/activity_search.xml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingLeft="60dp"
|
||||||
|
android:paddingRight="60dp"
|
||||||
|
android:orientation="horizontal" >
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/searchTermEdit"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:maxLength="20"
|
||||||
|
android:inputType="text"
|
||||||
|
android:lines="1"
|
||||||
|
android:focusable="true"
|
||||||
|
android:tooltipText="关键字" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/searchBtn"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginLeft="@dimen/button_margin_left"
|
||||||
|
android:focusable="true"
|
||||||
|
android:background="@drawable/shape_user_focus"
|
||||||
|
android:src="@drawable/ic_outline_search_48" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<org.sifacai.vlcjellyfin.JRecyclerView
|
||||||
|
android:id="@+id/mGridView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:padding="@dimen/padding_border"
|
||||||
|
app:tv_horizontalSpacingWithMargins="@dimen/vh_space_width"
|
||||||
|
app:tv_selectedItemIsCentered="true"
|
||||||
|
app:tv_verticalSpacingWithMargins="@dimen/vh_space_width" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
186
app/src/main/res/layout/activity_vlc_player.xml
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".VlcPlayerActivity">
|
||||||
|
|
||||||
|
<org.videolan.libvlc.util.VLCVideoLayout
|
||||||
|
android:id="@+id/VideoView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/Controller"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ControllerTop"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:background="@color/color_99000000"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingLeft="30dp"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingRight="30dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/videoTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="标题"
|
||||||
|
android:textColor="#99FFFFFF"
|
||||||
|
android:textSize="36dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ControllerBottom"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:background="@color/color_99000000"
|
||||||
|
android:gravity="bottom"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ControllerBottomTop"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/currTime"
|
||||||
|
android:layout_width="160dp"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="00:00:00"
|
||||||
|
android:textSize="26dp" />
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/currPostion"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/shape_user_focus"
|
||||||
|
android:focusable="true"
|
||||||
|
android:max="1000"
|
||||||
|
android:maxHeight="26dp"
|
||||||
|
android:minHeight="26dp"
|
||||||
|
android:progress="0" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/countTime"
|
||||||
|
android:layout_width="160dp"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="00:00:00"
|
||||||
|
android:textSize="26dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ControllerBottomBottom"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingBottom="20dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/playPauseBtn"
|
||||||
|
android:layout_width="@dimen/button_width"
|
||||||
|
android:layout_height="@dimen/button_height"
|
||||||
|
android:layout_marginLeft="@dimen/button_margin_left"
|
||||||
|
android:background="@drawable/shape_user_focus"
|
||||||
|
android:focusable="true"
|
||||||
|
android:src="@drawable/ic_outline_play_arrow_48" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/stopBtn"
|
||||||
|
android:layout_width="@dimen/button_width"
|
||||||
|
android:layout_height="@dimen/button_height"
|
||||||
|
android:layout_marginLeft="@dimen/button_margin_left"
|
||||||
|
android:background="@drawable/shape_user_focus"
|
||||||
|
android:focusable="true"
|
||||||
|
android:src="@drawable/ic_outline_stop_48" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/speedBtn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="@dimen/button_height"
|
||||||
|
android:layout_marginLeft="@dimen/button_margin_left"
|
||||||
|
android:background="@drawable/shape_user_focus"
|
||||||
|
android:focusable="true"
|
||||||
|
android:gravity="center"
|
||||||
|
android:minWidth="@dimen/button_width"
|
||||||
|
android:paddingLeft="10dp"
|
||||||
|
android:paddingRight="10dp"
|
||||||
|
android:textSize="@dimen/title_size" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/scaleBtn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="@dimen/button_height"
|
||||||
|
android:layout_marginLeft="@dimen/button_margin_left"
|
||||||
|
android:background="@drawable/shape_user_focus"
|
||||||
|
android:focusable="true"
|
||||||
|
android:gravity="center"
|
||||||
|
android:minWidth="@dimen/button_width"
|
||||||
|
android:paddingLeft="10dp"
|
||||||
|
android:paddingRight="10dp"
|
||||||
|
android:textSize="@dimen/title_size" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/subTracksBtn"
|
||||||
|
android:layout_width="@dimen/button_width"
|
||||||
|
android:layout_height="@dimen/button_height"
|
||||||
|
android:layout_marginLeft="@dimen/button_margin_left"
|
||||||
|
android:background="@drawable/shape_user_focus"
|
||||||
|
android:focusable="true"
|
||||||
|
android:src="@drawable/ic_outline_subtitles_48" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/audioTracksBtn"
|
||||||
|
android:layout_width="@dimen/button_width"
|
||||||
|
android:layout_height="@dimen/button_height"
|
||||||
|
android:layout_marginLeft="@dimen/button_margin_left"
|
||||||
|
android:background="@drawable/shape_user_focus"
|
||||||
|
android:focusable="true"
|
||||||
|
android:src="@drawable/ic_outline_audiotrack_48" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/playListBtn"
|
||||||
|
android:layout_width="@dimen/button_width"
|
||||||
|
android:layout_height="@dimen/button_height"
|
||||||
|
android:layout_marginLeft="@dimen/button_margin_left"
|
||||||
|
android:background="@drawable/shape_user_focus"
|
||||||
|
android:focusable="true"
|
||||||
|
android:src="@drawable/ic_outline_playlist_play_48" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/pauseFlag"
|
||||||
|
android:layout_width="160dp"
|
||||||
|
android:layout_height="160dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="@drawable/ic_baseline_pause_circle_outline_48"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/loading"
|
||||||
|
android:layout_width="160dp"
|
||||||
|
android:layout_height="160dp"
|
||||||
|
android:layout_gravity="center" />
|
||||||
|
</FrameLayout>
|
73
app/src/main/res/layout/dialog_login.xml
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="@dimen/padding_border"
|
||||||
|
android:paddingTop="0dp">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/dialog_login_url"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/dialog_text_height"
|
||||||
|
android:inputType="text"
|
||||||
|
android:lines="1"
|
||||||
|
android:nextFocusDown="@id/dialog_login_un"
|
||||||
|
android:hint="服务器地址"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/dialog_login_un"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/dialog_text_height"
|
||||||
|
android:inputType="text"
|
||||||
|
android:lines="1"
|
||||||
|
android:nextFocusDown="@id/dialog_login_pw"
|
||||||
|
android:hint="用户名"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/dialog_login_pw"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/dialog_text_height"
|
||||||
|
android:inputType="text"
|
||||||
|
android:lines="1"
|
||||||
|
android:nextFocusDown="@id/dialog_login_save"
|
||||||
|
android:hint="密码"/>
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/dialog_login_save"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/dialog_text_height"
|
||||||
|
android:focusable="true"
|
||||||
|
android:background="@drawable/shape_user_focus"
|
||||||
|
android:nextFocusDown="@id/dialog_login_submit"
|
||||||
|
android:text="保存用户" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/dialog_text_height"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/dialog_login_submit"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:focusable="true"
|
||||||
|
android:background="@drawable/shape_user_focus"
|
||||||
|
android:text="确定"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/dialog_login_cancel"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:focusable="true"
|
||||||
|
android:background="@drawable/shape_user_focus"
|
||||||
|
android:text="取消"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
14
app/src/main/res/layout/home_horizon_tvrecycler.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<org.sifacai.vlcjellyfin.JRecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:padding="20dp"
|
||||||
|
app:tv_layoutManager="V7LinearLayoutManager"
|
||||||
|
app:tv_horizontalSpacingWithMargins="10dp"
|
||||||
|
app:tv_selectedItemIsCentered="true">
|
||||||
|
|
||||||
|
</org.sifacai.vlcjellyfin.JRecyclerView>
|
55
app/src/main/res/layout/item_h.xml
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/shape_user_focus_vholder"
|
||||||
|
android:focusable="true"
|
||||||
|
android:padding="@dimen/vh_padding_border">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="@dimen/horizon_cover_width"
|
||||||
|
android:layout_height="@dimen/horizon_cover_height">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ivThumb"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="0dp"
|
||||||
|
android:scaleType="fitXY" />
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:background="@color/color_99000000"
|
||||||
|
android:padding="1dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/title_height"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:padding="0dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:singleLine="true" />
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/tvPlayedPercentage"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="2dp"
|
||||||
|
android:max="100"
|
||||||
|
android:progress="0"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:thumb="@null"
|
||||||
|
android:progressDrawable="@drawable/played_percentage"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
55
app/src/main/res/layout/item_v.xml
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/shape_user_focus_vholder"
|
||||||
|
android:focusable="true"
|
||||||
|
android:padding="@dimen/vh_padding_border">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="@dimen/vertical_cover_width"
|
||||||
|
android:layout_height="@dimen/vertical_cover_height">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ivThumb"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="0dp"
|
||||||
|
android:scaleType="fitXY" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:background="@color/color_99000000"
|
||||||
|
android:padding="1dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/title_height"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:padding="0dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:singleLine="true" />
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/tvPlayedPercentage"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="2dp"
|
||||||
|
android:max="100"
|
||||||
|
android:progress="0"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:thumb="@null"
|
||||||
|
android:progressDrawable="@drawable/played_percentage"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
29
app/src/main/res/layout/jellyfin_home.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:context=".HomeActivity">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
android:fadingEdge="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/tvItems"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="@dimen/padding_border_10"
|
||||||
|
android:paddingRight="@dimen/padding_border_10"/>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
28
app/src/main/res/layout/loading_alert.xml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@color/color_6CFFFFFF"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progressBar1"
|
||||||
|
android:layout_width="80dp"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_centerVertical="true" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/progressText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textSize="@dimen/title_size"
|
||||||
|
android:text=""
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
16
app/src/main/res/layout/popmenu.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="0dp"
|
||||||
|
android:layout_margin="0dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/popitemContiner"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="0dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="0dp" />
|
||||||
|
|
||||||
|
</ScrollView>
|
12
app/src/main/res/layout/popmenu_item.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:minWidth="100dp"
|
||||||
|
android:maxWidth="300dp"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:layout_margin="0dp"
|
||||||
|
android:background="@drawable/popmenu_focus"
|
||||||
|
android:focusable="true" >
|
||||||
|
|
||||||
|
</TextView>
|
36
app/src/main/res/menu/activebar_menu.xml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:orderInCategory="0"
|
||||||
|
android:title="设置选项:" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/actionBar_option_PlayStartInBegin"
|
||||||
|
android:orderInCategory="6"
|
||||||
|
android:checkable="true"
|
||||||
|
android:title="从头开始播放" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/actionBar_option_HAACC"
|
||||||
|
android:orderInCategory="7"
|
||||||
|
android:checkable="true"
|
||||||
|
android:title="硬解解码" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/actionBar_option_FORCE_HAACC"
|
||||||
|
android:orderInCategory="8"
|
||||||
|
android:checkable="true"
|
||||||
|
android:title="强制硬解解码" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/actionBar_option_ExtensionPlayer"
|
||||||
|
android:orderInCategory="9"
|
||||||
|
android:checkable="true"
|
||||||
|
android:title="调用外部播放器" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/activeBar_option_logout"
|
||||||
|
android:orderInCategory="99"
|
||||||
|
android:title="注销" />
|
||||||
|
|
||||||
|
</menu>
|
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?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>
|
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?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>
|
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 982 B |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Normal file
After Width: | Height: | Size: 7.6 KiB |
37
app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="purple_200">#FFBB86FC</color>
|
||||||
|
<color name="purple_500">#FF6200EE</color>
|
||||||
|
<color name="purple_700">#FF3700B3</color>
|
||||||
|
<color name="teal_200">#FF03DAC5</color>
|
||||||
|
<color name="teal_700">#FF018786</color>
|
||||||
|
<color name="black">#FF000000</color>
|
||||||
|
<color name="white">#FFFFFFFF</color>
|
||||||
|
|
||||||
|
<color name="color_6200EE">#6200EE</color>
|
||||||
|
<color name="color_3700B3">#3700B3</color>
|
||||||
|
<color name="color_03DAC5">#03DAC5</color>
|
||||||
|
<color name="color_1890FF">#1890FF</color>
|
||||||
|
<color name="color_FFB800">#FF6600</color>
|
||||||
|
<color name="color_00FF0A">#00FF0A</color>
|
||||||
|
<color name="color_CBF46A">#CBF46A</color>
|
||||||
|
<color name="color_FF90CA09">#90CA09</color>
|
||||||
|
<color name="color_BD0CADE2">#BD0CADE2</color>
|
||||||
|
<color name="color_FF0057">#FF0057</color>
|
||||||
|
<color name="color_FF5F00">#FF5F00</color>
|
||||||
|
<color name="color_3D3D3D">#3D3D3D</color>
|
||||||
|
<color name="color_32364E">#32364E</color>
|
||||||
|
<color name="color_6632364E">#6632364E</color>
|
||||||
|
<color name="color_CCFFFFFF">#BBFFFFFF</color>
|
||||||
|
<color name="color_0CADE2">#0CADE2</color>
|
||||||
|
<color name="color_6CFFFFFF">#6CFFFFFF</color>
|
||||||
|
<color name="color_66000000">#66000000</color>
|
||||||
|
<color name="color_99000000">#99000000</color>
|
||||||
|
<color name="color_CC000000">#CC000000</color>
|
||||||
|
<color name="color_02F8E1">#02F8E1</color>
|
||||||
|
<color name="color_353744">#353744</color>
|
||||||
|
<color name="color_CC353744">#CC353744</color>
|
||||||
|
<color name="color_6C3D3D3D">#6c3d3d3d</color>
|
||||||
|
<color name="color_BBFFFFFF">#bbffffff</color>
|
||||||
|
<color name="color_FFFFFF">#ffffffff</color>
|
||||||
|
</resources>
|
36
app/src/main/res/values/dimens.xml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<dimen name="title_height">32dp</dimen>
|
||||||
|
<dimen name="title_size">22dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="horizon_cover_width">400dp</dimen>
|
||||||
|
<dimen name="horizon_cover_height">228dp</dimen>
|
||||||
|
<dimen name="vertical_cover_width">180dp</dimen>
|
||||||
|
<dimen name="vertical_cover_height">270dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="focus_border">1dp</dimen>
|
||||||
|
<dimen name="radius">5dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="mGrid_padding_left">20dp</dimen>
|
||||||
|
<dimen name="mGrid_padding_top">20dp</dimen>
|
||||||
|
<dimen name="mGrid_padding_right">20dp</dimen>
|
||||||
|
<dimen name="mGrid_padding_bottom">20dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="padding_border_10">10dp</dimen>
|
||||||
|
<dimen name="padding_border">20dp</dimen>
|
||||||
|
<dimen name="vh_space_width">10dp</dimen>
|
||||||
|
<dimen name="vh_padding_border">2dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="drawable_padding_s">6dp</dimen>
|
||||||
|
<dimen name="drawable_padding_b">12dp</dimen>
|
||||||
|
|
||||||
|
<!-- 登录框 -->
|
||||||
|
<dimen name="dialog_text_height">48dp</dimen>
|
||||||
|
|
||||||
|
<!-- 播放控制器 -->
|
||||||
|
<dimen name="time_text_size">26dp</dimen>
|
||||||
|
<dimen name="button_height">60dp</dimen>
|
||||||
|
<dimen name="button_width">60dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="button_margin_left">26dp</dimen>
|
||||||
|
</resources>
|
3
app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">VlcJellyfin</string>
|
||||||
|
</resources>
|
26
app/src/main/res/values/theme.xml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<style name="jellyfin" parent="Theme.AppCompat">
|
||||||
|
<!-- hide title bar -->
|
||||||
|
<!-- <item name="windowNoTitle">true</item>-->
|
||||||
|
<!-- <item name="android:background">@drawable/bg_purplehaze</item>-->
|
||||||
|
<item name="android:windowBackground">@drawable/bg_blueradiance</item>
|
||||||
|
<item name="actionBarStyle">@style/JyfActionBar</item>
|
||||||
|
|
||||||
|
<item name="android:textSize">22dp</item>
|
||||||
|
<!-- 默认 Button,TextView的文字颜色 -->
|
||||||
|
<item name="android:textColor">@android:color/white</item>
|
||||||
|
<!-- 默认 EditView 输入框字体的颜色 -->
|
||||||
|
<!-- <item name="android:editTextColor">#E6E6FA</item>-->
|
||||||
|
<!-- RadioButton checkbox等控件的文字 -->
|
||||||
|
<!-- <item name="android:textColorPrimaryDisableOnly">#1C71A9</item>-->
|
||||||
|
<!-- 应用的主要文字颜色,actionBar的标题文字默认使用该颜色 -->
|
||||||
|
<item name="android:textColorPrimary">@android:color/white</item>
|
||||||
|
<!-- 辅助的文字颜色,一般比textColorPrimary的颜色弱一点,用于一些弱化的表示 -->
|
||||||
|
<item name="android:textColorSecondary">#ffffff</item>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<style name="JyfActionBar" parent="@android:style/Widget.Holo.Light.ActionBar">
|
||||||
|
<!-- <item name="android:background">#00000000</item>-->
|
||||||
|
</style>
|
||||||
|
</resources>
|
13
app/src/main/res/xml/backup_rules.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
Sample backup rules file; uncomment and customize as necessary.
|
||||||
|
See https://developer.android.com/guide/topics/data/autobackup
|
||||||
|
for details.
|
||||||
|
Note: This file is ignored for devices older that API 31
|
||||||
|
See https://developer.android.com/about/versions/12/backup-restore
|
||||||
|
-->
|
||||||
|
<full-backup-content>
|
||||||
|
<!--
|
||||||
|
<include domain="sharedpref" path="."/>
|
||||||
|
<exclude domain="sharedpref" path="device.xml"/>
|
||||||
|
-->
|
||||||
|
</full-backup-content>
|
19
app/src/main/res/xml/data_extraction_rules.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
Sample data extraction rules file; uncomment and customize as necessary.
|
||||||
|
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||||
|
for details.
|
||||||
|
-->
|
||||||
|
<data-extraction-rules>
|
||||||
|
<cloud-backup>
|
||||||
|
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||||
|
<include .../>
|
||||||
|
<exclude .../>
|
||||||
|
-->
|
||||||
|
</cloud-backup>
|
||||||
|
<!--
|
||||||
|
<device-transfer>
|
||||||
|
<include .../>
|
||||||
|
<exclude .../>
|
||||||
|
</device-transfer>
|
||||||
|
-->
|
||||||
|
</data-extraction-rules>
|
@ -0,0 +1,17 @@
|
|||||||
|
package org.sifacai.vlcjellyfin;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
public class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
public void addition_isCorrect() {
|
||||||
|
assertEquals(4, 2 + 2);
|
||||||
|
}
|
||||||
|
}
|
9
build.gradle
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
plugins {
|
||||||
|
id 'com.android.application' version '7.2.2' apply false
|
||||||
|
id 'com.android.library' version '7.2.2' apply false
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|