Browse Source

视频类基础框架,待开发中

lyn 6 years ago
commit
4115fc6dff
58 changed files with 2849 additions and 0 deletions
  1. 13 0
      .gitignore
  2. 4 0
      .idea/encodings.xml
  3. 18 0
      .idea/gradle.xml
  4. 14 0
      .idea/misc.xml
  5. 12 0
      .idea/runConfigurations.xml
  6. 6 0
      .idea/vcs.xml
  7. 1 0
      app/.gitignore
  8. 75 0
      app/build.gradle
  9. 21 0
      app/proguard-rules.pro
  10. 27 0
      app/src/androidTest/java/com/haochuan/hciptvbasic/ExampleInstrumentedTest.java
  11. 44 0
      app/src/androidTest/java/com/haochuan/hciptvbasic/MainActivityTest.java
  12. 27 0
      app/src/main/AndroidManifest.xml
  13. 234 0
      app/src/main/java/com/haochuan/hciptvbasic/BaseWebActivity.java
  14. 109 0
      app/src/main/java/com/haochuan/hciptvbasic/MainActivity.java
  15. 9 0
      app/src/main/java/com/haochuan/hciptvbasic/Util/Juge.java
  16. 27 0
      app/src/main/java/com/haochuan/hciptvbasic/Util/Logger.java
  17. 337 0
      app/src/main/java/com/haochuan/hciptvbasic/Util/MacUtil.java
  18. 36 0
      app/src/main/java/com/haochuan/hciptvbasic/Util/ScreenSnap.java
  19. 70 0
      app/src/main/java/com/haochuan/hciptvbasic/test/TestActivity.java
  20. 84 0
      app/src/main/java/com/haochuan/hciptvbasic/video/BaseMediaPlayer.java
  21. 123 0
      app/src/main/java/com/haochuan/hciptvbasic/video/EmptyControlVideoView.java
  22. 111 0
      app/src/main/java/com/haochuan/hciptvbasic/video/HCGsyVideoPlayer.java
  23. 171 0
      app/src/main/java/com/haochuan/hciptvbasic/video/HCPlayer.java
  24. 19 0
      app/src/main/java/com/haochuan/hciptvbasic/video/IVideoPlayer.java
  25. 26 0
      app/src/main/java/com/haochuan/hciptvbasic/webview/HCWebChromeClient.java
  26. 141 0
      app/src/main/java/com/haochuan/hciptvbasic/webview/PayToJS.java
  27. 283 0
      app/src/main/java/com/haochuan/hciptvbasic/webview/PlayerToJS.java
  28. 138 0
      app/src/main/java/com/haochuan/hciptvbasic/webview/ToolToJS.java
  29. 34 0
      app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  30. 170 0
      app/src/main/res/drawable/ic_launcher_background.xml
  31. 18 0
      app/src/main/res/layout/activity_main.xml
  32. 60 0
      app/src/main/res/layout/activity_test.xml
  33. 9 0
      app/src/main/res/layout/player_gsy_hc.xml
  34. 9 0
      app/src/main/res/layout/player_hc.xml
  35. 15 0
      app/src/main/res/layout/view_video_control_empty.xml
  36. 5 0
      app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  37. 5 0
      app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  38. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher.png
  39. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_round.png
  40. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher.png
  41. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher_round.png
  42. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher.png
  43. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
  44. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  45. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
  46. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  47. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
  48. 6 0
      app/src/main/res/values/colors.xml
  49. 3 0
      app/src/main/res/values/strings.xml
  50. 11 0
      app/src/main/res/values/styles.xml
  51. 17 0
      app/src/test/java/com/haochuan/hciptvbasic/ExampleUnitTest.java
  52. 27 0
      build.gradle
  53. 17 0
      gradle.properties
  54. BIN
      gradle/wrapper/gradle-wrapper.jar
  55. 6 0
      gradle/wrapper/gradle-wrapper.properties
  56. 172 0
      gradlew
  57. 84 0
      gradlew.bat
  58. 1 0
      settings.gradle

+ 13 - 0
.gitignore

@@ -0,0 +1,13 @@
+*.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

+ 4 - 0
.idea/encodings.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding" addBOMForNewFiles="with NO BOM" />
+</project>

+ 18 - 0
.idea/gradle.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GradleSettings">
+    <option name="linkedExternalProjectsSettings">
+      <GradleProjectSettings>
+        <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>
+        <option name="resolveModulePerSourceSet" value="false" />
+      </GradleProjectSettings>
+    </option>
+  </component>
+</project>

+ 14 - 0
.idea/misc.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CMakeSettings">
+    <configurations>
+      <configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
+    </configurations>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/build/classes" />
+  </component>
+  <component name="ProjectType">
+    <option name="id" value="Android" />
+  </component>
+</project>

+ 12 - 0
.idea/runConfigurations.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="RunConfigurationProducerService">
+    <option name="ignoredProducers">
+      <set>
+        <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
+        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
+        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
+      </set>
+    </option>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -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>

+ 1 - 0
app/.gitignore

@@ -0,0 +1 @@
+/build

+ 75 - 0
app/build.gradle

@@ -0,0 +1,75 @@
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 28
+    defaultConfig {
+        applicationId "com.haochuan.hciptvbasic"
+        minSdkVersion 19
+        targetSdkVersion 28
+        versionCode 1
+        versionName "1.0"
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        ndk {
+            //APP的build.gradle设置支持的SO库架构
+            abiFilters 'armeabi', 'armeabi-v7a', 'x86'
+        }
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            buildConfigField "Boolean", "isDebug", "false"     //app全局是否调试,发布版本不要开启
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+
+        debug {
+            buildConfigField "Boolean", "isDebug", "true"     //app全局是否调试,调试版本开启
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility 'VERSION_1_8'
+        targetCompatibility 'VERSION_1_8'
+    }
+
+    // Gradle automatically adds 'android.test.runner' as a dependency.
+    useLibrary 'android.test.runner'
+
+    useLibrary 'android.test.base'
+    useLibrary 'android.test.mock'
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation 'com.android.support:appcompat-v7:28.0.0'
+    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
+    implementation 'androidx.appcompat:appcompat:1.0.2'
+    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+    testImplementation 'junit:junit:4.12'
+    androidTestImplementation 'com.android.support.test:runner:1.0.2'
+    // Core library
+    androidTestImplementation 'androidx.test:core:1.0.0'
+
+    // AndroidJUnitRunner and JUnit Rules
+    androidTestImplementation 'androidx.test:runner:1.1.0'
+    androidTestImplementation 'androidx.test:rules:1.1.0'
+
+    // Assertions
+    androidTestImplementation 'androidx.test.ext:junit:1.0.0'
+    androidTestImplementation 'androidx.test.ext:truth:1.0.0'
+    androidTestImplementation 'com.google.truth:truth:0.42'
+
+    // Espresso dependencies
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
+    androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.1.0'
+    androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0'
+    androidTestImplementation 'androidx.test.espresso:espresso-accessibility:3.1.0'
+    androidTestImplementation 'androidx.test.espresso:espresso-web:3.1.0'
+    androidTestImplementation 'androidx.test.espresso.idling:idling-concurrent:3.1.0'
+
+    // The following Espresso dependency can be either "implementation"
+    // or "androidTestImplementation", depending on whether you want the
+    // dependency to appear on your APK's compile classpath or the test APK
+    // classpath.
+    androidTestImplementation 'androidx.test.espresso:espresso-idling-resource:3.1.0'
+    implementation 'com.shuyu:GSYVideoPlayer:7.0.2'
+}

+ 21 - 0
app/proguard-rules.pro

@@ -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

+ 27 - 0
app/src/androidTest/java/com/haochuan/hciptvbasic/ExampleInstrumentedTest.java

@@ -0,0 +1,27 @@
+package com.haochuan.hciptvbasic;
+
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.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.getTargetContext();
+
+        assertEquals("com.haochuan.hciptvbasic", appContext.getPackageName());
+    }
+}

+ 44 - 0
app/src/androidTest/java/com/haochuan/hciptvbasic/MainActivityTest.java

@@ -0,0 +1,44 @@
+package com.haochuan.hciptvbasic;
+
+import android.content.Context;
+
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import androidx.test.rule.ActivityTestRule;
+
+import com.haochuan.hciptvbasic.Util.ScreenSnap;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+
+public class MainActivityTest{
+
+    @Rule
+    public ActivityTestRule<MainActivity> mActivityRule
+            = new ActivityTestRule<>(MainActivity.class);
+
+
+
+   private Context getContext(){
+       return InstrumentationRegistry.getInstrumentation().getContext();
+   }
+
+
+
+    @Test
+    public void playTest(){
+       try{
+           MainActivity activity = mActivityRule.getActivity();
+           String url = "https://gzhc-sxrj.oss-cn-shenzhen.aliyuncs.com/gzhc-djbl/djbl01.mp4";
+           int x = 0;
+           int y = 0;
+           int width = ScreenSnap.getScreenWidth(getContext());
+           int height = ScreenSnap.getScreenHeight(getContext());
+       }catch (Throwable throwable) {
+           throwable.printStackTrace();
+       }
+
+    }
+}

+ 27 - 0
app/src/main/AndroidManifest.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.haochuan.hciptvbasic">
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme">
+        <activity android:name=".test.TestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".MainActivity">
+
+        </activity>
+
+        <uses-library
+            android:name="android.test.runner"
+            android:required="false" />
+    </application>
+
+</manifest>

+ 234 - 0
app/src/main/java/com/haochuan/hciptvbasic/BaseWebActivity.java

@@ -0,0 +1,234 @@
+package com.haochuan.hciptvbasic;
+
+/*
+* 这是主页面的基类,负责webview的初始化工作
+*
+* */
+
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.content.ContextCompat;
+
+import com.haochuan.hciptvbasic.video.BaseMediaPlayer;
+import com.haochuan.hciptvbasic.video.HCPlayer;
+import com.haochuan.hciptvbasic.video.IVideoPlayer;
+import com.haochuan.hciptvbasic.webview.PayToJS;
+import com.haochuan.hciptvbasic.webview.PlayerToJS;
+import com.haochuan.hciptvbasic.webview.HCWebChromeClient;
+import com.haochuan.hciptvbasic.webview.ToolToJS;
+
+public abstract class BaseWebActivity extends AppCompatActivity {
+    private WebView webView;                                    //整个应用唯一的webview
+    private PlayerToJS playerToJS;                              //PlayerToJS类实例
+    private PayToJS payToJS;                                    // PayToJS类实例
+    private ToolToJS toolToJS;                                  //ToolToJS实例
+    String playerToJSName = PlayerToJS.class.getSimpleName();    //playerToJS类名
+    String payToJSName = PayToJS.class.getSimpleName();         //payToJS类名
+    String toolToJSName = ToolToJS.class.getSimpleName();       //toolToJS类名
+    //播放器
+    private HCPlayer mHCPlayer = null;
+
+    /**-----------------------虚函数-----------------------*/
+
+    //获取启动页web地址
+    protected abstract String getIndexURL();
+    //处理intent中的参数
+    protected abstract void handleIntent(Intent intent);
+
+    /*--------------------生命周期---------------------*/
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (getIntent() != null) {
+            handleIntent(getIntent());
+        }
+
+        initPlayer();
+
+        webView = new WebView(this);
+        webView.setBackgroundColor(ContextCompat.getColor(this, android.R.color.transparent));
+//        webView.setBackgroundResource(R.drawable.img_start_bg);
+        setContentView(webView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+
+        initWebSetting(webView);
+        webView.loadUrl(getIndexURL());
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        //关闭软键盘
+        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (webView != null) {
+            webView.onResume();
+        }
+        if(mHCPlayer !=null){
+            mHCPlayer.onResume();
+        }
+
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (webView != null) {
+            webView.onPause();
+        }
+        if(mHCPlayer !=null){
+            mHCPlayer.onPause();
+        }
+    }
+
+    @Override
+    protected void onStop(){
+        super.onStop();
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (webView != null) {
+            webView.removeJavascriptInterface(playerToJSName);
+            webView.clearCache(true);
+            webView.clearFormData();
+            webView.clearMatches();
+            webView.clearSslPreferences();
+            webView.clearDisappearingChildren();
+            webView.clearHistory();
+            webView.clearAnimation();
+            webView.loadUrl("about:blank");
+            webView.removeAllViews();
+
+            webView = null;
+        }
+        if(mHCPlayer !=null){
+            mHCPlayer.onDestroy();
+        }
+        super.onDestroy();
+    }
+
+    /**
+     * webView对象获取"返回"按键事件
+     */
+    @Override
+    public void onBackPressed() {
+        toolToJS.onBackPressed();
+    }
+
+    /*--------------------------初始化函数---------------------------*/
+    /*
+    * 初始化播放器
+    * */
+    private void initPlayer(){
+        mHCPlayer = new HCPlayer(this);
+        mHCPlayer.setIVideoPlayerListener(new IVideoPlayer() {
+            @Override
+            public void onPreparing() {
+                playerToJS.onPlayerPreparing();
+            }
+
+            @Override
+            public void onPlaying() {
+                playerToJS.onPlayerPlaying();
+            }
+
+            @Override
+            public void onResume() {
+                playerToJS.onPlayerResume();
+            }
+
+            @Override
+            public void onPause() {
+                playerToJS.onPlayerPause();
+            }
+
+            @Override
+            public void onDestroy() {
+                playerToJS.onPlayerDestroy();
+            }
+
+            @Override
+            public void onPlayingBuffering() {
+                playerToJS.onPlayingBuffer();
+            }
+
+            @Override
+            public void onCompletion() {
+                playerToJS.onPlayerComplete();
+            }
+
+            @Override
+            public void onError(int what, int extra) {
+                playerToJS.onPlayerError(what,extra);
+            }
+        });
+    }
+
+
+    @SuppressLint({"SetJavaScriptEnabled", "JavascriptInterface", "AddJavascriptInterface"})
+    private void initWebSetting(WebView webView) {
+        WebSettings webSettings = webView.getSettings();
+        // 由H5端适配屏幕,具体参考文档:https://developer.chrome.com/multidevice/webview/pixelperfect
+        webSettings.setUseWideViewPort(true);
+        webSettings.setLoadWithOverviewMode(true);
+        webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
+        // 设置JS交互
+        webSettings.setJavaScriptEnabled(true);
+
+        HCWebChromeClient hcWebChromeClient = new HCWebChromeClient();
+        setPlayerToJS();
+        setPayToJS();
+        setToolToJS();
+
+        webView.addJavascriptInterface(playerToJS,playerToJSName);
+        webView.addJavascriptInterface(payToJS,payToJSName);
+        webView.addJavascriptInterface(toolToJS,toolToJSName);
+        // 设置WebClient
+        webView.setWebViewClient(new WebViewClient());
+        webView.setWebChromeClient(hcWebChromeClient);
+        // 设置是否开启web内容调试,具体调试方式查看:https://developers.google.com/web/tools/chrome-devtools/remote-debugging/?utm_source=dcc&utm_medium=redirect&utm_campaign=2016q3
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            WebView.setWebContentsDebuggingEnabled(BuildConfig.isDebug);
+        }
+    }
+
+    private void setPlayerToJS(){
+        playerToJS = new PlayerToJS(this,webView,mHCPlayer);
+    }
+    private void setPayToJS(){ payToJS = new PayToJS(this,webView); }
+    private void setToolToJS(){ toolToJS = new ToolToJS(this,webView); }
+
+    /*------------------------子类获取实例接口------------------------------*/
+
+    /**
+     * 获取当前WebView对象
+     */
+    protected WebView getWebView(){return this.webView;}
+
+
+    /*
+    * 获取PlayerToJs实例
+    * */
+    protected PlayerToJS getPlayerToJS(){return playerToJS;}
+
+    /*
+    * 获取播放器实例
+    * */
+    protected BaseMediaPlayer getMediaPlayer(){return this.mHCPlayer;}
+
+}

+ 109 - 0
app/src/main/java/com/haochuan/hciptvbasic/MainActivity.java

@@ -0,0 +1,109 @@
+package com.haochuan.hciptvbasic;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.haochuan.hciptvbasic.Util.Juge;
+import com.haochuan.hciptvbasic.Util.Logger;
+import com.haochuan.hciptvbasic.webview.PlayerToJS;
+
+import org.json.JSONObject;
+
+import java.util.Set;
+
+
+public class MainActivity extends BaseWebActivity {
+    private String mBasicUrl = "http://10.255.25.176:8091/sxrj/loading.html";    //入口地址
+    private String mIntentParamsUrl = "";                                               //启动参数拼接地址
+    private String mIntentParamsJson = "";                                              //启动参数集合json
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    /*    //test
+        String url = "https://gzhc-sxrj.oss-cn-shenzhen.aliyuncs.com/gzhc-djbl/djbl01.mp4";
+        int x = 0;
+        int y = 0;
+        int width = ScreenSnap.getScreenWidth(this);
+        int height = ScreenSnap.getScreenHeight(this);
+        playVideo(url,x,y,width,height);*/
+    }
+
+
+
+
+
+    /**-------------------BaseWebActivity重载函数 start--------------------------*/
+
+    /*
+    * 获取WebView入口地址
+    * */
+    @Override
+    protected String getIndexURL() {
+        //mIntentParams不能为空,而且不能是纯IP地址(深圳天威VV玩具就是纯IP地址),才能加到入口地址中
+        if(!mIntentParamsUrl.isEmpty() && !Juge.isPureIp(mBasicUrl)){
+            mBasicUrl += mIntentParamsUrl;
+        }
+        return mBasicUrl;
+    }
+
+    //处理启动参数,1,将参数添加到启动链接的参数中;2将其转化为json,传给前端
+    @Override
+    protected void handleIntent(Intent intent) {
+        try{
+            if(intent != null){
+                Bundle bundle = intent.getExtras();
+                if(bundle != null){
+                    Set<String> keySet = bundle.keySet();
+                    JSONObject intentJson = new JSONObject();
+                    int i = 0;
+                    StringBuilder sb = new StringBuilder(mIntentParamsUrl);
+                    for(String key : keySet){
+                        String bundleValue = String.valueOf(bundle.get(key));
+                        intentJson.put(key,bundleValue);
+                        if(i==0 && !mBasicUrl.contains("?")){
+                            sb.append("?");
+                            sb.append(key);
+                            sb.append("=");
+                            sb.append(bundleValue);
+                        }else{
+                            sb.append("&");
+                            sb.append(key);
+                            sb.append("=");
+                            sb.append(bundleValue);
+                        }
+                        i++;
+                    }
+                    mIntentParamsJson = sb.toString();
+                    mIntentParamsJson = intentJson.toString();
+                    Logger.show(this,"mIntentParamsJson:" + mIntentParamsJson);
+
+                }
+            }else{
+                Logger.show(this,"get intent is null");
+            }
+        }catch (Exception e){
+            Logger.show(this,"get intent exception");
+            e.printStackTrace();
+        }
+    }
+
+
+    /**-------------------BaseWebActivity重载函数 end--------------------------*/
+
+
+    /*-----------------------------------功能函数 start----------------------------------*/
+
+    /*
+    *  将日志发给前端
+    *  @param log    日志内容
+    * */
+
+    public void loggerToJs(String log){
+        getPlayerToJS().logToJs(log);
+    }
+
+
+
+
+}

+ 9 - 0
app/src/main/java/com/haochuan/hciptvbasic/Util/Juge.java

@@ -0,0 +1,9 @@
+package com.haochuan.hciptvbasic.Util;
+
+public class Juge {
+    //判断当前是不是纯IP地址
+    public static boolean isPureIp(String url){
+        url = url.replace("http://","");
+        return !url.contains("/");
+    }
+}

+ 27 - 0
app/src/main/java/com/haochuan/hciptvbasic/Util/Logger.java

@@ -0,0 +1,27 @@
+package com.haochuan.hciptvbasic.Util;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.haochuan.hciptvbasic.BuildConfig;
+import com.haochuan.hciptvbasic.MainActivity;
+
+public class Logger {
+    static String  TAG = "HcIPTV";
+
+
+    public static void show(@NonNull Context context, @NonNull String message){
+        if(BuildConfig.isDebug){
+            Log.d(TAG,message);
+        }
+        //将日志传给MainActivity,然后传给js
+        if(context instanceof MainActivity){
+            MainActivity mainActivity = (MainActivity)context;
+            mainActivity.loggerToJs(message);
+        }
+    }
+
+
+}

+ 337 - 0
app/src/main/java/com/haochuan/hciptvbasic/Util/MacUtil.java

@@ -0,0 +1,337 @@
+package com.haochuan.hciptvbasic.Util;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.Reader;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Enumeration;
+
+public final class MacUtil {
+
+    public static String getMac(Context context) {
+        String strMac;
+
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            Log.d("=====", "6.0以下");
+            strMac = getLocalMacAddressFromWifiInfo(context);
+        } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+            Log.d("=====", "6.0以上7.0以下");
+            strMac = getMacAddress(context);
+        } else {
+            Log.d("=====", "7.0以上");
+            if (!TextUtils.isEmpty(getMacAddress())) {
+                Log.d("=====", "7.0以上1");
+                strMac = getMacAddress();
+            } else if (!TextUtils.isEmpty(getMachineHardwareAddress())) {
+                Log.d("=====", "7.0以上2");
+                strMac = getMachineHardwareAddress();
+            } else {
+                Log.d("=====", "7.0以上3");
+                strMac = getLocalMacAddressFromBusybox();
+            }
+        }
+
+        if (TextUtils.isEmpty(strMac)) {
+            strMac = "02:00:00:00:00:00";
+        }
+
+        return strMac;
+    }
+
+
+    /**
+     * 根据wifi信息获取本地mac
+     */
+    @SuppressLint("HardwareIds")
+    public static String getLocalMacAddressFromWifiInfo(Context context) {
+        WifiManager wifi = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+        WifiInfo info = null;
+        if (wifi != null) {
+            info = wifi.getConnectionInfo();
+        }
+        String mac = null;
+        if (info != null) {
+            mac = info.getMacAddress();
+        }
+        return mac;
+    }
+
+    /**
+     * android 6.0及以上、7.0以下 获取mac地址
+     */
+    public static String getMacAddress(Context context) {
+        // 如果是6.0以下,直接通过wifimanager获取
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+            String macAddress0 = getMacAddress0(context);
+            if (!TextUtils.isEmpty(macAddress0)) {
+                return macAddress0;
+            }
+        }
+        return getMacAddress2();
+    }
+
+    public static String getMacAddress2() {
+        String str = "";
+        String macSerial = "";
+        try {
+            Process pp = Runtime.getRuntime().exec("cat /sys/class/net/wlan0/address");
+            InputStreamReader ir = new InputStreamReader(pp.getInputStream());
+            LineNumberReader input = new LineNumberReader(ir);
+            for (; null != str; ) {
+                str = input.readLine();
+                if (str != null) {
+                    macSerial = str.trim();// 去空格
+                    break;
+                }
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            Log.e("----->" + "NetInfoManager", "getMacAddress:" + ex.toString());
+        }
+        if (TextUtils.isEmpty(macSerial)) {
+            try {
+                return loadFileAsString("/sys/class/net/eth0/address").toUpperCase().substring(0, 17);
+            } catch (Exception e) {
+                e.printStackTrace();
+                Log.e("----->" + "NetInfoManager", "getMacAddress:" + e.toString());
+            }
+
+        }
+        return macSerial;
+    }
+
+    @SuppressLint("HardwareIds")
+    private static String getMacAddress0(Context context) {
+        if (isAccessWifiStateAuthorized(context)) {
+            WifiManager wifiMgr = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+            WifiInfo wifiInfo = null;
+            try {
+                if (wifiMgr != null) {
+                    wifiInfo = wifiMgr.getConnectionInfo();
+                }
+                if (wifiInfo != null) {
+                    return wifiInfo.getMacAddress();
+                }
+            } catch (Exception e) {
+                Log.e("----->" + "NetInfoManager", "getMacAddress0:" + e.toString());
+            }
+        }
+        return "";
+
+    }
+
+    /**
+     * Check whether accessing wifi state is permitted
+     */
+    private static boolean isAccessWifiStateAuthorized(Context context) {
+        if (PackageManager.PERMISSION_GRANTED == context.checkCallingOrSelfPermission(Manifest.permission.ACCESS_WIFI_STATE)) {
+            Log.d("----->" + "NetInfoManager", "isAccessWifiStateAuthorized:" + "access wifi state is enabled");
+            return true;
+        } else
+            return false;
+    }
+
+    private static String loadFileAsString(String fileName) throws Exception {
+        FileReader reader = new FileReader(fileName);
+        String text = loadReaderAsString(reader);
+        reader.close();
+        return text;
+    }
+
+    private static String loadReaderAsString(Reader reader) throws Exception {
+        StringBuilder builder = new StringBuilder();
+        char[] buffer = new char[4096];
+        int readLength = reader.read(buffer);
+        while (readLength >= 0) {
+            builder.append(buffer, 0, readLength);
+            readLength = reader.read(buffer);
+        }
+        return builder.toString();
+    }
+
+    /**
+     * 根据IP地址获取MAC地址
+     */
+    public static String getMacAddress() {
+        String strMacAddr = null;
+        try {
+            // 获得IpD地址
+            InetAddress ip = getLocalInetAddress();
+            byte[] b = NetworkInterface.getByInetAddress(ip).getHardwareAddress();
+            StringBuilder buffer = new StringBuilder();
+            for (int i = 0; i < b.length; i++) {
+                if (i != 0) {
+                    buffer.append(':');
+                }
+                String str = Integer.toHexString(b[i] & 0xFF);
+                buffer.append(str.length() == 1 ? 0 + str : str);
+            }
+            strMacAddr = buffer.toString().toUpperCase();
+        } catch (Exception e) {
+            e.printStackTrace();
+            Log.e("====", e.getMessage());
+        }
+        return strMacAddr;
+    }
+
+    /**
+     * 获取移动设备本地IP
+     */
+    private static InetAddress getLocalInetAddress() {
+        InetAddress ip = null;
+        try {
+            // 列举
+            Enumeration<NetworkInterface> en_netInterface = NetworkInterface
+                    .getNetworkInterfaces();
+            while (en_netInterface.hasMoreElements()) {// 是否还有元素
+                NetworkInterface ni = en_netInterface.nextElement();// 得到下一个元素
+                Enumeration<InetAddress> en_ip = ni.getInetAddresses();// 得到一个ip地址的列举
+                while (en_ip.hasMoreElements()) {
+                    ip = en_ip.nextElement();
+                    if (!ip.isLoopbackAddress() && !ip.getHostAddress().contains(":")) {
+                        break;
+                    } else {
+                        ip = null;
+                    }
+                }
+
+                if (ip != null) {
+                    break;
+                }
+            }
+        } catch (SocketException e) {
+
+            e.printStackTrace();
+        }
+        return ip;
+    }
+
+    /**
+     * 获取本地IP
+     */
+    private static String getLocalIpAddress() {
+        try {
+            for (Enumeration<NetworkInterface> en = NetworkInterface
+                    .getNetworkInterfaces(); en.hasMoreElements(); ) {
+                NetworkInterface intf = en.nextElement();
+                for (Enumeration<InetAddress> enumIpAddr = intf
+                        .getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
+                    InetAddress inetAddress = enumIpAddr.nextElement();
+                    if (!inetAddress.isLoopbackAddress()) {
+                        return inetAddress.getHostAddress();
+                    }
+                }
+            }
+        } catch (SocketException ex) {
+            ex.printStackTrace();
+        }
+        return null;
+    }
+
+    // android 7.0及以上 (2)扫描各个网络接口获取mac地址
+
+    /**
+     * 获取设备HardwareAddress地址
+     */
+    public static String getMachineHardwareAddress() {
+        Enumeration<NetworkInterface> interfaces = null;
+        try {
+            interfaces = NetworkInterface.getNetworkInterfaces();
+        } catch (SocketException e) {
+            e.printStackTrace();
+        }
+        String hardWareAddress = null;
+        NetworkInterface iF = null;
+        if (interfaces == null) {
+            return null;
+        }
+        while (interfaces.hasMoreElements()) {
+            iF = interfaces.nextElement();
+            try {
+                hardWareAddress = bytesToString(iF.getHardwareAddress());
+                if (hardWareAddress != null)
+                    break;
+            } catch (SocketException e) {
+                e.printStackTrace();
+            }
+        }
+        return hardWareAddress;
+    }
+
+    /***
+     * byte转为String
+     */
+    private static String bytesToString(byte[] bytes) {
+        if (bytes == null || bytes.length == 0) {
+            return null;
+        }
+        StringBuilder buf = new StringBuilder();
+        for (byte b : bytes) {
+            buf.append(String.format("%02X:", b));
+        }
+        if (buf.length() > 0) {
+            buf.deleteCharAt(buf.length() - 1);
+        }
+        return buf.toString();
+    }
+
+    // android 7.0及以上 (3)通过busybox获取本地存储的mac地址
+
+    /**
+     * 根据busybox获取本地Mac
+     *
+     * @return
+     */
+    public static String getLocalMacAddressFromBusybox() {
+        String result = "";
+        String Mac = "";
+        result = callCmd("busybox ifconfig", "HWaddr");
+        // 如果返回的result == null,则说明网络不可取
+        if (result == null) {
+            return "网络异常";
+        }
+        // 对该行数据进行解析
+        // 例如:eth0 Link encap:Ethernet HWaddr 00:16:E8:3E:DF:67
+        if (result.length() > 0 && result.contains("HWaddr")) {
+            Mac = result.substring(result.indexOf("HWaddr") + 6,
+                    result.length() - 1);
+            result = Mac;
+        }
+        return result;
+    }
+
+    private static String callCmd(String cmd, String filter) {
+        String result = "";
+        String line = "";
+        try {
+            Process proc = Runtime.getRuntime().exec(cmd);
+            InputStreamReader is = new InputStreamReader(proc.getInputStream());
+            BufferedReader br = new BufferedReader(is);
+
+            while ((line = br.readLine()) != null
+                    && !line.contains(filter)) {
+                result += line;
+            }
+
+            result = line;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return result;
+    }
+
+}

+ 36 - 0
app/src/main/java/com/haochuan/hciptvbasic/Util/ScreenSnap.java

@@ -0,0 +1,36 @@
+package com.haochuan.hciptvbasic.Util;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.os.Build;
+import android.view.WindowManager;
+
+public class ScreenSnap {
+
+    public static int getScreenWidth(Context context) {
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        Point point = new Point();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            //noinspection ConstantConditions
+            wm.getDefaultDisplay().getRealSize(point);
+        } else {
+            //noinspection ConstantConditions
+            wm.getDefaultDisplay().getSize(point);
+        }
+        return point.x;
+    }
+
+
+    public static int getScreenHeight(Context context) {
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        Point point = new Point();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            //noinspection ConstantConditions
+            wm.getDefaultDisplay().getRealSize(point);
+        } else {
+            //noinspection ConstantConditions
+            wm.getDefaultDisplay().getSize(point);
+        }
+        return point.y;
+    }
+}

+ 70 - 0
app/src/main/java/com/haochuan/hciptvbasic/test/TestActivity.java

@@ -0,0 +1,70 @@
+package com.haochuan.hciptvbasic.test;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Button;
+
+import com.haochuan.hciptvbasic.BaseWebActivity;
+import com.haochuan.hciptvbasic.R;
+import com.haochuan.hciptvbasic.Util.ScreenSnap;
+import com.haochuan.hciptvbasic.webview.PlayerToJS;
+
+public class TestActivity extends BaseWebActivity {
+
+    @Override
+    protected String getIndexURL() {
+        return "";
+    }
+
+    @Override
+    protected void handleIntent(Intent intent) {
+
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_test);
+
+        Button playBtn = findViewById(R.id.play_btn);
+        playBtn.setOnClickListener(v -> {
+            String url = "https://gzhc-sxrj.oss-cn-shenzhen.aliyuncs.com/gzhc-djbl/djbl01.mp4";
+            int x = 0;
+            int y = 0;
+            int width = ScreenSnap.getScreenWidth(TestActivity.this);
+            int height = ScreenSnap.getScreenHeight(TestActivity.this);
+            getPlayerToJS().play(url,x,y,width,height);
+        });
+
+        Button pauseBtn = findViewById(R.id.pause_btn);
+        pauseBtn.setOnClickListener(v -> getPlayerToJS().pause());
+
+        Button resumeBtn = findViewById(R.id.resume_btn);
+        resumeBtn.setOnClickListener(v -> getPlayerToJS().resume());
+
+        Button forwardBtn = findViewById(R.id.forward_btn);
+        forwardBtn.setOnClickListener(v -> {
+            int currentPos = getMediaPlayer().getCurrentPlayPosition();
+            int seekPos = currentPos + 5000;
+            if(seekPos > getMediaPlayer().getDuration()){
+                seekPos = getMediaPlayer().getDuration() - 1000;
+            }
+            getPlayerToJS().seek(seekPos);
+        });
+
+        Button backBtn = findViewById(R.id.back_btn);
+        backBtn.setOnClickListener(v -> {
+            int currentPos = getMediaPlayer().getCurrentPlayPosition();
+            int seekPos = currentPos - 5000;
+            if(seekPos < 0){
+                seekPos = 1000;
+            }
+            getPlayerToJS().seek(seekPos);
+        });
+
+        Button exitBtn = findViewById(R.id.exit_btn);
+        exitBtn.setOnClickListener(v -> getPlayerToJS().exit());
+    }
+
+
+}

+ 84 - 0
app/src/main/java/com/haochuan/hciptvbasic/video/BaseMediaPlayer.java

@@ -0,0 +1,84 @@
+package com.haochuan.hciptvbasic.video;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public abstract class BaseMediaPlayer extends FrameLayout {
+
+
+    public BaseMediaPlayer(@NonNull Context context) {
+        super(context);
+    }
+
+    public BaseMediaPlayer(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public BaseMediaPlayer(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    /*---------------------------操作函数------------------------*/
+    /*
+    * 播放视频
+    *@param url
+    * */
+    public abstract void play(String url);
+
+    /*
+     * 恢复
+     *@param url
+     * */
+    public abstract void resume();
+
+    /*
+     * 暂停
+     * */
+    public abstract void pause();
+
+    /*
+     * 拖动
+     * @param 目标位置,单位毫秒
+     * */
+    public abstract void seek(int position);
+
+
+
+
+    /*
+    * 资源释放
+    * */
+    public abstract void release();
+
+    /*---------------------------获取播放器状态和参数函数------------------------*/
+
+
+    /*
+     * 当前是否正在播放
+     * */
+    public abstract boolean isPlaying();
+
+
+    /*
+     * 视频是否准备完毕
+     * */
+    public abstract boolean isPrePared();
+
+
+    /*
+     * 获得视频时长
+     * */
+    public abstract int getDuration();
+
+
+    /*
+     * 获得当前播放时长
+     * */
+    public abstract int getCurrentPlayPosition();
+
+
+}

+ 123 - 0
app/src/main/java/com/haochuan/hciptvbasic/video/EmptyControlVideoView.java

@@ -0,0 +1,123 @@
+package com.haochuan.hciptvbasic.video;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.NonNull;
+
+import com.haochuan.hciptvbasic.R;
+import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer;
+
+/**
+ * 无任何控制ui的播放
+ * Created by 许林 on 2017/8/6.
+ */
+
+public class EmptyControlVideoView extends StandardGSYVideoPlayer {
+
+    IVideoPlayer iVideoPlayerListener ;
+
+    public EmptyControlVideoView(Context context, Boolean fullFlag) {
+        super(context, fullFlag);
+    }
+
+    public EmptyControlVideoView(Context context) {
+        super(context);
+    }
+
+    public EmptyControlVideoView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+
+    @Override
+    public int getLayoutId() {
+        return R.layout.view_video_control_empty;
+    }
+
+    @Override
+    protected void touchSurfaceMoveFullLogic(float absDeltaX, float absDeltaY) {
+        super.touchSurfaceMoveFullLogic(absDeltaX, absDeltaY);
+        //不给触摸快进,如果需要,屏蔽下方代码即可
+        mChangePosition = false;
+
+        //不给触摸音量,如果需要,屏蔽下方代码即可
+        mChangeVolume = false;
+
+        //不给触摸亮度,如果需要,屏蔽下方代码即可
+        mBrightness = false;
+    }
+
+    @Override
+    protected void touchDoubleUp() {
+        //super.touchDoubleUp();
+        //不需要双击暂停
+    }
+    /*---------------------------播放器生命周期-----------------------------*/
+    @Override
+    protected void changeUiToPreparingShow() {
+        super.changeUiToPreparingShow();
+        if(iVideoPlayerListener != null){
+            iVideoPlayerListener.onPreparing();
+        }
+    }
+
+    @Override
+    protected void changeUiToPlayingShow() {
+        super.changeUiToPlayingShow();
+        if(iVideoPlayerListener != null){
+            iVideoPlayerListener.onPlaying();
+        }
+    }
+
+    @Override
+    protected void changeUiToPauseShow() {
+       super.changeUiToPauseShow();
+        if(iVideoPlayerListener != null){
+            iVideoPlayerListener.onPause();
+        }
+    }
+
+    @Override
+    protected void changeUiToPlayingBufferingShow() {
+        super.changeUiToPlayingBufferingShow();
+        if(iVideoPlayerListener != null){
+            iVideoPlayerListener.onPlayingBuffering();
+        }
+    }
+
+    @Override
+    protected void changeUiToCompleteShow() {
+       super.changeUiToCompleteShow();
+        if(iVideoPlayerListener != null){
+            iVideoPlayerListener.onCompletion();
+        }
+    }
+
+    @Override
+    public void onError(int what, int extra){
+        super.changeUiToError();
+        if(iVideoPlayerListener != null){
+            iVideoPlayerListener.onError(what,extra);
+        }
+    }
+
+
+    /**---------------------功能函数------------------*/
+    public boolean isPrePared(){
+        return mHadPrepared;
+    }
+
+    public Context getCurrentContext(){
+        return getActivityContext();
+    }
+
+    public void setVideoPlayerListener(@NonNull IVideoPlayer iVideoPlayer){
+        this.iVideoPlayerListener = iVideoPlayer;
+    }
+
+
+    /*-----------------*/
+
+
+}

+ 111 - 0
app/src/main/java/com/haochuan/hciptvbasic/video/HCGsyVideoPlayer.java

@@ -0,0 +1,111 @@
+package com.haochuan.hciptvbasic.video;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.haochuan.hciptvbasic.R;
+import com.haochuan.hciptvbasic.Util.Logger;
+
+public class HCGsyVideoPlayer extends BaseMediaPlayer {
+
+    private EmptyControlVideoView mEmptyControlVideo;        //GSY播放器实例
+
+    public HCGsyVideoPlayer(@NonNull Context context) {
+        super(context);
+        init(context);
+    }
+
+    public HCGsyVideoPlayer(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        init(context);
+    }
+
+    public HCGsyVideoPlayer(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context);
+    }
+
+
+    /*
+    * 初始化
+    * */
+    private void init(Context context){
+        View.inflate(context, R.layout.player_gsy_hc,this);
+        mEmptyControlVideo = findViewById(R.id.empty_control_video);
+    }
+
+    public void setVideoPlayerListener(@NonNull IVideoPlayer iVideoPlayer){
+        mEmptyControlVideo.setVideoPlayerListener(iVideoPlayer);
+    }
+
+
+    /*----------------------------从父类继承 播放器功能函数----------------------------------*/
+    @Override
+    public void play(@NonNull String url) {
+        mEmptyControlVideo.setUp(url,false,"");
+        mEmptyControlVideo.startPlayLogic();
+    }
+
+    @Override
+    public void resume() {
+        mEmptyControlVideo.onVideoResume();
+    }
+
+    @Override
+    public void pause() {
+        mEmptyControlVideo.onVideoPause();
+    }
+
+    @Override
+    public void seek(int position) {
+        if(isPrePared()){
+            mEmptyControlVideo.seekTo(position);
+        }else{
+            Logger.show(mEmptyControlVideo.getCurrentContext(),"视频未准备,不能拖动");
+        }
+    }
+
+    public void setSpeed(float speed){
+        mEmptyControlVideo.setSpeed(speed);
+    }
+
+    @Override
+    public boolean isPlaying() {
+        return mEmptyControlVideo.isInPlayingState();
+    }
+
+    @Override
+    public boolean isPrePared() {
+        return mEmptyControlVideo.isPrePared();
+    }
+
+    @Override
+    public int getDuration() {
+        if(isPrePared()){
+            return mEmptyControlVideo.getDuration();
+        }else{
+            Logger.show(mEmptyControlVideo.getCurrentContext(),"视频未准备,不能获得视频总时长");
+            return 0;
+        }
+    }
+
+    @Override
+    public int getCurrentPlayPosition() {
+        if(isPrePared()){
+            return mEmptyControlVideo.getCurrentPositionWhenPlaying();
+        }else{
+            Logger.show(mEmptyControlVideo.getCurrentContext(),"视频未准备,不能获得视频当前位置");
+            return 0;
+        }
+    }
+    @Override
+    public void release(){
+        mEmptyControlVideo.release();
+    }
+
+
+}

+ 171 - 0
app/src/main/java/com/haochuan/hciptvbasic/video/HCPlayer.java

@@ -0,0 +1,171 @@
+package com.haochuan.hciptvbasic.video;
+
+import android.app.Activity;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.haochuan.hciptvbasic.R;
+import com.haochuan.hciptvbasic.Util.Logger;
+import com.shuyu.gsyvideoplayer.utils.CommonUtil;
+
+public class HCPlayer extends BaseMediaPlayer implements IVideoPlayer{
+
+    private HCGsyVideoPlayer mHcGsyVideoPlayer;
+
+    private IVideoPlayer iVideoPlayerListener;
+
+    public HCPlayer(@NonNull Context context) {
+        super(context);
+        init(context);
+    }
+
+    public HCPlayer(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        init(context);
+    }
+
+    public HCPlayer(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context);
+    }
+
+
+    /**--------------------------功能性函数------------------------*/
+
+    /*
+    * 视频VIEW初始化
+    * */
+    private void init(Context context){
+        View.inflate(context, R.layout.player_hc,this);
+        mHcGsyVideoPlayer = findViewById(R.id.hc_gsy_player);
+        mHcGsyVideoPlayer.setVideoPlayerListener(this);
+    }
+
+    public void setIVideoPlayerListener(IVideoPlayer listener){
+        this.iVideoPlayerListener = listener;
+    }
+
+    /*--------------------------播放器通用事件接口-----------------------------*/
+    @Override
+    public void onPreparing() {
+        if(iVideoPlayerListener != null){
+            iVideoPlayerListener.onPreparing();
+        }else{
+            Logger.show(CommonUtil.getActivityContext(getContext()),"错误!HCPlayer 未设置监听接口");
+        }
+    }
+
+    @Override
+    public void onPlaying() {
+        if(iVideoPlayerListener != null){
+            iVideoPlayerListener.onPlaying();
+        }else{
+            Logger.show(CommonUtil.getActivityContext(getContext()),"错误!HCPlayer 未设置监听接口");
+        }
+    }
+
+    @Override
+    public void onResume() {
+        resume();
+    }
+
+    @Override
+    public void onPause() {
+        if(iVideoPlayerListener != null){
+            iVideoPlayerListener.onPause();
+        }else{
+            Logger.show(CommonUtil.getActivityContext(getContext()),"错误!HCPlayer 未设置监听接口");
+        }
+    }
+
+
+    @Override
+    public void onDestroy() {
+        release();
+    }
+
+    @Override
+    public void onPlayingBuffering() {
+        if(iVideoPlayerListener != null){
+            iVideoPlayerListener.onPlayingBuffering();
+        }else{
+            Logger.show(CommonUtil.getActivityContext(getContext()),"错误!HCPlayer 未设置监听接口");
+        }
+    }
+
+    @Override
+    public void onCompletion() {
+        if(iVideoPlayerListener != null){
+            iVideoPlayerListener.onCompletion();
+        }else{
+            Logger.show(CommonUtil.getActivityContext(getContext()),"错误!HCPlayer 未设置监听接口");
+        }
+    }
+
+
+    @Override
+    public void onError(int what, int extra) {
+        if(iVideoPlayerListener != null){
+            iVideoPlayerListener.onError(what,extra);
+        }else{
+            Logger.show(CommonUtil.getActivityContext(getContext()),"错误!HCPlayer 未设置监听接口");
+        }
+    }
+
+    /*------------------------通用操作接口-------------------------*/
+    @Override
+    public void play(String url) {
+        mHcGsyVideoPlayer.play(url);
+    }
+
+    @Override
+    public void resume() {
+        mHcGsyVideoPlayer.resume();
+    }
+
+    @Override
+    public void pause() {
+        mHcGsyVideoPlayer.pause();
+    }
+
+    @Override
+    public void seek(int position) {
+        mHcGsyVideoPlayer.seek(position);
+    }
+
+    @Override
+    public void release() {
+        mHcGsyVideoPlayer.release();
+    }
+
+    @Override
+    public boolean isPlaying() {
+        return mHcGsyVideoPlayer.isPlaying();
+    }
+
+    @Override
+    public boolean isPrePared() {
+        return mHcGsyVideoPlayer.isPrePared();
+    }
+
+    @Override
+    public int getDuration() {
+        return mHcGsyVideoPlayer.getDuration();
+    }
+
+    @Override
+    public int getCurrentPlayPosition() {
+        return mHcGsyVideoPlayer.getCurrentPlayPosition();
+    }
+
+    public void setSpeed(@NonNull float speed){
+        mHcGsyVideoPlayer.setSpeed(speed);
+    }
+
+
+
+}

+ 19 - 0
app/src/main/java/com/haochuan/hciptvbasic/video/IVideoPlayer.java

@@ -0,0 +1,19 @@
+package com.haochuan.hciptvbasic.video;
+
+public interface IVideoPlayer {
+    void onPreparing();
+
+    void onPlaying();
+
+    void onResume();
+
+    void onPause();
+
+    void onDestroy();
+
+    void onPlayingBuffering();
+
+    void onCompletion();
+
+    void onError(int what, int extra);
+}

+ 26 - 0
app/src/main/java/com/haochuan/hciptvbasic/webview/HCWebChromeClient.java

@@ -0,0 +1,26 @@
+package com.haochuan.hciptvbasic.webview;
+
+import android.util.Log;
+import android.webkit.ConsoleMessage;
+import android.webkit.WebChromeClient;
+
+
+public class HCWebChromeClient extends WebChromeClient {
+
+    private String errorMsg;
+    private String TAG = "HCWebApp";
+
+    @Override
+    public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
+        errorMsg = consoleMessage.message() + " -- From line "
+                + consoleMessage.lineNumber() + " of "
+                + consoleMessage.sourceId();
+        Log.d(TAG,  errorMsg);
+        return true;
+    }
+
+    public String getErrorMsg(){
+        return errorMsg;
+    }
+
+}

+ 141 - 0
app/src/main/java/com/haochuan/hciptvbasic/webview/PayToJS.java

@@ -0,0 +1,141 @@
+package com.haochuan.hciptvbasic.webview;
+
+import android.content.Context;
+import android.os.Build;
+import android.util.Log;
+import android.webkit.JavascriptInterface;
+import android.webkit.WebView;
+
+import androidx.annotation.StringDef;
+
+import com.haochuan.hciptvbasic.Util.Logger;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import static com.haochuan.hciptvbasic.webview.PayToJS.JsEvent.JS_EVENT_AUTH_RESULT;
+import static com.haochuan.hciptvbasic.webview.PayToJS.JsEvent.JS_EVENT_PAY_RESULT;
+import static com.haochuan.hciptvbasic.webview.PayToJS.JsEvent.JS_EVENT_SDK_INIT_RESULT;
+
+public class PayToJS {
+    private Context context;                        //MainActivity 句柄
+    private WebView webView;
+
+    public PayToJS(Context context, WebView webView) {
+        this.context = context;
+        this.webView = webView;
+    }
+
+    /**
+     * JS调用类型
+     */
+    @StringDef({JS_EVENT_SDK_INIT_RESULT,JS_EVENT_AUTH_RESULT,JS_EVENT_PAY_RESULT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface JsEvent{
+
+        /**
+         * 将SDK初始化结果传递给js
+         * 参数:0,成功;-1,失败
+         */
+        String JS_EVENT_SDK_INIT_RESULT = "javascript:onSDKInitResult(%s)";
+
+        /**
+         * 将SDK鉴权结果传递给js
+         * 参数:json字符串
+         */
+        String JS_EVENT_AUTH_RESULT = "javascript:onAuthResult('%s')";
+
+
+        /*
+        * 将计费结果传递给js
+        * 参数:json字符串
+        * */
+        String JS_EVENT_PAY_RESULT = "javascript:onPayResult('%s')";
+
+
+    }
+
+    /*--------------------功能性函数-----------------------------*/
+    /**
+     * 调用js事件
+     */
+    private void evaluateJavascript(WebView webView, @PayToJS.JsEvent String script) {
+        Logger.show(context,"ToolToJS 执行脚本:" + script);
+        if (webView == null) {
+            Logger.show(context,"webView对象为空,JS事件调用无法执行");
+            return;
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            webView.evaluateJavascript(script, value -> {
+                //此处为 js 返回的结果
+                Logger.show(context,value);
+            });
+        } else {
+            webView.loadUrl(script);
+        }
+    }
+
+    /*---------------------------功能性事件--------------------------------*/
+
+    /*
+    * SDK初始化
+    * */
+    @JavascriptInterface
+    public void sdkInit(String paramsJson){
+        //在这里添加SDK初始化逻辑
+    }
+
+    /*
+    * 鉴权
+    * */
+    @JavascriptInterface
+    public void auth(String paramsJson){
+        //在这里添加鉴权逻辑
+    }
+
+
+    /*
+    * 计费
+    * */
+    @JavascriptInterface
+    public void pay(String paramsJson){
+        //在这里添加支付逻辑
+
+    }
+
+    /*
+    * 获取用户ID
+    * */
+    @JavascriptInterface
+    public String getUserId(){
+        //这这里添加获取用户ID逻辑
+
+        return "";
+    }
+
+    /*---------------------------事件函数--------------------------------*/
+
+    /**
+     * 将SDK初始化结果传递给js
+     * 参数:0,成功;-1,失败
+     */
+    public void onSDKInitResult(int code){
+        evaluateJavascript(webView, String.format(JS_EVENT_SDK_INIT_RESULT,code));
+    }
+
+    /**
+     * 将SDK鉴权结果传递给js
+     * 参数:json字符串
+     */
+    public void onAuthResult(String paramJson){
+        evaluateJavascript(webView, String.format(JS_EVENT_SDK_INIT_RESULT,paramJson));
+    }
+
+    /*
+     * 将计费结果传递给js
+     * 参数:json字符串
+     * */
+    public void onPayResult(String paramJson){
+        evaluateJavascript(webView, String.format(JS_EVENT_SDK_INIT_RESULT,paramJson));
+    }
+}

+ 283 - 0
app/src/main/java/com/haochuan/hciptvbasic/webview/PlayerToJS.java

@@ -0,0 +1,283 @@
+package com.haochuan.hciptvbasic.webview;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Build;
+import android.util.Log;
+import android.view.ViewGroup;
+import android.webkit.JavascriptInterface;
+import android.webkit.WebView;
+import android.widget.FrameLayout;
+
+import androidx.annotation.StringDef;
+
+import com.haochuan.hciptvbasic.Util.Logger;
+import com.haochuan.hciptvbasic.video.BaseMediaPlayer;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import static com.haochuan.hciptvbasic.webview.PlayerToJS.JsEvent.JS_EVENT_COMPLETE;
+import static com.haochuan.hciptvbasic.webview.PlayerToJS.JsEvent.JS_EVENT_DESTROY;
+import static com.haochuan.hciptvbasic.webview.PlayerToJS.JsEvent.JS_EVENT_LOG;
+import static com.haochuan.hciptvbasic.webview.PlayerToJS.JsEvent.JS_EVENT_PAUSE;
+import static com.haochuan.hciptvbasic.webview.PlayerToJS.JsEvent.JS_EVENT_PLAYERROR;
+import static com.haochuan.hciptvbasic.webview.PlayerToJS.JsEvent.JS_EVENT_PLAYING;
+import static com.haochuan.hciptvbasic.webview.PlayerToJS.JsEvent.JS_EVENT_PLAYINGBUFFER;
+import static com.haochuan.hciptvbasic.webview.PlayerToJS.JsEvent.JS_EVENT_PREPARING;
+import static com.haochuan.hciptvbasic.webview.PlayerToJS.JsEvent.JS_EVENT_RESUME;
+
+public class PlayerToJS {
+    private Context context;                        //MainActivity 句柄
+    private BaseMediaPlayer baseMediaPlayer;        //播放器
+    private WebView webView;
+
+
+    public PlayerToJS(Context context, WebView webView, BaseMediaPlayer mediaPlayer) {
+        this.context = context;
+        this.baseMediaPlayer = mediaPlayer;
+        this.webView = webView;
+    }
+
+    /**
+     * JS调用类型
+     */
+    @StringDef({JS_EVENT_LOG,JS_EVENT_PREPARING,JS_EVENT_PLAYING,
+            JS_EVENT_RESUME,JS_EVENT_PAUSE,JS_EVENT_DESTROY,JS_EVENT_PLAYINGBUFFER,
+            JS_EVENT_COMPLETE,JS_EVENT_PLAYERROR})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface JsEvent{
+
+
+        /**
+         * 将日志传递给js
+         */
+        String JS_EVENT_LOG = "javascript:onLog('%s')";
+
+        /*--------------------传给前端的播放器事件--------------------------*/
+        String JS_EVENT_PREPARING = "javascript:onPlayerPreparing()";
+        String JS_EVENT_PLAYING = "javascript:onPlayerPlaying()";
+        String JS_EVENT_RESUME = "javascript:onPlayerResume()";
+        String JS_EVENT_PAUSE = "javascript:onPlayerPause()";
+        String JS_EVENT_DESTROY="javascript:onPlayerDestroy()";
+        String JS_EVENT_PLAYINGBUFFER = "javascript:onPlayingBuffer()";
+        String JS_EVENT_COMPLETE = "javascript:onPlayerComplete()";
+        String JS_EVENT_PLAYERROR="javascript:onPlayerError(%s,%s)";
+    }
+
+    /*--------------------功能性函数-----------------------------*/
+    /**
+     * 调用js事件
+     */
+    private void evaluateJavascript(WebView webView, @JsEvent String script) {
+        Logger.show(context,"PlayerToJS 执行脚本:" + script);
+        if (webView == null) {
+            Logger.show(context,"webView对象为空,JS事件调用无法执行");
+            return;
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            webView.evaluateJavascript(script, value -> {
+                //此处为 js 返回的结果
+                Logger.show(context,value);
+            });
+        } else {
+            webView.loadUrl(script);
+        }
+    }
+
+    /**-----------------------------------写入前端函数实现-----------------------------------**/
+
+
+    /*
+    * 将log传递给前端
+    * */
+    public void logToJs(String log){
+        evaluateJavascript(webView,
+                String.format(JS_EVENT_LOG,log));
+    }
+
+    /*---------------------------事件函数---------------------------*/
+    /*
+     * 播放器准备事件
+     * */
+    public void onPlayerPreparing(){
+        evaluateJavascript(webView,JS_EVENT_PREPARING);
+    }
+
+    /*
+     * 播放器开始播放事件
+     * */
+    public void onPlayerPlaying(){
+        evaluateJavascript(webView,JS_EVENT_PLAYING);
+    }
+
+    /*
+     * 播放器继续播放事件
+     * */
+    public void onPlayerResume(){
+        evaluateJavascript(webView,JS_EVENT_RESUME);
+    }
+
+    /*
+     * 播放器暂停播放事件
+     * */
+    public void onPlayerPause(){
+        evaluateJavascript(webView,JS_EVENT_PAUSE);
+    }
+
+    /*
+     * 播放器销毁释放事件
+     * */
+    public void onPlayerDestroy(){
+        evaluateJavascript(webView,JS_EVENT_DESTROY);
+    }
+
+    /*
+     * 播放器缓冲事件
+     * */
+    public void onPlayingBuffer(){
+        evaluateJavascript(webView,JS_EVENT_PLAYINGBUFFER);
+    }
+
+    /*
+     * 播放器播放完毕事件
+     * */
+    public void onPlayerComplete(){
+        evaluateJavascript(webView,JS_EVENT_COMPLETE);
+    }
+
+    /*
+     * 播放器播放错误事件
+     * */
+    public void onPlayerError(int what, int extra){
+        evaluateJavascript(webView,String.format(JS_EVENT_PLAYERROR,what,extra));
+    }
+
+    /*----------------------------------播放器功能函数----------------------------------------*/
+
+    /*
+    * 播放函数
+    * */
+    @JavascriptInterface
+    public void play(String url, int x,int y,int width, int height){
+        if(baseMediaPlayer == null){
+            Logger.show(context,"播放器为空,不能播放");
+            return;
+        }
+        Activity activity = (Activity)context;
+        activity.runOnUiThread(()->{
+            initVideoParamsIfNoInit(baseMediaPlayer,x, y, width, height);
+            baseMediaPlayer.play(url);
+            webView.requestFocus();
+        });
+    }
+
+    /*
+     * 暂停
+     * */
+    @JavascriptInterface
+    public void pause(){
+        if(baseMediaPlayer == null){
+            Logger.show(context,"播放器为空,不能暂停");
+            return;
+        }
+        baseMediaPlayer.pause();
+    }
+
+    /*
+     * 恢复
+     * */
+    @JavascriptInterface
+    public void resume(){
+        if(baseMediaPlayer == null){
+            Logger.show(context,"播放器为空,不能恢复");
+            return;
+        }
+        baseMediaPlayer.resume();
+    }
+
+    /*
+     * 快进到指定位置
+     * */
+    @JavascriptInterface
+    public void seek(int position){
+        if(baseMediaPlayer == null){
+            Logger.show(context,"播放器为空,不能拖动");
+            return;
+        }
+        baseMediaPlayer.seek(position);
+    }
+
+  /*  *//*
+     * 设定播放速率
+     * *//*
+    @JavascriptInterface
+    public void setPlaySpeed(String speed){
+
+    }*/
+
+    /*
+     * 资源释放
+     * */
+    @JavascriptInterface
+    public void release(){
+        if(baseMediaPlayer == null){
+            Logger.show(context,"播放器为空,不能释放资源");
+            return;
+        }
+        baseMediaPlayer.release();
+    }
+
+    /*
+    * 退出播放
+    * */
+    @JavascriptInterface
+    public void exit(){
+        if(baseMediaPlayer == null){
+            Logger.show(context,"播放器为空,不能退出");
+            return;
+        }
+        Activity activity = (Activity)context;
+        activity.runOnUiThread(this::destroyVideo);
+    }
+
+    /**-------------------------------------------功能函数-----------------------------------------------*/
+
+
+    /*
+     * 在视频未初始化的情况下,调用该函数初始化
+     * @param x                  播放器x坐标
+     * @param y                  播放器y坐标
+     * @param width              播放器宽度
+     * @param height             播放器高度
+     * */
+    private void initVideoParamsIfNoInit(BaseMediaPlayer baseMediaPlayer,int x, int y, int width, int height){
+        if(baseMediaPlayer == null){
+            Logger.show(context,"播放器为空,不能执行initVideoParamsIfNoInit函数");
+            return;
+        }
+        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+        params.width = width;
+        params.height = height;
+        baseMediaPlayer.setLayoutParams(params);
+
+        baseMediaPlayer.setX(x);
+        baseMediaPlayer.setY(y);
+
+        if (baseMediaPlayer.getParent() == null) {
+            Activity activity = (Activity)context;
+            ViewGroup viewGroup = (ViewGroup)activity.getWindow().getDecorView();
+            viewGroup.addView(baseMediaPlayer, 0);
+        }
+    }
+
+
+    private void destroyVideo(){
+        if(baseMediaPlayer != null && baseMediaPlayer.getParent() != null){
+            release();
+            Activity activity = (Activity)context;
+            ViewGroup viewGroup = (ViewGroup) activity.getWindow().getDecorView();
+            viewGroup.removeView(baseMediaPlayer);
+        }
+    }
+}

+ 138 - 0
app/src/main/java/com/haochuan/hciptvbasic/webview/ToolToJS.java

@@ -0,0 +1,138 @@
+package com.haochuan.hciptvbasic.webview;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.webkit.JavascriptInterface;
+import android.webkit.WebView;
+
+import androidx.annotation.StringDef;
+
+import com.haochuan.hciptvbasic.BuildConfig;
+import com.haochuan.hciptvbasic.Util.Logger;
+import com.haochuan.hciptvbasic.Util.MacUtil;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import static com.haochuan.hciptvbasic.webview.ToolToJS.JsEvent.JS_EVENT_BACK;
+
+
+public class ToolToJS {
+    private Context context;                        //MainActivity 句柄
+    private WebView webView;
+
+    public ToolToJS(Context context, WebView webView){
+        this.context = context;
+        this.webView = webView;
+    }
+
+    /**
+     * JS调用类型
+     */
+    @StringDef({JS_EVENT_BACK})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface JsEvent{
+
+        /**
+         * 将遥控返回按键传递给js
+         */
+        String JS_EVENT_BACK = "javascript:onBackEvent()";
+
+    }
+
+    /*------------------------------------功能性函数-----------------------------------------*/
+    /*---------------------------------------------------------------------------------------*/
+
+    /**
+     * 调用js事件
+     */
+    private void evaluateJavascript(WebView webView, @ToolToJS.JsEvent String script) {
+        Logger.show(context,"ToolToJS 执行脚本:" + script);
+        if (webView == null) {
+            Logger.show(context,"webView对象为空,JS事件调用无法执行");
+            return;
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            webView.evaluateJavascript(script, value -> {
+                //此处为 js 返回的结果
+                Logger.show(context,value);
+            });
+        } else {
+            webView.loadUrl(script);
+        }
+    }
+
+    /*
+     * webView对象获取"返回"按键事件
+     * */
+    public void onBackPressed(){
+        evaluateJavascript(webView, JS_EVENT_BACK);
+    }
+
+    /*---------------------------------获取本地参数--------------------------*/
+    /**
+     * 当前app版本号
+     */
+    @JavascriptInterface
+    public int getVersionCode() {
+        return BuildConfig.VERSION_CODE;
+    }
+
+    /**
+     * 当前app版本名
+     */
+    @JavascriptInterface
+    public String getVersionName() {
+        return BuildConfig.VERSION_NAME;
+    }
+
+
+    /**
+     * 获取Mac地址
+     */
+    @JavascriptInterface
+    public String getMac() {
+        return MacUtil.getMac(context);
+    }
+
+    /*-----------------------------操作APK-------------------------------------*/
+
+    /*
+    * 查看目标包名app是否安装
+    * */
+    @JavascriptInterface
+    public void checkInstall(String pkgName) {
+        //((Activity) context).runOnUiThread(() -> installApk(apkPath));
+    }
+
+    /**
+     * 安装app
+     * @param apkPath 本地安装包路径
+     */
+    @JavascriptInterface
+    public void install(String apkPath) {
+        ((Activity) context).runOnUiThread(() -> installApk(apkPath));
+    }
+
+    private void installApk(String filePath) {
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.setDataAndType(Uri.parse("file://" + filePath), "application/vnd.android.package-archive");
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//4.0以上系统弹出安装成功打开界面
+        context.startActivity(intent);
+    }
+
+    /**
+     * 卸载app
+     */
+    @JavascriptInterface
+    public void uninstall() {
+        ((Activity) context).runOnUiThread(() -> {
+            Uri uri = Uri.fromParts("package", context.getPackageName(), null);
+            Intent intent = new Intent(Intent.ACTION_DELETE, uri);
+            context.startActivity(intent);
+        });
+    }
+}

File diff suppressed because it is too large
+ 34 - 0
app/src/main/res/drawable-v24/ic_launcher_foreground.xml


+ 170 - 0
app/src/main/res/drawable/ic_launcher_background.xml

@@ -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="#008577"
+        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>

+ 18 - 0
app/src/main/res/layout/activity_main.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout 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=".MainActivity">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Hello World!"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+</android.support.constraint.ConstraintLayout>

+ 60 - 0
app/src/main/res/layout/activity_test.xml

@@ -0,0 +1,60 @@
+<?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"
+    android:id="@+id/player_container"
+    tools:context=".test.TestActivity">
+
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_gravity="center"
+        android:gravity="center_horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        >
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:text="播放"
+            android:id="@+id/play_btn"/>
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_marginTop="8dp"
+            android:text="暂停"
+            android:id="@+id/pause_btn"/>
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_marginTop="8dp"
+            android:text="恢复"
+            android:id="@+id/resume_btn"/>
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_marginTop="8dp"
+            android:text="快进5秒"
+            android:id="@+id/forward_btn"/>
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_marginTop="8dp"
+            android:text="快退5秒"
+            android:id="@+id/back_btn"/>
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_marginTop="8dp"
+            android:text="退出"
+            android:id="@+id/exit_btn"/>
+    </LinearLayout>
+
+</FrameLayout>

+ 9 - 0
app/src/main/res/layout/player_gsy_hc.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent" android:layout_height="match_parent">
+    <com.haochuan.hciptvbasic.video.EmptyControlVideoView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/empty_control_video"
+        />
+</FrameLayout>

+ 9 - 0
app/src/main/res/layout/player_hc.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <com.haochuan.hciptvbasic.video.HCGsyVideoPlayer
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/hc_gsy_player"/>
+</FrameLayout>

+ 15 - 0
app/src/main/res/layout/view_video_control_empty.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/black">
+
+    <FrameLayout
+        android:id="@+id/surface_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center">
+
+    </FrameLayout>
+
+</RelativeLayout>

+ 5 - 0
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml

@@ -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 - 0
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml

@@ -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.png


BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png


BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png


+ 6 - 0
app/src/main/res/values/colors.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#008577</color>
+    <color name="colorPrimaryDark">#00574B</color>
+    <color name="colorAccent">#D81B60</color>
+</resources>

+ 3 - 0
app/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">广州浩传基线版本</string>
+</resources>

+ 11 - 0
app/src/main/res/values/styles.xml

@@ -0,0 +1,11 @@
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+        <item name="colorAccent">@color/colorAccent</item>
+    </style>
+
+</resources>

+ 17 - 0
app/src/test/java/com/haochuan/hciptvbasic/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package com.haochuan.hciptvbasic;
+
+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);
+    }
+}

+ 27 - 0
build.gradle

@@ -0,0 +1,27 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+    repositories {
+        google()
+        jcenter()
+        
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:3.4.1'
+        
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        jcenter()
+        
+    }
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}

+ 17 - 0
gradle.properties

@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+android.injected.testOnly=false
+android.useAndroidX=true
+android.enableJetifier=true
+

BIN
gradle/wrapper/gradle-wrapper.jar


+ 6 - 0
gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,6 @@
+#Mon Jul 22 21:20:39 CST 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

+ 172 - 0
gradlew

@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"

+ 84 - 0
gradlew.bat

@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 1 - 0
settings.gradle

@@ -0,0 +1 @@
+include ':app'