whyclj
2019-12-31 d2272f21a9d4491f22b3d3c9d6dbfebcecff76e3
第一次提交
82个文件已添加
2935 ■■■■■ 已修改文件
.gitignore 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/codeStyles/Project.xml 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/encodings.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/gradle.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/misc.xml 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/runConfigurations.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.project 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.settings/org.eclipse.buildship.core.prefs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/.gitignore 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/build.gradle 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/debug/app-online.apk 补丁 | 查看 | 原始文档 | blame | 历史
app/debug/app.apk 补丁 | 查看 | 原始文档 | blame | 历史
app/debug/app1.apk 补丁 | 查看 | 原始文档 | blame | 历史
app/debug/output.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/proguard-rules.pro 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/release/app-release.apk 补丁 | 查看 | 原始文档 | blame | 历史
app/release/output.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/androidTest/java/com/mytestapp/ExampleInstrumentedTest.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/AndroidManifest.xml 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/mytestapp/ActivityCollector.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/mytestapp/BaseActivity.java 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/mytestapp/CrashHandler.java 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/mytestapp/CrashMonitor.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/mytestapp/HeartJavaScriptFunction.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/mytestapp/LaunchSelfReceiver.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/mytestapp/LoginActivity.java 329 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/mytestapp/MainActivity.java 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/mytestapp/MyLoadingDialog.java 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/mytestapp/MyUtil.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/mytestapp/ServerInfo.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/mytestapp/ServerInfoDialog.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/mytestapp/SoftInputUtil.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/mytestapp/SoftKeyboardStateHelper.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/mytestapp/Softkeyboardlistener.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable-v24/ic_launcher_foreground.xml 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/backimg44.jpg 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/bg_edittext.xml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/bg_edittext_focused.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/bg_edittext_normal.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/first_bg.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ic_launcher_background.xml 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ic_loading_bg.9.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ic_loading_white_01.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ic_loading_white_02.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ic_loading_white_03.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ic_loading_white_04.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ic_loading_white_05.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ic_loading_white_06.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ic_loading_white_07.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ic_loading_white_08.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ic_loading_white_09.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ic_loading_white_10.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ic_loading_white_11.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ic_loading_white_12.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/progress_drawable_white.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/activity_login.xml 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/activity_main.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/dialog.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/dialog_layout.xml 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-hdpi/ic_launcher.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-hdpi/ic_launcher_round.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-mdpi/ic_launcher.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-mdpi/ic_launcher_round.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/ic_launcher.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xxhdpi/ic_launcher.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/values/colors.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/values/strings.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/values/styles.xml 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/test/java/com/mytestapp/ExampleUnitTest.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
build.gradle 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
gradle.properties 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
gradle/wrapper/gradle-wrapper.jar 补丁 | 查看 | 原始文档 | blame | 历史
gradle/wrapper/gradle-wrapper.properties 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
gradlew 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
gradlew.bat 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
settings.gradle 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
New file
@@ -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
.idea/codeStyles/Project.xml
New file
@@ -0,0 +1,116 @@
<component name="ProjectCodeStyleConfiguration">
  <code_scheme name="Project" version="173">
    <codeStyleSettings language="XML">
      <indentOptions>
        <option name="CONTINUATION_INDENT_SIZE" value="4" />
      </indentOptions>
      <arrangement>
        <rules>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>xmlns:android</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>xmlns:.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
              <order>BY_NAME</order>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*:id</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*:name</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>name</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>style</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>^$</XML_NAMESPACE>
                </AND>
              </match>
              <order>BY_NAME</order>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
                </AND>
              </match>
              <order>ANDROID_ATTRIBUTE_ORDER</order>
            </rule>
          </section>
          <section>
            <rule>
              <match>
                <AND>
                  <NAME>.*</NAME>
                  <XML_ATTRIBUTE />
                  <XML_NAMESPACE>.*</XML_NAMESPACE>
                </AND>
              </match>
              <order>BY_NAME</order>
            </rule>
          </section>
        </rules>
      </arrangement>
    </codeStyleSettings>
  </code_scheme>
</component>
.idea/encodings.xml
New file
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>
.idea/gradle.xml
New file
@@ -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>
.idea/misc.xml
New file
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" 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>
.idea/runConfigurations.xml
New file
@@ -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>
.project
New file
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
    <name>myTestApp</name>
    <comment>Project myTestApp created by Buildship.</comment>
    <projects>
    </projects>
    <buildSpec>
        <buildCommand>
            <name>org.eclipse.buildship.core.gradleprojectbuilder</name>
            <arguments>
            </arguments>
        </buildCommand>
    </buildSpec>
    <natures>
        <nature>org.eclipse.buildship.core.gradleprojectnature</nature>
    </natures>
</projectDescription>
.settings/org.eclipse.buildship.core.prefs
New file
@@ -0,0 +1,2 @@
connection.project.dir=
eclipse.preferences.version=1
app/.gitignore
New file
@@ -0,0 +1 @@
/build
app/build.gradle
New file
@@ -0,0 +1,33 @@
apply plugin: 'com.android.application'
android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.mytestapp"
        minSdkVersion 14
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        ndk{
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
app/debug/app-online.apk
Binary files differ
app/debug/app.apk
Binary files differ
app/debug/app1.apk
Binary files differ
app/debug/output.json
New file
@@ -0,0 +1 @@
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-debug.apk","fullName":"debug","baseName":"debug"},"path":"app-debug.apk","properties":{}}]
app/proguard-rules.pro
New file
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
app/release/app-release.apk
Binary files differ
app/release/output.json
New file
@@ -0,0 +1 @@
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
app/src/androidTest/java/com/mytestapp/ExampleInstrumentedTest.java
New file
@@ -0,0 +1,27 @@
package com.mytestapp;
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.mytestapp", appContext.getPackageName());
    }
}
app/src/main/AndroidManifest.xml
New file
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mytestapp">
    <!--获取Intenet访问权限-->
    <uses-permission android:name="android.permission.INTERNET" />
    <!--监听开机广播-->
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission>
    <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/MyTheme">
        <activity android:name=".LoginActivity"
            android:windowSoftInputMode="adjustResize|stateHidden"
            android:launchMode="singleInstance"
            android:theme="@style/MyTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity" android:theme="@style/MyTheme">
        </activity>
        <receiver
            android:name=".LaunchSelfReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="1000">
                <action android:name="android.intent.action.BOOT_COMPLETED"></action>
            </intent-filter>
        </receiver>
    </application>
</manifest>
app/src/main/java/com/mytestapp/ActivityCollector.java
New file
@@ -0,0 +1,28 @@
package com.mytestapp;
import android.app.Activity;
import java.util.ArrayList;
import java.util.List;
public class ActivityCollector {
    public static List<Activity> activities = new ArrayList<>();
    public static void addActivity(Activity activity){
        activities.add(activity);
    }
    public static void removeActivity(Activity activity){
        activities.remove(activity);
    }
    public static void finishAll(){
        for (Activity activity:activities){
            if(!activity.isFinishing()){
                activity.finish();
            }
        }
        activities.clear();
    }
}
app/src/main/java/com/mytestapp/BaseActivity.java
New file
@@ -0,0 +1,198 @@
package com.mytestapp;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
/**
 */import androidx.appcompat.app.AppCompatActivity;
/**
 * Author:
 * Date: 2017/8/10
 * Description:   用户实现手机屏保功能
 */
public abstract class BaseActivity extends AppCompatActivity {
    public CountDownTimer countDownTimer;
    private long advertisingTime = 5 * 60 * 1000;//定时跳转广告时间
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //设置屏幕长亮
        /*getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);*/
        setContentView(getLayoutRes());
        ActivityCollector.addActivity(this);
        goneSystemUi();                         //屏蔽底部系统虚拟按键
        getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
            @Override
            public void onSystemUiVisibilityChange(int visibility) {
                Log.e("", "onSystemUiVisibilityChange:OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO " );
                int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                        //布局位于状态栏下方
                        View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
                        //全屏
                        View.SYSTEM_UI_FLAG_FULLSCREEN |
                        //隐藏导航栏
                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
                        View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
                if (Build.VERSION.SDK_INT >= 19) {
                    uiOptions |= 0x00001000;
                } else {
                    uiOptions |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
                }
                getWindow().getDecorView().setSystemUiVisibility(uiOptions);
            }
        });
    }
    protected abstract int getLayoutRes();
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //有按下动作时取消定时
                if (countDownTimer != null){
                    countDownTimer.cancel();
                }
                break;
            case MotionEvent.ACTION_UP:
                //抬起时启动定时
                startAD();
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
    /**
     *
     */
    public void startAD() {
        if (countDownTimer == null) {
            countDownTimer = new CountDownTimer(advertisingTime, 1000l) {
                @Override
                public void onTick(long millisUntilFinished) {
                }
                @Override
                public void onFinish() {
                }
            };
            countDownTimer.start();
        } else {
            countDownTimer.start();
        }
    }
    @Override
    protected void onResume() {
        super.onResume();
        //显示是启动定时
        startAD();
    }
    @Override
    protected void onPause() {
        super.onPause();
        //当activity不在前台是停止定时
        if (countDownTimer != null){
            countDownTimer.cancel();
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //销毁时停止定时
        if (countDownTimer != null){
            countDownTimer.cancel();
        }
        ActivityCollector.removeActivity(this);
    }
    @Override
    public void finish() {
        super.finish();
    }
    public void finishAll(){
        ActivityCollector.finishAll();
    }
    private void fullscreen(boolean enable) {
        if (enable) { //显示状态栏
            WindowManager.LayoutParams lp = getWindow().getAttributes();
            lp.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
            getWindow().setAttributes(lp);
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
        } else { //隐藏状态栏
            WindowManager.LayoutParams lp = getWindow().getAttributes();
            lp.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
            getWindow().setAttributes(lp);
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
        }
    }
    /**
     * 隐藏虚拟按键
     */
    public void goneSystemUi(){
        //隐藏虚拟按键
        if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) {
            View v = getWindow().getDecorView();
            v.setSystemUiVisibility(View.GONE);
        } else if (Build.VERSION.SDK_INT >= 19) {
            View decorView = getWindow().getDecorView();
            int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                    | View.SYSTEM_UI_FLAG_FULLSCREEN;
            decorView.setSystemUiVisibility(uiOptions);
        }
    }
    /**
     * 显示虚拟按键
     */
    public void visibleSystemUi(){
        //显示虚拟键盘
        if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) {
            //低版本sdk
            View v = getWindow().getDecorView();
            v.setSystemUiVisibility(View.VISIBLE);
        } else if (Build.VERSION.SDK_INT >= 19) {
            View decorView = getWindow().getDecorView();
            int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
            decorView.setSystemUiVisibility(uiOptions);
        }
    }
}
app/src/main/java/com/mytestapp/CrashHandler.java
New file
@@ -0,0 +1,207 @@
package com.mytestapp;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class CrashHandler implements Thread.UncaughtExceptionHandler {
    public static final String TAG = "CrashHandler";
    // 系统默认的UncaughtException处理类
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    // CrashHandler实例
    private static CrashHandler INSTANCE = new CrashHandler();
    // 程序的Context对象
    private Context mContext;
    // 用来存储设备信息和异常信息
    private Map<String, String> infos = new HashMap<String, String>();
    // 用于格式化日期,作为日志文件名的一部分
    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss SSS");
    private CrashHandler() {
    }
    /**
     * 获取CrashHandler实例 ,单例模式
     */
    public static CrashHandler getInstance() {
        return INSTANCE;
    }
    /**
     * 初始化
     *
     * @param context
     */
    public void init(Context context) {
        mContext = context;
        // 获取系统默认的UncaughtException处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }
    /**
     * 当UncaughtException发生时会转入该函数来处理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (!handleException(ex) && mDefaultHandler != null) {
            // 如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                Log.e(TAG, "error : ", e);
            }
            Log.e(TAG, "uncaughtException: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" );
            Intent intent = new Intent(mContext, MainActivity.class);
            PendingIntent restartIntent = PendingIntent.getActivity(
                    mContext, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);
            //退出程序
            AlarmManager mgr = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
            mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000,
                    restartIntent); // 1秒钟后重启应用
            System.exit(1);
            ActivityCollector.finishAll();
//            ARouter.getInstance().build("/module_login/SplashActivity").navigation();
//           Intent intent = new Intent(this.mContext, LoginActivity.class);
//           PendingIntent restartIntent = PendingIntent.getActivity(
//                    this.mContext, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);
//            //退出程序
//            AlarmManager mgr = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
//            // 退出程序
//            android.os.Process.killProcess(android.os.Process.myPid());
//            ActivityCollector.finishAll();
            //System.exit(1);
        }
    }
    /**
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
     *
     * @param ex
     * @return true:如果处理了该异常信息;否则返回false.
     */
    private boolean handleException(Throwable ex) {
        if (ex == null) {
            return false;
        }
        // 使用Toast来显示异常信息
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(mContext, "很抱歉,程序出现异常即将退出.", Toast.LENGTH_LONG).show();
                Looper.loop();
            }
        }.start();
        // 收集设备参数信息
        //collectDeviceInfo(mContext);
        // 保存日志文件
        //saveCrashInfo2File(ex);
        return true;
    }
    /**
     * 收集设备参数信息
     *
     * @param ctx
     */
    public void collectDeviceInfo(Context ctx) {
        try {
            PackageManager pm = ctx.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
            if (pi != null) {
                String versionName = pi.versionName == null ? "null" : pi.versionName;
                String versionCode = pi.versionCode + "";
                infos.put("versionName", versionName);
                infos.put("versionCode", versionCode);
            }
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "an error occured when collect package info", e);
        }
        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                infos.put(field.getName(), field.get(null).toString());
                Log.d(TAG, field.getName() + " : " + field.get(null));
            } catch (Exception e) {
                Log.e(TAG, "an error occured when collect crash info", e);
            }
        }
    }
    /**
     * 保存错误信息到文件中
     *
     * @param ex
     * @return 返回文件名称, 便于将文件传送到服务器
     */
    private String saveCrashInfo2File(Throwable ex) {
        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, String> entry : infos.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key + "=" + value + "\n");
        }
        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);
        Throwable cause = ex.getCause();
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();
        String result = writer.toString();
        sb.append(result);
        try {
            String time = formatter.format(new Date());
            String fileName = time + ".txt";
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                String path = "data/data/";
                File dir = new File(path);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(path + fileName);
                fos.write(sb.toString().getBytes("UTF-8"));
                fos.close();
            }
            return fileName;
        } catch (Exception e) {
            Log.e(TAG, "an error occured while writing file...", e);
        }
        return null;
    }
}
app/src/main/java/com/mytestapp/CrashMonitor.java
New file
@@ -0,0 +1,82 @@
package com.mytestapp;
import android.util.Log;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class CrashMonitor {
    private final static  String TAG = "CrashMonitor";
    private ScheduledExecutorService executorService  = null;
    private long oldpos = 0;
    private int pauseNum = 0;
    private OnCrashedListener onCrashedListener;
    private static CrashMonitor crashMonitor;
    private CrashMonitor(){
    }
    public static CrashMonitor createCrashMonitor(){
        if(crashMonitor == null){
            crashMonitor = new CrashMonitor();
        }
        crashMonitor.executorService = null;
        crashMonitor.oldpos = new Date().getTime();
        crashMonitor.pauseNum = 0;
        if(crashMonitor.executorService == null){
            crashMonitor.monitorPageCrashed(1500);
        }
        return crashMonitor;
    }
    public void destory(){
        this.executorService.shutdown();
        this.executorService = null;
        onCrashedListener = null;
    }
    public interface OnCrashedListener {
        void onCrashed();
    }
    public void setOnCrashedListener(OnCrashedListener listener) {
        onCrashedListener = listener;
    }
    public boolean isWorking(){
        boolean res = false;
        if(executorService != null){
            res = true;
        }
        return res;
    }
    public void updateTimer(long pos){
        oldpos = pos;
    }
    public void monitorPageCrashed(final int period){
        executorService  = Executors.newScheduledThreadPool(1);
        executorService.scheduleAtFixedRate(
                new Runnable() {
                    @Override
                    public void run() {
                        Log.e(TAG, "run: "+ (new Date().getTime() - oldpos)+"##############" + pauseNum);
                        if(oldpos > 0 && (new Date().getTime() - oldpos) > 1 * 60 * 1000){
                            pauseNum ++;
                        }
                        if(pauseNum >= 3){
                            oldpos = 0;
                            executorService.shutdown();
                            //do something
                            if(onCrashedListener != null){
                                onCrashedListener.onCrashed();
                            }
                            return;
                        }
                    }
                },0,period, TimeUnit.MILLISECONDS);
    }
}
app/src/main/java/com/mytestapp/HeartJavaScriptFunction.java
New file
@@ -0,0 +1,9 @@
package com.mytestapp;
import android.webkit.JavascriptInterface;
public interface HeartJavaScriptFunction {
    @JavascriptInterface
    void onJsFunctionCalled(int tag);
}
app/src/main/java/com/mytestapp/LaunchSelfReceiver.java
New file
@@ -0,0 +1,24 @@
package com.mytestapp;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
//开机自启动
public class LaunchSelfReceiver extends BroadcastReceiver
{
    public LaunchSelfReceiver()
    {
    }
    @Override
    public void onReceive(Context context, Intent intent)
    {
        if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED"))
        {
            Intent i = new Intent(context, LoginActivity.class);
            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(i);
        }
    }
}
app/src/main/java/com/mytestapp/LoginActivity.java
New file
@@ -0,0 +1,329 @@
package com.mytestapp;
import androidx.appcompat.app.AppCompatActivity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.InputType;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
public class LoginActivity extends BaseActivity implements View.OnClickListener{
    private static final String TAG = "LoginActivity";
    private Button loginBtn;
    private Button testBtn;
    private EditText et_server_ip;
    private EditText et_server_port;
    private boolean isSuccessConn = false;
    private Dialog progressDialog;
    private Softkeyboardlistener softkeyboardlistener;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
        MyUtil.hideNavigationBar(getWindow());
        setContentView(R.layout.activity_login);
        MyUtil.hideNavigationBar(getWindow());
        loginBtn = (Button)findViewById(R.id.btn_loginSys);
        testBtn =  (Button)findViewById(R.id.btn_testConn);
        et_server_ip = (EditText)findViewById(R.id.et_server_ip);
        et_server_port = (EditText)findViewById(R.id.et_server_port);
        loginBtn.setOnClickListener(this);
        testBtn.setOnClickListener(this);
        final ServerInfo info = ServerInfo.readServerInfo(LoginActivity.this);
        if(info != null){
            et_server_ip.setText(info.getServer_ip());
            et_server_port.setText(info.getServer_port()+"");
            // Android 4.0 之后不能在主线程中请求HTTP请求
            new Thread(new Runnable(){
                @Override
                public void run() {
                    boolean flag = testUrlWithTimeOut(info.createServerUrl(),2000);
                    //判断是否已经是设置过了参数
                    if(flag){
                        Intent intent = new Intent(LoginActivity.this,MainActivity.class);
                        startActivity(intent);
                    }
                    //Log.e(TAG, "onCreate:$$$$$$$$$$$$$$$$ "+flag );
                }
            }).start();
        }else{
            et_server_ip.setText("");
            et_server_port.setText("");
        }
        Softkeyboardlistener.setListener(LoginActivity.this, new Softkeyboardlistener.OnSoftKeyBoardChangeListener() {
            @Override
            public void keyBoardShow(int height) {
                //键盘显示
                Log.e(TAG, "keyBoardHide: 键盘显示" );
                //hideSoftInputMethod(et_server_ip);
            }
            @Override
            public void keyBoardHide(int height) {
                Log.e(TAG, "keyBoardHide: 键盘隐藏" );
            }
        });
    }
    @Override
    protected int getLayoutRes() {
        return R.layout.activity_login;
    }
    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg){
            if(msg.what == 84){
                Toast.makeText(LoginActivity.this,"测试连接成功,可以登陆系统",Toast.LENGTH_SHORT).show();
                ServerInfo serverInfo = createServerInfo();
                ServerInfo.WriteServerInfo(LoginActivity.this,serverInfo);
                isSuccessConn = true;
            }else if(msg.what == 85){
                isSuccessConn = false;
                Toast.makeText(LoginActivity.this,"测试连接失败,请检查服务器信息",Toast.LENGTH_SHORT).show();
            }
            MyLoadingDialog.stopLoading();
        }
    };
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_loginSys:{
                if(isSuccessConn){
                    //点击登陆系统
                    Intent intent = new Intent(LoginActivity.this,MainActivity.class);
                    startActivity(intent);
                    //MyUtil.showKeyboard(et_server_ip);
                }else{
                    Toast.makeText(LoginActivity.this,"请先测试连接接成功,方可进入系统!",Toast.LENGTH_SHORT).show();
                }
            }break;
            case R.id.btn_testConn:{
                String server_ip = et_server_ip.getText().toString();
                String server_port = et_server_port.getText().toString();
                if(server_ip.length() == 0){
                    Toast.makeText(LoginActivity.this,"请输入服务器IP地址!",Toast.LENGTH_SHORT).show();
                }else if(server_port.length() == 0){
                    Toast.makeText(LoginActivity.this,"请输入服务器端口号!",Toast.LENGTH_SHORT).show();
                }else{
                    MyLoadingDialog.showLoading(LoginActivity.this,"正在测试连接");
//                    progressDialog = new Dialog(LoginActivity.this,R.style.progress_dialog);
//
//                    progressDialog.setContentView(R.layout.dialog);
//                    progressDialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
//                    TextView msg = (TextView) progressDialog.findViewById(R.id.id_tv_loadingmsg);
//                    progressDialog.setCancelable(false);
//                    msg.setText("正在测试连接.....");
//
//
//                    progressDialog.show();
                    //点击测试连接
                    // Android 4.0 之后不能在主线程中请求HTTP请求
                    new Thread(new Runnable(){
                        @Override
                        public void run() {
                            ServerInfo serverInfo = createServerInfo();
                            boolean flag = testUrlWithTimeOut(serverInfo.createServerUrl(),2000);
                            Message msg = Message.obtain();
                            if(flag){
                                msg.what = 84;
                            }else{
                                msg.what = 85;
                            }
                            handler.sendMessage(msg);
                        }
                    }).start();
                }
            }break;
        }
    }
    public ServerInfo createServerInfo(){
        ServerInfo serverInfo = new ServerInfo();
        serverInfo.setServer_ip(et_server_ip.getText().toString());
        serverInfo.setServer_port(Integer.parseInt(et_server_port.getText().toString()));
        return serverInfo;
    }
    public static boolean testUrlWithTimeOut(String urlString,int timeOutMillSeconds){
        boolean flag = false;
        long lo = System.currentTimeMillis();
        URL url;
        try {
            url = new URL(urlString);
            HttpURLConnection co =  (HttpURLConnection)url.openConnection();
            co.setUseCaches(false);
            co.setConnectTimeout(timeOutMillSeconds);
            int status = co.getResponseCode();
            if(200 == status){
                flag = true;
                Log.e(TAG, "testUrlWithTimeOut: 连接可用"+urlString);
            }
        } catch (Exception e1) {
            Log.e(TAG, "testUrlWithTimeOut:连接打不开!" + e1);
            url = null;
        }
        return flag;
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            View v = getCurrentFocus();
            if (isShouldHideInput(v, ev)) {
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                if (imm != null) {
                    assert v != null;
                    imm.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
                    v.setClickable(true);
                    v.setFocusable(true);
                    v.setFocusableInTouchMode(true);
                    v.requestFocusFromTouch();
                    Log.e(TAG, "dispatchTouchEvent: 111111111111111111" );
                }
            }
            Log.e(TAG, "dispatchTouchEvent: 22222222222222222222222" );
            return super.dispatchTouchEvent(ev);
        }
        Log.e(TAG, "dispatchTouchEvent: 333333333333333333333" );
        // 必不可少,否则所有的组件都不会有TouchEvent了
        return getWindow().superDispatchTouchEvent(ev) || onTouchEvent(ev);
    }
    public boolean isShouldHideInput(View v, MotionEvent event) {
        if (v != null && (v instanceof EditText)) {
            int[] leftTop = {0, 0};
            //获取输入框当前的location位置
            v.getLocationInWindow(leftTop);
            int left = leftTop[0];
            int top = leftTop[1];
            int bottom = top + v.getHeight();
            int right = left + v.getWidth();
            return !(event.getX() > left && event.getX() < right
                    && event.getY() > top && event.getY() < bottom);
        }
        return true;
    }
    //监听系统ui变化
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        //Log.e(TAG, "onWindowFocusChanged: XXXXXXXXXXXXXXXXXXXXXXXXx" );
        super.onWindowFocusChanged(hasFocus);
        int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                //布局位于状态栏下方
                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
                //全屏
                View.SYSTEM_UI_FLAG_FULLSCREEN |
                //隐藏导航栏
                View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
        uiOptions |= 0x00001000;
        getWindow().getDecorView().setSystemUiVisibility(uiOptions);
    }
    public void hideSystemMenu(){
        int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                //布局位于状态栏下方
                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
                //全屏
                View.SYSTEM_UI_FLAG_FULLSCREEN |
                //隐藏导航栏
                View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
        uiOptions |= 0x00001000;
        getWindow().getDecorView().setSystemUiVisibility(uiOptions);
    }
    /**
     * 禁掉系统软键盘
     */
    public void hideSoftInputMethod(EditText mEdit) {
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
        int currentVersion = android.os.Build.VERSION.SDK_INT;
        String methodName = null;
        if (currentVersion >= 16) {
            // 4.2
            methodName = "setShowSoftInputOnFocus";
        } else if (currentVersion >= 14) {
            // 4.0
            methodName = "setSoftInputShownOnFocus";
        }
        if (methodName == null) {
            mEdit.setInputType(InputType.TYPE_NULL);
        } else {
            Class<EditText> cls = EditText.class;
            Method setShowSoftInputOnFocus;
            try {
                setShowSoftInputOnFocus = cls.getMethod(methodName, boolean.class);
                setShowSoftInputOnFocus.setAccessible(true);
                setShowSoftInputOnFocus.invoke(mEdit, false);
            } catch (NoSuchMethodException e) {
                mEdit.setInputType(InputType.TYPE_NULL);
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
    @Override
    protected void onStart() {
        super.onStart();
        Log.e(TAG, "onStart: &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" );
        ServerInfo temp = ServerInfo.readServerInfo(LoginActivity.this);
        if(temp != null){
            et_server_ip.setText(temp.getServer_ip());
            et_server_port.setText(temp.getServer_port()+"");
        }
    }
}
app/src/main/java/com/mytestapp/MainActivity.java
New file
@@ -0,0 +1,207 @@
package com.mytestapp;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.JavascriptInterface;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import android.widget.ZoomButtonsController;
import java.lang.reflect.Field;
import java.util.Date;
public class MainActivity extends BaseActivity {
    private static final String TAG = "MainActivity";
    private WebView webView;
    private CrashMonitor crashMonitor = null;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final ServerInfo serverInfo = ServerInfo.readServerInfo(MainActivity.this);
        if(serverInfo == null){
        }
        webView = (WebView) findViewById(R.id.mywebview);
        webView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setBuiltInZoomControls(true);
        webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);                        //不适用缓存
        webView.clearCache(true);
        webView.setBackgroundColor(android.R.color.transparent);
        webView.setBackgroundResource(R.drawable.first_bg);
        webView.loadUrl(serverInfo.createServerUrl());
        webSettings.setDisplayZoomControls(false);
        setZoomControlGone(webView);
        webView.setWebViewClient(new MyWebViewClient());
        webView.setWebChromeClient(new WebChromeClient(){
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                Toast.makeText(MainActivity.this,message,Toast.LENGTH_SHORT).show();
                return true;
            }
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);
                if(newProgress == 100 ){
                    //加载完成
                    //Toast.makeText(MainActivity.this,"加载完成",Toast.LENGTH_SHORT).show();
                }else{
                    //加载中
                }
            }
        });
        CrashHandler crashHandler = CrashHandler.getInstance();
        crashHandler.init(MainActivity.this);
        crashMonitor = CrashMonitor.createCrashMonitor();
        webView.addJavascriptInterface(new HeartJavaScriptFunction() {
            @JavascriptInterface
            public void onJsFunctionCalled(int tag) {
                if(crashMonitor != null){
                    if(2000 == tag){
                        crashMonitor.updateTimer(new Date().getTime());
                    }
                    //Log.e(TAG, "onJsFunctionCalled                 : "+tag );
                    if(!crashMonitor.isWorking()){
                        //Log.e(TAG, "onJsFunctionCalled: isWorking ................" );
                        crashMonitor.monitorPageCrashed(tag);
                    }
                }
            }
            @JavascriptInterface
            public void saveServerInfo(String server_ip,int server_port){
                ServerInfo serverInfo1 = new ServerInfo();
                serverInfo1.setServer_ip(server_ip);
                serverInfo1.setServer_port(server_port);
                ServerInfo.WriteServerInfo(MainActivity.this,serverInfo1);
            }
            @JavascriptInterface
            public void reStartSystem(){
                //Intent intent = new Intent(MainActivity.this,LoginActivity.class);
                //startActivity(intent);
                // 找到这个 webview
                crashMonitor.destory();
                crashMonitor = null;
                // 加载一个空的url 或者content即可。
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (webView != null) {
                            webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
                            webView.clearHistory();
                            ((ViewGroup) webView.getParent()).removeView(webView);
                            webView.destroy();
                            webView = null;
                        }
                    }
                });
                MainActivity.this.finish();
            }
        }, "Android");
        crashMonitor.setOnCrashedListener(new CrashMonitor.OnCrashedListener() {
            @Override
            public void onCrashed() {
                //Log.e(TAG, "onCrashed: 页面卡死进入登陆页面" );
                Intent intent = new Intent(MainActivity.this,LoginActivity.class);
                startActivity(intent);
            }
        });
    }
    @Override
    protected void onDestroy() {
        if (webView != null) {
            webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
            webView.clearHistory();
            ((ViewGroup) webView.getParent()).removeView(webView);
            webView.destroy();
            webView = null;
        }
        super.onDestroy();
    }
    protected int getLayoutRes() {
        return R.layout.activity_main;
    }
    /**
     * 为了能够响应链接继续在本webview控件中显示,要声明此类。
     * 如果没有这个类,点击了一个链接后,系统会自动选择浏览器浏览
     */
    private class MyWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }
    }
    /**
     * 设置webview的后退,如果后退没有网页了,则关闭该activity
     */
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
            webView.goBack();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
    /**
     * 隐藏放大缩小控件
     */
    public void setZoomControlGone(View view) {
        Class classType;
        Field field;
        try {
            classType = WebView.class;
            field = classType.getDeclaredField("mZoomButtonsController");
            field.setAccessible(true);
            ZoomButtonsController mZoomButtonsController = new ZoomButtonsController(
                    view);
            mZoomButtonsController.getZoomControls().setVisibility(View.GONE);
            try {
                field.set(view, mZoomButtonsController);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}
app/src/main/java/com/mytestapp/MyLoadingDialog.java
New file
@@ -0,0 +1,132 @@
package com.mytestapp;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;
import java.lang.ref.WeakReference;
public class MyLoadingDialog extends Dialog implements DialogInterface.OnCancelListener {
    private WeakReference<Context> mContext = new WeakReference<>(null);
    private volatile static MyLoadingDialog sDialog;
    private MyLoadingDialog(Context context, CharSequence message) {
        super(context, R.style.progress_dialog);
        mContext = new WeakReference<>(context);
        applyCompat();
        @SuppressLint("InflateParams")
        View view = LayoutInflater.from(context).inflate(R.layout.dialog, null);
        TextView tvMessage = (TextView) view.findViewById(R.id.id_tv_loadingmsg);
        if (message != null && message.length() > 0) {
            tvMessage.setText(message);
        }
        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        addContentView(view, lp);
        setOnCancelListener(this);
    }
    @Override
    public void onCancel(DialogInterface dialog) {
        // 点手机返回键等触发Dialog消失,应该取消正在进行的网络请求等
        Context context = mContext.get();
        if (context != null) {
            Toast.makeText(context, "cancel", Toast.LENGTH_SHORT).show();
        }
    }
    public static synchronized void showLoading(Context context) {
        showLoading(context, "loading...");
    }
    public static synchronized void showLoading(Context context, CharSequence message) {
        showLoading(context, message, true);
    }
    public static synchronized void showLoading(Context context, CharSequence message, boolean cancelable) {
        if (sDialog != null && sDialog.isShowing()) {
            sDialog.dismiss();
        }
        if (context == null || !(context instanceof Activity)) {
            return;
        }
        sDialog = new MyLoadingDialog(context, message);
        sDialog.setCancelable(cancelable);
        if (sDialog != null && !sDialog.isShowing() && !((Activity) context).isFinishing()) {
            sDialog.show();
        }
    }
    @Override
    protected void onStart() {
        super.onStart();
        int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_IMMERSIVE
                | View.SYSTEM_UI_FLAG_FULLSCREEN;
        this.getWindow().getDecorView().setSystemUiVisibility(uiOptions);
    }
    public static synchronized void stopLoading() {
        if (sDialog != null && sDialog.isShowing()) {
            sDialog.dismiss();
        }
        sDialog = null;
    }
    private void fullScreenImmersive(View view) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_FULLSCREEN;
            view.setSystemUiVisibility(uiOptions);
        }
    }
    @Override
    public void show() {
        Window window = getWindow();
        MyUtil.focusNotAle(window);
        super.show();
        MyUtil.hideNavigationBar(window);
        MyUtil.clearFocusNotAle(window);
    }
    private void applyCompat() {
        if (Build.VERSION.SDK_INT < 19) {
            return;
        }
        // 隐藏状态栏不占位
        getWindow().setFlags(
                WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
    }
}
app/src/main/java/com/mytestapp/MyUtil.java
New file
@@ -0,0 +1,98 @@
package com.mytestapp;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.os.Build;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
public class MyUtil {
    public static void showDialog(Dialog dialog, Activity activity) {
        if (activity != null) {
            //Set the dialog to not focusable (makes navigation ignore us adding the window)
            dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
            dialog.show();
            //Set the dialog to immersive
            dialog.getWindow().getDecorView().setSystemUiVisibility(activity.getWindow().getDecorView().getSystemUiVisibility());
            //Clear the not focusable flag from the window
            dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
        }
    }
    public static void showKeyboard(View view) {
        InputMethodManager imm = (InputMethodManager) view.getContext()
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null) {
            view.requestFocus();
            imm.showSoftInput(view, 0);
        }
    }
    public static void hideKeyboard(View view){
        InputMethodManager imm = (InputMethodManager) view.getContext()
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null) {
            imm.hideSoftInputFromWindow(view.getWindowToken(),0);
        }
    }
    public static void  toggleSoftInput(View view){
        InputMethodManager imm = (InputMethodManager) view.getContext()
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null) {
            imm.toggleSoftInput(0,0);
        }
    }
    /**
     * 隐藏虚拟栏 ,显示的时候再隐藏掉
     * @param window
     */
    static public void hideNavigationBar(final Window window) {
        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
        window.getDecorView().setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
            @Override
            public void onSystemUiVisibilityChange(int visibility) {
                int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                        //布局位于状态栏下方
                        View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
                        //全屏
                        View.SYSTEM_UI_FLAG_FULLSCREEN |
                        //隐藏导航栏
                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
                        View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
                if (Build.VERSION.SDK_INT >= 19) {
                    uiOptions |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
                } else {
                    uiOptions |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
                }
                window.getDecorView().setSystemUiVisibility(uiOptions);
            }
        });
    }
    /**
     * dialog 需要全屏的时候用,和clearFocusNotAle() 成对出现
     * 在show 前调用  focusNotAle   show后调用clearFocusNotAle
     * @param window
     */
    static public void focusNotAle(Window window) {
        window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
    }
    /**
     * dialog 需要全屏的时候用,focusNotAle() 成对出现
     * 在show 前调用  focusNotAle   show后调用clearFocusNotAle
     * @param window
     */
    static public void clearFocusNotAle(Window window) {
        window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
    }
}
app/src/main/java/com/mytestapp/ServerInfo.java
New file
@@ -0,0 +1,70 @@
package com.mytestapp;
import android.content.Context;
import android.content.SharedPreferences;
public class ServerInfo {
    private String server_ip;                          //服务器ip地址
    private int server_port;                           //服务器端口号
    private String server_name;                         //服务器项目名称
    //获取服务器信息
    public static ServerInfo readServerInfo(Context context){
        ServerInfo serverInfo = null;
        if(context != null){
            serverInfo = new ServerInfo();
            SharedPreferences pref    = context.getSharedPreferences("data",    Context.MODE_PRIVATE);
            serverInfo.server_ip = pref.getString("server_ip","");
            serverInfo.server_port = pref.getInt("server_port",0);
            serverInfo.server_name = pref.getString("server_name","");
        }
        return  serverInfo;
    }
    //保存服务器参数信息
    public static void WriteServerInfo(Context context,ServerInfo serverInfo){
        SharedPreferences.Editor    editor    =    context.getSharedPreferences("data",Context.MODE_PRIVATE).edit();
        editor.putString("server_ip",serverInfo.server_ip);
        editor.putInt("server_port",serverInfo.server_port);
        editor.apply();
    }
    public String createServerUrl(){
        return "http://"+server_ip+":"+server_port+"/Device_Manage/index.html";
        //return "http://"+server_ip+":"+server_port+"/";
    }
    public String getServer_ip() {
        return server_ip;
    }
    public void setServer_ip(String server_ip) {
        this.server_ip = server_ip;
    }
    public int getServer_port() {
        return server_port;
    }
    public void setServer_port(int server_port) {
        this.server_port = server_port;
    }
    public String getServer_name() {
        return server_name;
    }
    public void setServer_name(String server_name) {
        this.server_name = server_name;
    }
    @Override
    public String toString() {
        return "ServerInfo{" +
                "server_ip='" + server_ip + '\'' +
                ", server_port=" + server_port +
                ", server_name='" + server_name + '\'' +
                '}';
    }
}
app/src/main/java/com/mytestapp/ServerInfoDialog.java
New file
@@ -0,0 +1,90 @@
package com.mytestapp;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ServerInfoDialog extends AlertDialog implements View.OnClickListener{
    private static String TAG = "ServerInfoDialog";
    private Context context;
    private Button btn_cancel;
    private Button btn_confirm;
    private ServerInfo serverInfo;
    private EditText tv_server_ip;
    private EditText tv_server_port;
    public ServerInfoDialog(Context context){
        super(context);
        this.context = context;
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.dialog_layout);
        btn_cancel = (Button)findViewById(R.id.btn_cancel);
        btn_confirm = (Button)findViewById(R.id.btn_confirrm);
        this.tv_server_ip = (EditText) findViewById(R.id.et_server_ip);
        this.tv_server_port = (EditText)findViewById(R.id.et_server_port);
        this.serverInfo = ServerInfo.readServerInfo(this.context);
        Log.e(TAG, "onCreate: "+this.tv_server_ip );
       if(this.serverInfo != null){
            tv_server_ip.setText(this.serverInfo.getServer_ip());
            tv_server_port.setText(this.serverInfo.getServer_port()+"");
        }else{
            tv_server_ip.setText("");
            tv_server_port.setText("");
        }
        this.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
        this.setCancelable(false);
        btn_cancel.setOnClickListener(this);
        btn_confirm.setOnClickListener(this);
    }
    @Override
    public void onClick(View view){
        Log.e(TAG, "onClick: "+"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"+view.getId());
        switch (view.getId()){
            case R.id.btn_cancel:{
                this.dismiss();
            }break;
            case R.id.btn_confirrm:{
                String server_ip = tv_server_ip.getText().toString();
                int server_port = Integer.parseInt(tv_server_port.getText().toString());
                Pattern pattern = Pattern.compile("\\b((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\b");
                Matcher matcher = pattern.matcher(server_ip); //验证IP地址有效性
                if(!matcher.matches()){
                    Toast.makeText(this.context, "IP地址格式错误", Toast.LENGTH_SHORT).show();
                }else if(server_port<1){
                    Toast.makeText(this.context, "端口号必须大于0", Toast.LENGTH_SHORT).show();
                }else{
                    this.serverInfo.setServer_ip(server_ip);
                    this.serverInfo.setServer_port(server_port);
                    ServerInfo.WriteServerInfo(this.context,this.serverInfo);
                }
            }break;
        }
    }
}
app/src/main/java/com/mytestapp/SoftInputUtil.java
New file
@@ -0,0 +1,104 @@
package com.mytestapp;
import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
/**
 * 软键盘工具类
 *   ·是否隐藏
 *   ·显示
 *   ·隐藏
 */
public class SoftInputUtil {
    /**
     *  判断键盘是否显示(当前editText是否在输入法活动状态,即是否正在接收软键盘输入)。
     * @param editText 输入框
     */
    public static boolean isSoftInputShowing(EditText editText){
        return isSoftInputShowing(editText.getContext(), editText);
    }
    /**
     * 判断键盘是否显示(当前view是否在输入法活动状态,即是否正在接收软键盘输入)。
     * @param context 上下文
     * @param view 当前聚焦的正在接收软件盘输入的View。
     */
    public static boolean isSoftInputShowing(Context context, View view) {
        boolean bool = false;
        InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null && imm.isActive(view)) {
            bool = true;
        }
        return bool;
    }
    /**
     * 判断键盘是否显示(是否有View是在输入法活动状态,即是否正有View正在接收软键盘输入)。
     * @param context 上下文
     */
    public static boolean isSoftInputShowing(Context context) {
        boolean bool = false;
        InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null && imm.isActive()) {
            bool = true;
        }
        return bool;
    }
    /**
     * 显示键盘
     * @param view 当前聚焦的正在接收软件盘输入的View,通常为EditText。
     */
    public static void showSoftInput(View view) {
        showSoftInput(view.getContext(), view);
    }
    /**
     * 显示键盘
     * @param context 上下文
     * @param view 当前聚焦的正在接收软件盘输入的View,通常为EditText。
     */
    public static void showSoftInput(Context context, View view) {
        InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null) {
            //第二个参数为flags, 0 | InputMethodManager.SHOW_FORCED | InputMethodManager.SHOW_IMPLICIT
            imm.showSoftInput(view, InputMethodManager.SHOW_FORCED);
        }
    }
    /**
     * 隐藏软键盘
     * @param activity
     */
    public static void hideSoftInput(Activity activity) {
        View view = activity.getWindow().peekDecorView();
        if (view != null) {
            hideSoftInput(activity, view);
        }
    }
    public static void hideSoftInput(View view) {
        if (view != null) {
            hideSoftInput(view.getContext(), view);
        }
    }
    /**
     * 隐藏输入法
     * @param context 上下文
     * @param view 可以是任意已添加到window中的View(已添加到布局中的View)。
     */
    public static void hideSoftInput(Context context, View view) {
        InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null && imm.isActive()) {
            //第二个参数为flags, 0 | InputMethodManager.HIDE_IMPLICIT_ONLY | InputMethodManager.HIDE_NOT_ALWAYS
            imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
    }
}
app/src/main/java/com/mytestapp/SoftKeyboardStateHelper.java
New file
@@ -0,0 +1,92 @@
package com.mytestapp;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
import java.util.LinkedList;
import java.util.List;
public class SoftKeyboardStateHelper implements ViewTreeObserver.OnGlobalLayoutListener {
    public interface SoftKeyboardStateListener {
        void onSoftKeyboardOpened(int keyboardHeightInPx);
        void onSoftKeyboardClosed();
    }
    private final List<SoftKeyboardStateListener> listeners = new LinkedList<SoftKeyboardStateListener>();
    private final View activityRootView;
    private int lastSoftKeyboardHeightInPx;
    private boolean isSoftKeyboardOpened;
    public SoftKeyboardStateHelper(View activityRootView) {
        this(activityRootView, false);
    }
    public SoftKeyboardStateHelper(View activityRootView, boolean isSoftKeyboardOpened) {
        this.activityRootView = activityRootView;
        this.isSoftKeyboardOpened = isSoftKeyboardOpened;
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
    }
    @Override
    public void onGlobalLayout() {
        final Rect r = new Rect();
        //r will be populated with the coordinates of your view that area still visible.  
        activityRootView.getWindowVisibleDisplayFrame(r);
        final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
        if (!isSoftKeyboardOpened && heightDiff > 100) {// if more than 100 pixels, its probably a keyboard...  
            isSoftKeyboardOpened = true;
            notifyOnSoftKeyboardOpened(heightDiff);
        } else if (isSoftKeyboardOpened && heightDiff < 100) {
            isSoftKeyboardOpened = false;
            notifyOnSoftKeyboardClosed();
        }
    }
    public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) {
        this.isSoftKeyboardOpened = isSoftKeyboardOpened;
    }
    public boolean isSoftKeyboardOpened() {
        return isSoftKeyboardOpened;
    }
    /**
     *  
     *      * Default value is zero (0) 
     *      * @return last saved keyboard height in px 
     *      
     */
    public int getLastSoftKeyboardHeightInPx() {
        return lastSoftKeyboardHeightInPx;
    }
    public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
        listeners.add(listener);
    }
    public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
        listeners.remove(listener);
    }
    private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) {
        this.lastSoftKeyboardHeightInPx = keyboardHeightInPx;
        for (SoftKeyboardStateListener listener : listeners) {
            if (listener != null) {
                listener.onSoftKeyboardOpened(keyboardHeightInPx);
            }
        }
    }
    private void notifyOnSoftKeyboardClosed() {
        for (SoftKeyboardStateListener listener : listeners) {
            if (listener != null) {
                listener.onSoftKeyboardClosed();
            }
        }
    }
}
app/src/main/java/com/mytestapp/Softkeyboardlistener.java
New file
@@ -0,0 +1,74 @@
package com.mytestapp;
import android.app.Activity;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
public class Softkeyboardlistener {
    private View rootView;//activity的根视图
    int rootViewVisibleHeight;//纪录根视图的显示高度
    private OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener;
    public Softkeyboardlistener(Activity activity) {
        //获取activity的根视图
        rootView = activity.getWindow().getDecorView();
        //监听视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变
        rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                //获取当前根视图在屏幕上显示的大小
                Rect r = new Rect();
                rootView.getWindowVisibleDisplayFrame(r);
                int visibleHeight = r.height();
                System.out.println(""+visibleHeight);
                if (rootViewVisibleHeight == 0) {
                    rootViewVisibleHeight = visibleHeight;
                    return;
                }
                //根视图显示高度没有变化,可以看作软键盘显示/隐藏状态没有改变
                if (rootViewVisibleHeight == visibleHeight) {
                    return;
                }
                //根视图显示高度变小超过200,可以看作软键盘显示了
                if (rootViewVisibleHeight - visibleHeight > 200) {
                    if (onSoftKeyBoardChangeListener != null) {
                        onSoftKeyBoardChangeListener.keyBoardShow(rootViewVisibleHeight - visibleHeight);
                    }
                    rootViewVisibleHeight = visibleHeight;
                    return;
                }
                //根视图显示高度变大超过200,可以看作软键盘隐藏了
                if (visibleHeight - rootViewVisibleHeight > 200) {
                    if (onSoftKeyBoardChangeListener != null) {
                        onSoftKeyBoardChangeListener.keyBoardHide(visibleHeight - rootViewVisibleHeight);
                    }
                    rootViewVisibleHeight = visibleHeight;
                    return;
                }
            }
        });
    }
    private void setOnSoftKeyBoardChangeListener(OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener) {
        this.onSoftKeyBoardChangeListener = onSoftKeyBoardChangeListener;
    }
    public interface OnSoftKeyBoardChangeListener {
        void keyBoardShow(int height);
        void keyBoardHide(int height);
    }
    public static void setListener(Activity activity, OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener) {
        Softkeyboardlistener softKeyBoardListener = new Softkeyboardlistener(activity);
        softKeyBoardListener.setOnSoftKeyBoardChangeListener(onSoftKeyBoardChangeListener);
    }
}
app/src/main/res/drawable-v24/ic_launcher_foreground.xml
New file
@@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="108dp"
    android:height="108dp"
    android:viewportWidth="108"
    android:viewportHeight="108">
    <path
        android:fillType="evenOdd"
        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
        android:strokeWidth="1"
        android:strokeColor="#00000000">
        <aapt:attr name="android:fillColor">
            <gradient
                android:endX="78.5885"
                android:endY="90.9159"
                android:startX="48.7653"
                android:startY="61.0927"
                android:type="linear">
                <item
                    android:color="#44000000"
                    android:offset="0.0" />
                <item
                    android:color="#00000000"
                    android:offset="1.0" />
            </gradient>
        </aapt:attr>
    </path>
    <path
        android:fillColor="#FFFFFF"
        android:fillType="nonZero"
        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
        android:strokeWidth="1"
        android:strokeColor="#00000000" />
</vector>
app/src/main/res/drawable/backimg44.jpg
app/src/main/res/drawable/bg_edittext.xml
New file
@@ -0,0 +1,11 @@
<?xml version ="1.0" encoding ="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_window_focused="false"
        android:drawable="@drawable/bg_edittext_normal"
        />
    <item
        android:state_focused="true"
        android:drawable="@drawable/bg_edittext_focused" />
</selector>
app/src/main/res/drawable/bg_edittext_focused.xml
New file
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@android:color/transparent" />
    <corners android:radius="1dp"/>
    <stroke
        android:width="1dip"
        android:color="#728ea3" />
</shape>
app/src/main/res/drawable/bg_edittext_normal.xml
New file
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@android:color/transparent" />
    <corners android:radius="1dp"/>
    <stroke
        android:width="1dip"
        android:color="#BDC7D8" />
</shape>
app/src/main/res/drawable/first_bg.png
app/src/main/res/drawable/ic_launcher_background.xml
New file
@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="108dp"
    android:height="108dp"
    android:viewportWidth="108"
    android:viewportHeight="108">
    <path
        android:fillColor="#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>
app/src/main/res/drawable/ic_loading_bg.9.png
app/src/main/res/drawable/ic_loading_white_01.png
app/src/main/res/drawable/ic_loading_white_02.png
app/src/main/res/drawable/ic_loading_white_03.png
app/src/main/res/drawable/ic_loading_white_04.png
app/src/main/res/drawable/ic_loading_white_05.png
app/src/main/res/drawable/ic_loading_white_06.png
app/src/main/res/drawable/ic_loading_white_07.png
app/src/main/res/drawable/ic_loading_white_08.png
app/src/main/res/drawable/ic_loading_white_09.png
app/src/main/res/drawable/ic_loading_white_10.png
app/src/main/res/drawable/ic_loading_white_11.png
app/src/main/res/drawable/ic_loading_white_12.png
app/src/main/res/drawable/progress_drawable_white.xml
New file
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<animation-list android:oneshot="false"
                xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:duration="83" android:drawable="@drawable/ic_loading_white_01" />
    <item android:duration="83" android:drawable="@drawable/ic_loading_white_02" />
    <item android:duration="83" android:drawable="@drawable/ic_loading_white_03" />
    <item android:duration="83" android:drawable="@drawable/ic_loading_white_04" />
    <item android:duration="83" android:drawable="@drawable/ic_loading_white_05" />
    <item android:duration="83" android:drawable="@drawable/ic_loading_white_06" />
    <item android:duration="83" android:drawable="@drawable/ic_loading_white_07" />
    <item android:duration="83" android:drawable="@drawable/ic_loading_white_08" />
    <item android:duration="83" android:drawable="@drawable/ic_loading_white_09" />
    <item android:duration="83" android:drawable="@drawable/ic_loading_white_10" />
    <item android:duration="83" android:drawable="@drawable/ic_loading_white_11" />
    <item android:duration="83" android:drawable="@drawable/ic_loading_white_12" />
</animation-list>
app/src/main/res/layout/activity_login.xml
New file
@@ -0,0 +1,139 @@
<?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="#fff">
    <RelativeLayout
        android:id="@+id/layout_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="50dp"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="蓄电池监控管理系统"
            android:textSize="60dp"
            android:textColor="#1A81FF"
            />
    </RelativeLayout>
    <RelativeLayout
        android:id="@+id/layout_content"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:layout_below="@+id/layout_title"
        android:background="#FFFFFF"
        android:layout_marginTop="30dp"
        android:focusable="true"
        android:focusableInTouchMode="true"
        >
        <RelativeLayout
            android:id="@+id/layout_serverip"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="20dp"
            android:orientation="horizontal">
            <TextView
                android:id="@+id/tv_server_ip"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="服务器IP地址:"
                android:textColor="#000"
                android:layout_centerVertical="true"
                android:layout_marginLeft="200dp"
                android:textSize="26dp"
                />
            <EditText
                android:id="@+id/et_server_ip"
                android:layout_toRightOf="@+id/tv_server_ip"
                android:layout_centerVertical="true"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="200dp"
                android:textColor="#000"
                android:textSize="25dp"
                android:background="@drawable/bg_edittext"
                android:paddingLeft="10dp"
                android:lines="1"
                />
        </RelativeLayout>
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:orientation="horizontal"
            android:layout_below="@+id/layout_serverip"
            >
            <TextView
                android:id="@+id/tv_server_port"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="服务器端口号:"
                android:textColor="#000"
                android:layout_centerVertical="true"
                android:layout_marginLeft="200dp"
                android:textSize="26dp"
                />
            <EditText
                android:id="@+id/et_server_port"
                android:layout_toRightOf="@+id/tv_server_port"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:layout_centerVertical="true"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="200dp"
                android:textColor="#000"
                android:textSize="25dp"
                android:background="@drawable/bg_edittext"
                android:lines="1"
                android:hint="请输入服务器端口号"
                android:paddingLeft="10dp"
                android:imeOptions="flagNoExtractUi"
                />
        </RelativeLayout>
    </RelativeLayout>
    <RelativeLayout
        android:id="@+id/layout_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/layout_content"
        android:layout_centerHorizontal="true"
        android:padding="20dp"
        >
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:orientation="horizontal"
            >
            <Button
            android:id="@+id/btn_testConn"
            android:layout_width="150dp"
            android:layout_height="60dp"
            android:text="测试连接"
            android:textSize="20dp"
            android:background="#5491FA"
            android:layout_marginRight="80dp"
            />
            <Button
                android:id="@+id/btn_loginSys"
                android:layout_width="150dp"
                android:layout_height="60dp"
                android:layout_toRightOf="@+id/btn_testConn"
                android:text="进入系统"
                android:textSize="20dp"
                android:background="#5491FA"
                />
        </LinearLayout>
    </RelativeLayout>
</RelativeLayout>
app/src/main/res/layout/activity_main.xml
New file
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <WebView
        android:id="@+id/mywebview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        ></WebView>
</LinearLayout>
app/src/main/res/layout/dialog.xml
New file
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:background="@drawable/ic_loading_bg"
          android:gravity="center"
          android:orientation="vertical"
          android:padding="16dp" >
    <ProgressBar
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:indeterminateDrawable="@drawable/progress_drawable_white" />
    <TextView
            android:id="@+id/id_tv_loadingmsg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginTop="8dp"
            android:textColor="#fff"
            android:textSize="16dp" />
</LinearLayout>
app/src/main/res/layout/dialog_layout.xml
New file
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="550dp"
    android:layout_height="240dp"
    android:orientation="vertical"
    android:background="#5893DF"
    >
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:gravity="center"
        android:text="服务器信息配置"
        android:textSize="18sp" />
    <View
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginTop="10dp"
        android:background="#F5F5F5" />
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        >
        <EditText
            android:id="@+id/et_server_ip"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:layout_marginTop="10dp"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:hint="请输入服务器IP"
            android:inputType="textUri" />
        <EditText
            android:id="@+id/et_server_port"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/et_server_ip"
            android:layout_centerHorizontal="true"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:layout_marginTop="10dp"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:hint="请输入端口号"
            android:inputType="number" />
    </RelativeLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:orientation="horizontal">
        <Button
            android:id="@+id/btn_cancel"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="@null"
            android:text="取消"
            android:textColor="#1965db"
            android:textSize="16sp" />
        <Button
            android:id="@+id/btn_confirrm"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="@null"
            android:text="确定"
            android:textColor="#1965db"
            android:textSize="16sp" />
    </LinearLayout>
</LinearLayout>
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background" />
    <foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background" />
    <foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
app/src/main/res/mipmap-hdpi/ic_launcher.png
app/src/main/res/mipmap-hdpi/ic_launcher_round.png
app/src/main/res/mipmap-mdpi/ic_launcher.png
app/src/main/res/mipmap-mdpi/ic_launcher_round.png
app/src/main/res/mipmap-xhdpi/ic_launcher.png
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
app/src/main/res/values/colors.xml
New file
@@ -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>
app/src/main/res/values/strings.xml
New file
@@ -0,0 +1,3 @@
<resources>
    <string name="app_name">myTestApp</string>
</resources>
app/src/main/res/values/styles.xml
New file
@@ -0,0 +1,27 @@
<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
    <style name="progress_dialog" parent="@android:style/Theme.Dialog">
        <item name="android:windowFrame">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:background">@null</item>
        <item name="android:windowBackground">@null</item>
        <item name="android:backgroundDimEnabled">false</item>
    </style>
    <style name="MyTheme" parent="Theme.AppCompat.NoActionBar">
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowNoTitle">true</item>
    </style>
</resources>
app/src/test/java/com/mytestapp/ExampleUnitTest.java
New file
@@ -0,0 +1,17 @@
package com.mytestapp;
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);
    }
}
build.gradle
New file
@@ -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.5.3'
        // 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
}
gradle.properties
New file
@@ -0,0 +1,20 @@
# 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
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
gradle/wrapper/gradle-wrapper.jar
Binary files differ
gradle/wrapper/gradle-wrapper.properties
New file
@@ -0,0 +1,6 @@
#Tue Dec 31 08:56:59 CST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
gradlew
New file
@@ -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" "$@"
gradlew.bat
New file
@@ -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
settings.gradle
New file
@@ -0,0 +1 @@
include ':app'