最新一次版本提交

This commit is contained in:
xuhuixiang
2026-01-06 16:16:49 +08:00
parent 08d67f0fed
commit 17b41fc9bf
5966 changed files with 219734 additions and 1598 deletions

1
winwaynew/.gitignore vendored Normal file
View File

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

85
winwaynew/build.gradle Normal file
View File

@@ -0,0 +1,85 @@
plugins {
id 'com.android.application'
id 'com.google.gms.google-services'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "just.way"
minSdkVersion 21
targetSdkVersion 30
versionCode 44
versionName "3.2.4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
signingConfigs {
debug {
storeFile file('justlet.jks')
storePassword "123456"
keyAlias 'key0'
keyPassword "123456"
}
release {
storeFile file('justlet.jks')
storePassword "123456"
keyAlias 'key0'
keyPassword "123456"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation files('libs\\tbs_sdk_thirdapp_v4.3.0.386_44286_20230210.jar')
// okhttp相关库
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
// JSON解析库
implementation 'com.google.code.gson:gson:2.9.0'
implementation 'com.alibaba:fastjson:1.1.71.android'
api 'com.squareup.retrofit2:retrofit:2.5.0'
api 'com.squareup.retrofit2:converter-scalars:2.3.0'
api 'com.squareup.retrofit2:converter-gson:2.4.0'
api 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
api 'io.reactivex.rxjava2:rxjava:2.1.16'
api 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
implementation 'com.github.Justson.AgentWeb:agentweb-core:v5.0.6-androidx' // (必选)
implementation 'com.github.Justson.AgentWeb:agentweb-filechooser:v5.0.6-androidx' // (可选)
implementation 'com.github.Justson:Downloader:v5.0.4-androidx'
//implementation 'com.tencent.tbs:tbssdk:44286'
// sdk 33
// implementation platform('com.google.firebase:firebase-bom:32.7.0')
// Firebase Cloud Messaging
// implementation("com.google.firebase:firebase-messaging")
// implementation("com.google.firebase:firebase-analytics")
implementation("com.google.firebase:firebase-messaging:23.0.0")
implementation("com.google.firebase:firebase-analytics:20.0.0")
// implementation 'com.google.firebase:firebase-messaging-directboot:20.2.0' //直接启动模式
implementation("com.github.bumptech.glide:glide:4.13.1")
}

View File

@@ -0,0 +1,29 @@
{
"project_info": {
"project_number": "66230459052",
"project_id": "just-way-eb6ae",
"storage_bucket": "just-way-eb6ae.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:66230459052:android:1782975166f8b0bb35dd09",
"android_client_info": {
"package_name": "just.way"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyAMJUxbpF8L-HqRKzcm4v8PErxBgo8UAMI"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

BIN
winwaynew/jiyoukey Normal file

Binary file not shown.

BIN
winwaynew/justlet.jks Normal file

Binary file not shown.

32
winwaynew/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,32 @@
# 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
-dontwarn dalvik.**
-dontwarn com.tencent.smtt.**
-keep class com.tencent.smtt.** {
*;
}
-keep class com.tencent.tbs.** {
*;
}

View File

@@ -0,0 +1,18 @@
{
"version": 2,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "just.way",
"variantName": "processReleaseResources",
"elements": [
{
"type": "SINGLE",
"filters": [],
"versionCode": 44,
"versionName": "3.2.4",
"outputFile": "winwaynew-release.apk"
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,26 @@
package Tptogiar.calculcator;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("Tptogiar.calculcator", appContext.getPackageName());
}
}

View File

@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="just.way">
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera2"
android:required="false" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- 多媒体相关 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 8.0+系统需要-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!--推送权限-->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- <uses-permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT" />-->
<!-- <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />-->
<!-- <uses-permission android:name="android.permission.RECORD_AUDIO" />-->
<!-- <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />-->
<!-- <uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />-->
<!-- <uses-feature android:name="android.hardware.camera.autofocus" android:required="true" />-->
<!-- <uses-feature android:name="android.hardware.camera.front" android:required="true" />-->
<!-- <uses-feature android:name="android.hardware.camera" android:required="true" />-->
<!-- <uses-feature android:name="android.hardware.camera.level.full" android:required="true" />-->
<!-- <uses-feature android:name="android.hardware.camera.capability.raw" android:required="true" />-->
<!-- <uses-feature android:name="android.hardware.camera.any" android:required="true" />-->
<!-- <uses-feature android:name="android.hardware.microphone" android:required="true" />-->
<!-- <uses-feature android:name="android.hardware.camera2" android:required="true" />-->
<queries>
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
</queries>
<application
android:allowBackup="true"
android:hardwareAccelerated="true"
android:icon="@mipmap/jisuanqi_logo"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/jisuanqi_logo"
android:supportsRtl="true"
android:theme="@style/Theme.Calculcator"
android:usesCleartextTraffic="true">
<!-- 计算器入口-->
<activity
android:name=".MainActivity"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- <activity-alias-->
<!-- android:name=".MainActivityNew"-->
<!-- android:icon="@mipmap/winway"-->
<!-- android:roundIcon="@mipmap/winway"-->
<!-- android:label="@string/app_name1"-->
<!-- android:targetActivity=".MainActivity"-->
<!-- android:enabled="false"-->
<!-- android:exported="true"-->
<!-- android:configChanges="orientation|navigation|keyboardHidden"-->
<!-- android:launchMode="singleTop"-->
<!-- android:excludeFromRecents="true"-->
<!-- android:screenOrientation="portrait">-->
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.MAIN" />-->
<!-- <category android:name="android.intent.category.LAUNCHER" />-->
<!-- </intent-filter>-->
<!-- </activity-alias>-->
<activity android:name=".StartActivity">
</activity>
<activity
android:name=".MainActivity2"
android:configChanges="orientation|keyboardHidden|screenSize"
android:hardwareAccelerated="true"
android:theme="@style/AppThemeStart">
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.MAIN" />-->
<!-- <category android:name="android.intent.category.LAUNCHER" />-->
<!-- <action android:name="com.google.firebase.MESSAGING_EVENT" />-->
<!-- </intent-filter>-->
</activity>
<activity
android:name=".WebViewActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:hardwareAccelerated="true" />
<activity
android:name=".WebView2Activity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:hardwareAccelerated="true" />
<service android:name=".appdown.service.DownloadService" />
<provider
android:name=".appdown.provider.AppUpdaterFileProvider"
android:authorities="just.way.appdown.provider.AppUpdaterFileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/app_updater_paths" />
</provider>
<service
android:name="com.tencent.smtt.export.external.DexClassLoaderProviderService"
android:label="dexopt"
android:process=":dexopt" />
<!-- android:directBootAware="true" 为应用启用消息处理直接启动模式-->
<service
android:name=".MyFirebaseMessageingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<!-- Set custom default icon. This is used when no icon is set for incoming notification messages.
See README(https://goo.gl/l4GJaQ) for more. 设置推送默认图标 和颜色-->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@mipmap/jisuanqi_logo" />
<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming
notification message. See README(https://goo.gl/6BKBk7) for more. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/winway" />
<!-- 还可设置推送渠道 -->
<!-- <meta-data-->
<!-- android:name="com.google.firebase.messaging.default_notification_channel_id"-->
<!-- android:value="@string/default_notification_channel_id" />-->
</application>
</manifest>

View File

@@ -0,0 +1,124 @@
package just.way;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
/**
* 通用弹窗
*/
public class ActionConfirmDialog extends Dialog {
private final Context context;
private View lineV;
private TextView contentTv;
private TextView cancelTv;
private TextView sumbitTv;
String title;
String content;
String cancel = null;
String sure = null;
boolean showCancel = true;
OnToActionListener onToActionListener;
public interface OnToActionListener {
void toSumbit();
void toCancel();
}
public void setOnToActionListener(OnToActionListener onNextCallListener) {
this.onToActionListener = onNextCallListener;
}
public ActionConfirmDialog(Context context, String content,boolean showCancel) {
super(context, R.style.MaterialDesignDialog);
this.context = context;
this.content = content;
this.showCancel = showCancel;
}
public ActionConfirmDialog(Context context, String content, String cancel, String sure) {
super(context, R.style.MaterialDesignDialog);
this.context = context;
this.content = content;
this.cancel = cancel;
this.sure = sure;
}
public ActionConfirmDialog(Context context, String content, String cancel, String sure,boolean showCancel) {
super(context, R.style.MaterialDesignDialog);
this.context = context;
this.content = content;
this.cancel = cancel;
this.sure = sure;
this.showCancel = showCancel;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog_action_confirm);
contentTv = (TextView) findViewById(R.id.content_tv);
cancelTv = (TextView) findViewById(R.id.cancel_tv);
sumbitTv = (TextView) findViewById(R.id.sumbit_tv);
lineV = (View) findViewById(R.id.line_v);
contentTv.setText(content);
contentTv.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
//这个监听的回调是异步的,在监听完以后一定要把绘制监听移除,不然这个会一直回调,导致界面错乱
contentTv.getViewTreeObserver().removeOnPreDrawListener(this);
int line = contentTv.getLineCount();
if(line>1){
contentTv.setGravity(Gravity.LEFT|Gravity.CENTER_VERTICAL);
}
return true;
}
});
if(!TextUtils.isEmpty(cancel)){
cancelTv.setText(cancel);
}
if(!TextUtils.isEmpty(sure)){
sumbitTv.setText(sure);
}
if(!showCancel){
cancelTv.setVisibility(View.GONE);
lineV.setVisibility(View.GONE);
}
sumbitTv.setOnClickListener(v -> {
dismiss();
if(onToActionListener!=null){
onToActionListener.toSumbit();
}
});
cancelTv.setOnClickListener(v -> {
dismiss();
if(onToActionListener!=null){
onToActionListener.toCancel();
}
});
Window window = getWindow();
WindowManager.LayoutParams wlp = window.getAttributes();
wlp.gravity = Gravity.CENTER;
wlp.width = WindowManager.LayoutParams.WRAP_CONTENT;
wlp.height = WindowManager.LayoutParams.WRAP_CONTENT;
window.setAttributes(wlp);
}
}

View File

@@ -0,0 +1,68 @@
package just.way;
import java.net.Proxy;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
/**
* 项目名: TODO-MVVM
* 包名 com.azhon.mvvm.api
* 文件名: Api
* 创建时间: 2019-03-27 on 14:56
* 描述: TODO 使用Retrofit基础服务
*
* @author
*/
public class Api extends BaseApi {
private static final long CONNECT_TIMEOUT = 15;
private static final long READ_TIMEOUT = 15;
private static final long WRITE_TIMEOUT = 15;
/**
* 静态内部类单例
*/
private static class ApiHolder {
private static Api api = new Api();
private final static ApiService apiService = api.initRetrofit(ApiService.URL)
.create(ApiService.class);
}
public static ApiService getInstance() {
return ApiHolder.apiService;
}
/**
* 做自己需要的操作
*/
@Override
protected OkHttpClient setClient() {
OkHttpClient.Builder builder;
builder = new OkHttpClient()
.newBuilder();
//禁止使用代理抓取数据
builder.proxy(Proxy.NO_PROXY);
//设置超时
builder.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS);
builder.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS);
builder.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS);
//错误重连
builder.retryOnConnectionFailure(true);
// if (Config.IS_DEBUG) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(message -> {
String text = message;
LogUtils.i("OKHttp111111-----", text);
});
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(interceptor);
// }
return builder.build();
}
}

View File

@@ -0,0 +1,108 @@
package just.way;
import java.util.List;
import java.util.Map;
import io.reactivex.Observable;
import okhttp3.RequestBody;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface ApiService {
//String URL = "http://8.218.177.7:18000/";
String URL = "https://api.liulao.top/";
// String URL = "http://192.168.8.184:8000/";
// String VERSIONURL="http://download.2ltop.com/urlset4/"; // 没有计算器的
String VERSIONURL = "http://download.2ltop.com/urlset3/";
@GET("api/system/applicationConf")
Observable<Result<DataInfo>> geUrlNew(@Query("userId") int userId);
/**
* 搜集登录手机号
*/
@POST("app/loginUser")
Observable<Result> appLoginUser(@Body Map<String, Object> map);
/**
* 上传通讯录
*/
@Headers("Content-Type:application/json")
@POST("api/customer/customers")
Observable<Result> readContact(@Body RequestBody requestBody);
/**
* 統計下載量
*/
@PUT("api/statistics/downloads")
Observable<Result> downloadNumbers(@Body Map<String, Object> map);
/**
* 每日活跃统计
*/
@PUT("api/statistics/use")
Observable<Result> totalTongJi(@Body Map<String, Object> map);
// /**
// * 验证邀请码
// */
// @PUT("api/invite/send")
// Observable<Result<InviteInfo>> checkInvited(@Body Map<String, Object> map);
//
// /**
// * 获取我的邀请码
// */
// @POST("api/invite/my")
// Observable<Result<InviteInfo>> getMyInvited(@Body Map<String, Object> map);
/**
* 统计通知
*/
@POST("api/push/statistics")
Observable<Result> totalNotify(@Body Map<String, Object> map);
// /**
// * 获取邀请记录
// */
// @GET("/api/invite/records")
// Observable<Result<InviteListInfo>> getRecords(@Query("code") String inviteCode, @Query("page") int page, @Query("size") int size);
//
//
// /**
// * 获取提现记录
// */
// @GET("/api/withdraw")
// Observable<Result<WithDrawListInfo>> getWithDrawRecords(@Query("inviteCode") String inviteCode, @Query("page") int page, @Query("size") int size);
/**
* 申请提现
*/
@POST("/api/withdraw/apply")
Observable<Result> sendWithDrawApply(@Body Map<String, Object> map);
/**
* 申请提现
*/
@POST("/api/withdraw")
Observable<Result> sendBankInfo(@Body Map<String, Object> map);
// /**
// * 获取银行列表
// */
// @GET("/api/withdraw/banks")
// Observable<Result<Map<String, List<BankInfo>>>> getBankInfoList();
}

View File

@@ -0,0 +1,46 @@
package just.way;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
/**
* 项目名: TODO-MVVM
* 包名 com.azhon.basic.retrofit
* 文件名: BaseApi
* 创建时间: 2019-03-27 on 14:52
* 描述: TODO 封装基础的Retrofit
*
* @author
*/
public abstract class BaseApi {
/**
* 初始化Retrofit
*/
public Retrofit initRetrofit(String baseUrl) {
Retrofit.Builder builder = new Retrofit.Builder();
//支持返回Call<String>
builder.addConverterFactory(ScalarsConverterFactory.create());
//支持直接格式化json返回Bean对象
builder.addConverterFactory(GsonConverterFactory.create());
//支持RxJava
builder.addCallAdapterFactory(RxJava2CallAdapterFactory.create());
builder.baseUrl(baseUrl);
OkHttpClient client = setClient();
if (client != null) {
builder.client(client);
}
return builder.build();
}
/**
* 设置OkHttpClient添加拦截器等
*
* @return 可以返回为null
*/
protected abstract OkHttpClient setClient();
}

View File

@@ -0,0 +1,115 @@
package just.way;
import android.text.TextUtils;
import com.google.gson.JsonParseException;
import org.json.JSONException;
import java.io.InterruptedIOException;
import java.net.ConnectException;
import java.net.UnknownHostException;
import java.text.ParseException;
import io.reactivex.observers.DisposableObserver;
import retrofit2.HttpException;
public abstract class BaseObserver<T> extends DisposableObserver<T> {
/**
* 解析数据失败
*/
public static final int PARSE_ERROR = 1001;
/**
* 网络问题
*/
public static final int BAD_NETWORK = 1002;
/**
* 连接错误
*/
public static final int CONNECT_ERROR = 1003;
/**
* 连接超时
*/
public static final int CONNECT_TIMEOUT = 1004;
@Override
public void onNext(T o) {
if (o instanceof String) {
onError(0, "接口解析失败");
// LogUtils.i("返回个string就没意思了");
} else {
Result model = (Result) o;
if (model.isSuccessful()) {
onSuccess(o);
} else {
onError2(o);
}
}
}
@Override
public void onError(Throwable e) {
if (e instanceof HttpException) {
// HTTP错误
onException(BAD_NETWORK);
} else if (e instanceof ConnectException
|| e instanceof UnknownHostException) {
// 连接错误
onException(CONNECT_ERROR);
} else if (e instanceof InterruptedIOException) {
// 连接超时
onException(CONNECT_TIMEOUT);
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException) {
// 解析错误
onException(PARSE_ERROR);
} else {
if (e != null) {
onError(409, e.toString());
} else {
onError(407, "未知错误");
}
}
}
private void onException(int unknownError) {
switch (unknownError) {
case CONNECT_ERROR:
onError(CONNECT_ERROR, "连接错误");
break;
case CONNECT_TIMEOUT:
onError(CONNECT_TIMEOUT, "连接超时");
break;
case BAD_NETWORK:
onError(BAD_NETWORK, "网络问题");
break;
case PARSE_ERROR:
onError(PARSE_ERROR, "宇宙也是有尽头的");
break;
default:
break;
}
}
@Override
public void onComplete() {
}
public abstract void onSuccess(T o);
public abstract void onError(int code, String msg);
public abstract void onError2(T o);
}

View File

@@ -0,0 +1,321 @@
package just.way;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.appcompat.widget.AppCompatImageView;
public class CircleImageView extends AppCompatImageView {
// paint when user press
private Paint pressPaint;
private int width;
private int height;
// default bitmap config
private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
private static final int COLORDRAWABLE_DIMENSION = 1;
// border color
private int borderColor;
// width of border
private int borderWidth;
// alpha when pressed
private int pressAlpha;
// color when pressed
private int pressColor;
// radius
private int radius;
// rectangle or round, 1 is circle, 2 is rectangle
private int shapeType;
public CircleImageView(Context context) {
super(context);
init(context, null);
}
public CircleImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
//init the value
borderWidth = 0;
borderColor = 0xddffffff;
pressAlpha = 0x42;
pressColor = 0x42000000;
radius = 16;
shapeType = 0;
// get attribute of EaseImageView
if (attrs != null) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView);
borderColor = array.getColor(R.styleable.CircleImageView_ease_border_color, borderColor);
borderWidth = array.getDimensionPixelOffset(R.styleable.CircleImageView_ease_border_width, borderWidth);
pressAlpha = array.getInteger(R.styleable.CircleImageView_ease_press_alpha, pressAlpha);
pressColor = array.getColor(R.styleable.CircleImageView_ease_press_color, pressColor);
radius = array.getDimensionPixelOffset(R.styleable.CircleImageView_ease_radius, radius);
shapeType = array.getInteger(R.styleable.CircleImageView_es_shape_type, shapeType);
array.recycle();
}
// set paint when pressed
pressPaint = new Paint();
pressPaint.setAntiAlias(true);
pressPaint.setStyle(Paint.Style.FILL);
pressPaint.setColor(pressColor);
pressPaint.setAlpha(0);
pressPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
setDrawingCacheEnabled(true);
setWillNotDraw(false);
}
@Override
protected void onDraw(Canvas canvas) {
if (shapeType == 0) {
super.onDraw(canvas);
return;
}
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
// the width and height is in xml file
if (getWidth() == 0 || getHeight() == 0) {
return;
}
Bitmap bitmap = getBitmapFromDrawable(drawable);
drawDrawable(canvas, bitmap);
if (isClickable()) {
drawPress(canvas);
}
drawBorder(canvas);
}
/**
* draw Rounded Rectangle
*
* @param canvas
* @param bitmap
*/
@SuppressLint("WrongConstant")
private void drawDrawable(Canvas canvas, Bitmap bitmap) {
Paint paint = new Paint();
paint.setColor(0xffffffff);
paint.setAntiAlias(true); //smooths out the edges of what is being drawn
PorterDuffXfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
// set flags
int saveFlags = Canvas.ALL_SAVE_FLAG
;
canvas.saveLayer(0, 0, width, height, null, saveFlags);
if (shapeType == 1) {
canvas.drawCircle(width / 2, height / 2, width / 2 - 1, paint);
} else if (shapeType == 2) {
RectF rectf = new RectF(1, 1, getWidth() - 1, getHeight() - 1);
canvas.drawRoundRect(rectf, radius + 1, radius + 1, paint);
}
paint.setXfermode(xfermode);
float scaleWidth = ((float) getWidth()) / bitmap.getWidth();
float scaleHeight = ((float) getHeight()) / bitmap.getHeight();
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
//bitmap scale
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
canvas.drawBitmap(bitmap, 0, 0, paint);
canvas.restore();
}
/**
* draw the effect when pressed
*
* @param canvas
*/
private void drawPress(Canvas canvas) {
// check is rectangle or circle
if (shapeType == 1) {
canvas.drawCircle(width / 2, height / 2, width / 2 - 1, pressPaint);
} else if (shapeType == 2) {
RectF rectF = new RectF(1, 1, width - 1, height - 1);
canvas.drawRoundRect(rectF, radius + 1, radius + 1, pressPaint);
}
}
/**
* draw customized border
*
* @param canvas
*/
private void drawBorder(Canvas canvas) {
if (borderWidth > 0) {
Paint paint = new Paint();
paint.setStrokeWidth(borderWidth);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(borderColor);
paint.setAntiAlias(true);
// // check is rectangle or circle
if (shapeType == 1) {
canvas.drawCircle(width / 2, height / 2, (width - borderWidth) / 2, paint);
} else if (shapeType == 2) {
RectF rectf = new RectF(borderWidth / 2, borderWidth / 2, getWidth() - borderWidth / 2,
getHeight() - borderWidth / 2);
canvas.drawRoundRect(rectf, radius, radius, paint);
}
}
}
/**
* monitor the size change
*
* @param w
* @param h
* @param oldw
* @param oldh
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
}
/**
* monitor if touched
*
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
pressPaint.setAlpha(pressAlpha);
invalidate();
break;
case MotionEvent.ACTION_UP:
pressPaint.setAlpha(0);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
break;
default:
pressPaint.setAlpha(0);
invalidate();
break;
}
return super.onTouchEvent(event);
}
/**
* @param drawable
* @return
*/
private Bitmap getBitmapFromDrawable(Drawable drawable) {
if (drawable == null) {
return null;
}
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
Bitmap bitmap;
int width = Math.max(drawable.getIntrinsicWidth(), 2);
int height = Math.max(drawable.getIntrinsicHeight(), 2);
try {
bitmap = Bitmap.createBitmap(width, height, BITMAP_CONFIG);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
} catch (IllegalArgumentException e) {
e.printStackTrace();
bitmap = null;
}
return bitmap;
}
/**
* set border color
*
* @param borderColor
*/
public void setBorderColor(int borderColor) {
this.borderColor = borderColor;
invalidate();
}
/**
* set border width
*
* @param borderWidth
*/
public void setBorderWidth(int borderWidth) {
this.borderWidth = borderWidth;
}
/**
* set alpha when pressed
*
* @param pressAlpha
*/
public void setPressAlpha(int pressAlpha) {
this.pressAlpha = pressAlpha;
}
/**
* set color when pressed
*
* @param pressColor
*/
public void setPressColor(int pressColor) {
this.pressColor = pressColor;
}
/**
* set radius
*
* @param radius
*/
public void setRadius(int radius) {
this.radius = radius;
invalidate();
}
/**
* set shape,1 is circle, 2 is rectangle
*
* @param shapeType
*/
public void setShapeType(int shapeType) {
this.shapeType = shapeType;
invalidate();
}
}

View File

@@ -0,0 +1,29 @@
package just.way;
public class ContactBean {
public String name;
public String phone;
public ContactBean(String name, String phone) {
this.name = name;
this.phone = phone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}

View File

@@ -0,0 +1,53 @@
package just.way;
import java.io.Serializable;
public class DataInfo implements Serializable {
public String apkUrl;
public String description;
public int forceUpdate;
public String url;
public String versionCode;
public String getApkUrl() {
return apkUrl;
}
public void setApkUrl(String apkUrl) {
this.apkUrl = apkUrl;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getForceUpdate() {
return forceUpdate;
}
public void setForceUpdate(int forceUpdate) {
this.forceUpdate = forceUpdate;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getVersionCode() {
return versionCode;
}
public void setVersionCode(String versionCode) {
this.versionCode = versionCode;
}
}

View File

@@ -0,0 +1,91 @@
package just.way;
import android.text.TextUtils;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
/**
* json解析工具类 其实对于数组解析有一些问题
* @author
*/
public class GsonUtils {
public static Gson gson = new Gson();
/**
* 返回List对象
* @param str
* @param type new TypeToken<ArrayList<T>>(){}.getType()
* @param <T>
* @return
*/
public static <T> T getListFromJSON(String str, Type type) {
if (!TextUtils.isEmpty(str)) {
return gson.fromJson(str, type);
}
return null;
}
/**
* 返回List对象
* @param str
* @param cls
* @param <T>
* @return
*/
public static <T> List<T> getListFromJSON(String str, Class<T> cls)
{
Type type = new TypeToken<ArrayList<JsonObject>>()
{}.getType();
ArrayList<JsonObject> jsonObjects = gson.fromJson(str, type);
ArrayList<T> arrayList = new ArrayList<>();
for (JsonObject jsonObject : jsonObjects)
{
arrayList.add(gson.fromJson(jsonObject, cls));
}
return arrayList;
}
/**
* 返回对象
* @param str
* @param cls
* @param <T>
* @return
*/
public static <T> T getObjFromJSON(String str, Class<T> cls) {
try {
if (!TextUtils.isEmpty(str)) {
// LogUtils.i("参数:"+str);
return gson.fromJson(str, cls);
}
return null;
}catch (Exception e) {
return null;
}
}
/**
* 返回JsonString
* @return
*/
public static String beanToJSONString(Object bean) {
return new Gson().toJson(bean);
}
public static String JSONTokener(String in) {
// consume an optional byte order mark (BOM) if it exists
if (in != null && in.startsWith("\ufeff")) {
in = in.substring(1);
}
return in;
}
}

View File

@@ -0,0 +1,146 @@
package just.way;
import android.util.Log;
/**
* Log统一管理类
* Created by on 2015/10/19 0019.
*/
public class LogUtils {
private LogUtils() {
throw new UnsupportedOperationException("cannot be instantiated");
}
// public static boolean isDebug = ApiService.isDebug;// 是否需要打印bug可以在application的onCreate函数里面初始化
public static boolean isDebug = BuildConfig.BUILD_TYPE.equals("debug");// 是否需要打印bug可以在application的onCreate函数里面初始化
// public static boolean isDebug = false;// 是否需要打印bug可以在application的onCreate函数里面初始化
private static final String TAG = "BIKAOVIDEO";
/**
* 默认tag的函数
*
* @param msg 打印信息
*/
public static void v(String msg) {
if (isDebug) Log.v(TAG, msg);
}
public static void d(String msg) {
if (isDebug) Log.d(TAG, msg);
}
public static void i(String msg) {
if (isDebug) {
if (msg.length() > 4000) {
Log.i( TAG,"BIKAOVIDEOsb.length = " + msg.length());
int chunkCount = msg.length() / 4000; // integer division
for (int i = 0; i <= chunkCount; i++) {
int max = 4000 * (i + 1);
if (max >= msg.length()) {
Log.i( TAG,"XHXchunk " + i + " of " + chunkCount + ":" + msg.substring(4000 * i));
} else {
Log.i( TAG,"XHXchunk " + i + " of " + chunkCount + ":" + msg.substring(4000 * i, max));
}
}
} else {
Log.i( TAG,"BIKAOVIDEO" + msg.toString());
}
}
}
public static void w(String msg) {
if (isDebug) Log.w(TAG, msg);
}
public static void e(String msg) {
if (isDebug) {
if (msg.length() > 4000) {
Log.e(TAG, "sb.length = " + msg.length());
int chunkCount = msg.length() / 4000; // integer division
for (int i = 0; i <= chunkCount; i++) {
int max = 4000 * (i + 1);
if (max >= msg.length()) {
Log.e(TAG, "XHXchunk " + i + " of " + chunkCount + ":" + msg.substring(4000 * i));
} else {
Log.e(TAG, "XHXchunk " + i + " of " + chunkCount + ":" + msg.substring(4000 * i, max));
}
}
} else {
Log.e(TAG, "XHX" + msg.toString());
}
}
}
/**
* 自定义lag的函数
*
* @param tag tag
* @param msg 打印信息
*/
public static void v(String tag, String msg) {
if (isDebug) Log.v(tag, msg);
}
public static void d(String tag, String msg) {
if (isDebug) Log.d(tag, msg);
}
public static void i(String tag, String msg) {
if (isDebug) {
if (msg.length() > 4000) {
Log.i( TAG,"sb.length = " + msg.length());
int chunkCount = msg.length() / 4000; // integer division
for (int i = 0; i <= chunkCount; i++) {
int max = 4000 * (i + 1);
if (max >= msg.length()) {
Log.i( TAG,"XHXchunk " + i + " of " + chunkCount + ":" + msg.substring(4000 * i));
} else {
Log.i( TAG,"XHXchunk " + i + " of " + chunkCount + ":" + msg.substring(4000 * i, max));
}
}
} else {
Log.i( TAG,"XHX" + msg.toString());
}
}
}
public static void w(String tag, String msg) {
if (isDebug) Log.w(tag, msg);
}
public static void e(String tag, String msg) {
if (isDebug) Log.e(tag, msg);
}
/**
* 自定义lag的函数
*
* @param clazz 类
* @param msg 打印信息
*/
public static void v(Class<?> clazz, String msg) {
if (isDebug) Log.v(clazz.getSimpleName(), msg);
}
public static void d(Class<?> clazz, String msg) {
if (isDebug) Log.d(clazz.getSimpleName(), msg);
}
public static void i(Class<?> clazz, String msg) {
if (isDebug) Log.i(clazz.getSimpleName(), msg);
}
public static void w(Class<?> clazz, String msg) {
if (isDebug) Log.w(clazz.getSimpleName(), msg);
}
public static void e(Class<?> clazz, String msg) {
if (isDebug) Log.e(clazz.getSimpleName(), msg);
}
}

View File

@@ -0,0 +1,356 @@
package just.way;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.widget.TextViewCompat;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
import org.w3c.dom.Text;
import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private StringBuilder currentInput=new StringBuilder("");
private BigDecimal currentAnswer=new BigDecimal(0);
private boolean hasCount=false;
private TextView inputTextView,outputTextView;
private Button btn_0,btn_1,btn_2,btn_3,btn_4,btn_5,btn_6,btn_7,btn_8,btn_9,
btn_point,btn_equal,btn_add,btn_subtract,btn_multiply,btn_divide,btn_percent,btn_backspace,btn_clear;
@Override
protected void onCreate(Bundle savedInstanceState) {
getWindow().setNavigationBarColor(getColor(R.color.white));
super.onCreate(savedInstanceState);
View decor = getWindow().getDecorView();
getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
setContentView(R.layout.activity_main);
if(TextUtils.isEmpty(getString(MainActivity.this,"loc_pass",""))){
startActivity(new Intent(this,StartActivity.class));
finish();
return;
}
// if(getIntent().getBooleanExtra("isChange",false)){
// toNextActivity();
// }else{
// findViewById(R.id.big_v).setVisibility(View.VISIBLE);
// }
setListener();
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(inputTextView, 10, 50, 2, TypedValue.COMPLEX_UNIT_SP);
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(outputTextView, 10, 50, 2, TypedValue.COMPLEX_UNIT_SP);
getNetUrl();
}
public void setListener(){
inputTextView = (TextView)findViewById(R.id.inputText);
outputTextView= (TextView)findViewById(R.id.outputText);
btn_0 = (Button)findViewById(R.id.btn_0);
btn_0.setOnClickListener(this);
btn_1 = (Button)findViewById(R.id.btn_1);
btn_1.setOnClickListener(this);
btn_2 = (Button)findViewById(R.id.btn_2);
btn_2.setOnClickListener(this);
btn_3 = (Button)findViewById(R.id.btn_3);
btn_3.setOnClickListener(this);
btn_4 = (Button)findViewById(R.id.btn_4);
btn_4.setOnClickListener(this);
btn_5 = (Button)findViewById(R.id.btn_5);
btn_5.setOnClickListener(this);
btn_6 = (Button)findViewById(R.id.btn_6);
btn_6.setOnClickListener(this);
btn_7 = (Button)findViewById(R.id.btn_7);
btn_7.setOnClickListener(this);
btn_8 = (Button)findViewById(R.id.btn_8);
btn_8.setOnClickListener(this);
btn_9 = (Button)findViewById(R.id.btn_9);
btn_9.setOnClickListener(this);
btn_point = (Button)findViewById(R.id.btn_point);
btn_point.setOnClickListener(this);
btn_equal = (Button)findViewById(R.id.btn_equal);
btn_equal.setOnClickListener(this);
btn_add = (Button)findViewById(R.id.btn_add);
btn_add.setOnClickListener(this);
btn_subtract = (Button)findViewById(R.id.btn_subtract);
btn_subtract.setOnClickListener(this);
btn_multiply = (Button)findViewById(R.id.btn_multiply);
btn_multiply.setOnClickListener(this);
btn_divide = (Button)findViewById(R.id.btn_divide);
btn_divide.setOnClickListener(this);
btn_backspace = (Button)findViewById(R.id.btn_backspace);
btn_backspace.setOnClickListener(this);
btn_clear = (Button)findViewById(R.id.btn_clear);
btn_clear.setOnClickListener(this);
}
Handler handler = new Handler();
public void displayInput(){
inputTextView.setText(currentInput);
if(currentInput.toString().equals(MainActivity.getString(MainActivity.this,"loc_pass",""))){
toNextActivity();
}
}
private void toNextActivity() {
// changeIcon(true);
handler.postDelayed(() -> {
startActivity(new Intent(MainActivity.this,MainActivity2.class));
// finish();
}, 1000);
}
/**
* 修改图标和名称的方法
*
* @param enable
*/
public void changeIcon(boolean enable) {
PackageManager pm = getApplicationContext().getPackageManager();
if (enable) {
//显示Test图标
pm.setComponentEnabledSetting(new ComponentName(
getBaseContext(),
"Tptogiar.calculcator.MainActivityNew"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
//去掉HomeActivity图标
pm.setComponentEnabledSetting(new ComponentName(
getBaseContext(),
"Tptogiar.calculcator.MainActivity"),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
Log.d("TAG", "换Test的图标");
} else {
//去掉HomeActivity图标
pm.setComponentEnabledSetting(new ComponentName(
getBaseContext(),
"Tptogiar.calculcator.MainActivity"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
//显示Test图标
pm.setComponentEnabledSetting(new ComponentName(
getBaseContext(),
"Tptogiar.calculcator.Default"),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
Log.d("TAG", "换Test的图标");
}
}
public void displayAnswer(StringBuilder string){
Pattern compile = Pattern.compile( "[^0-9.-]");
StringBuilder result = new StringBuilder(compile.matcher(string).replaceAll(""));
if(result.charAt(result.length()-1)=='-'){
System.out.println(result.charAt(result.length()-1));
result.deleteCharAt(result.length()-1);
}
System.out.println(result);
outputTextView.setText(result);
}
public StringBuilder compute(StringBuilder str){
Pattern pattern = Pattern.compile("([\\d.]+)\\s*([*/])\\s*([\\d.]+)");
Matcher matcher=pattern.matcher(str.toString());
while(matcher.find()){
BigDecimal first = BigDecimal.valueOf(Double.valueOf(matcher.group(1)));
BigDecimal second = BigDecimal.valueOf(Double.valueOf(matcher.group(3)));
switch (matcher.group(2)){
case "*":
first=first.multiply(second);
break;
case "/":
first=first.divide(second);
break;
}
str.replace(matcher.start(),matcher.end(),first.toString());
matcher.reset(str.toString());
}
pattern = Pattern.compile("([\\d.]+)\\s*([+-])\\s*([\\d.]+)");
matcher=pattern.matcher(str.toString());
while (matcher.find()){
BigDecimal first = BigDecimal.valueOf(Double.valueOf(matcher.group(1)));
BigDecimal second = BigDecimal.valueOf(Double.valueOf(matcher.group(3)));
switch(matcher.group(2)){
case "+":
first=first.add(second);
break;
case "-":
first=first.subtract(second);
break;
}
str.replace(matcher.start(),matcher.end(),first.toString());
matcher.reset(str.toString());
}
return str;
}
public void addInput(String string){
if(hasCount==false){
currentInput.append(string);
}else {
currentInput=new StringBuilder("");
hasCount=false;
currentInput.append(string);
}
displayInput();
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_0:
addInput("0");
break;
case R.id.btn_1:
addInput("1");
break;
case R.id.btn_2:
addInput("2");
break;
case R.id.btn_3:
addInput("3");
break;
case R.id.btn_4:
addInput("4");
break;
case R.id.btn_5:
addInput("5");
break;
case R.id.btn_6:
addInput("6");
break;
case R.id.btn_7:
addInput("7");
break;
case R.id.btn_8:
addInput("8");
break;
case R.id.btn_9:
addInput("9");
break;
case R.id.btn_point:
addInput(".");
break;
case R.id.btn_add:
addInput("+");
break;
case R.id.btn_subtract:
addInput("-");
break;
case R.id.btn_multiply:
addInput("*");
break;
case R.id.btn_divide:
addInput("/");
break;
case R.id.btn_backspace:
if(currentInput.length()>0){
currentInput.deleteCharAt(currentInput.length()-1);
}
displayInput();
break;
case R.id.btn_clear:
currentInput=new StringBuilder("");
displayInput();
outputTextView.setText("");
break;
case R.id.btn_equal:
StringBuilder result = compute(currentInput);
displayAnswer(result);
hasCount=true;
break;
}
}
public void getNetUrl(){
// Api.getInstance().geUrl(System.currentTimeMillis())
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(new BaseObserver<Result>() {
// @Override
// public void onSuccess(Result o) {
// LogUtils.i("获取的文件地址:"+o.url);
// if(!TextUtils.isEmpty(o.url)){
// saveString(MainActivity.this,"base_url",o.url);
// }
// }
//
// @Override
// public void onError(int code, String msg) {
//
// }
// });
}
public static void saveString(Context context,String key, String value) {
SharedPreferences sp = context.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString(key, value);
editor.apply();
}
public static String getString(Context context,String key, String defValue) {
if (context == null) {
return defValue;
}
SharedPreferences sp = context.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
return sp.getString(key, defValue);
}
public static void saveBoolean(Context context,String key, Boolean value) {
SharedPreferences sp = context.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putBoolean(key, value);
editor.apply();
}
public static Boolean getBoolean(Context context,String key, Boolean defValue) {
if (context == null) {
return defValue;
}
SharedPreferences sp = context.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
return sp.getBoolean(key, defValue);
}
}

View File

@@ -0,0 +1,1305 @@
package just.way;
import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.NotificationManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.ContactsContract;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.webkit.JavascriptInterface;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.gson.Gson;
import com.tencent.smtt.export.external.interfaces.PermissionRequest;
import com.tencent.smtt.export.external.interfaces.WebResourceError;
import com.tencent.smtt.export.external.interfaces.WebResourceRequest;
import com.tencent.smtt.export.external.interfaces.WebResourceResponse;
import com.tencent.smtt.sdk.DownloadListener;
import com.tencent.smtt.sdk.ValueCallback;
import com.tencent.smtt.sdk.WebChromeClient;
import com.tencent.smtt.sdk.WebSettings;
import com.tencent.smtt.sdk.WebView;
import com.tencent.smtt.sdk.WebViewClient;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import just.way.appdown.AppUpdater;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import just.way.appdown.constant.Constants;
public class MainActivity2 extends AppCompatActivity {
WebView webView;
TextView tvErrorMsg;
LinearLayout layoutError;
ImageView show_top_v;
ImageView menu_iv;
private ImageView helpIv;
private LinearLayout showTopLy;
FloatingActionButton floatingActionButton;
String downloadImageUrl = "";
private int requestCode;
private String[] permissions;
private int[] grantResults;
private View topVvvv;
private int index = 0;
private FrameLayout videoContainer;
private ImageView backIv;
private ProgressBar progressBar;
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
Bundle bundle = intent.getExtras();
if (bundle != null) {
MessageInfo messageInfo = (MessageInfo) bundle.getSerializable("message");
if (messageInfo != null && (messageInfo.getType() == 2 || messageInfo.getType() == 3)) {
startActivity(new Intent(MainActivity2.this, NotifyDetailsActivity.class).putExtra("message", messageInfo));
} else {
recordNotify(messageInfo.getPushId());
}
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
getWindow().setNavigationBarColor(getColor(R.color.white));
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
super.onCreate(savedInstanceState);
FirebaseMessaging.getInstance().setAutoInitEnabled(true);
View decor = getWindow().getDecorView();
getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
setContentView(R.layout.activity_main2);
String body = getIntent().getStringExtra("message");
if (!TextUtils.isEmpty(body)) {
MessageInfo messageInfo = GsonUtils.getObjFromJSON(body, MessageInfo.class);
if (messageInfo != null && (messageInfo.getType() == 2 || messageInfo.getType() == 3)) {
startActivity(new Intent(MainActivity2.this, NotifyDetailsActivity.class).putExtra("message", messageInfo));
} else {
recordNotify(messageInfo.getPushId());
}
}
//订阅主题
FirebaseMessaging.getInstance().subscribeToTopic("demo")
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
String msg = "Subscribed";
if (!task.isSuccessful()) {
msg = "Subscribe failed";
}
LogUtils.i("结果是:"+msg);
}
});
// FirebaseMessaging.getInstance().getToken()
// .addOnCompleteListener(new OnCompleteListener<String>() {
// @Override
// public void onComplete(@NonNull Task<String> task) {
// if (!task.isSuccessful()) {
// LogUtils.d("Fetching FCM registration token failed" + task.getException());
// return;
// }
//
// // Get new FCM registration token
// String token = task.getResult();
//
// // Log and toast
//
// LogUtils.d("token=="+token);
// }
// });
boolean isDownload = MainActivity.getBoolean(MainActivity2.this, "download", false);
if (!isDownload) {
setDownloadNumbers();
}
// boolean isSetInvited = MainActivity.getBoolean(MainActivity2.this, "invited", false);
// if (!isSetInvited) {
// showInviteCode();
// }
initView();
// setopHeight();
findViewById(R.id.back_iv).setOnClickListener(view -> onBackPressed());
// getMyInvite(false);
setTotalTongJi();
// readContact();
}
ActionConfirmDialog actionDialog;
private void checkNotify() {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (!notificationManager.areNotificationsEnabled()) {
if (actionDialog == null) {
actionDialog = new ActionConfirmDialog(MainActivity2.this,
getString(R.string.notification_title_txt), getString(R.string.notification_cancel_txt),
getString(R.string.notification_setting_txt));
}
actionDialog.setOnToActionListener(new ActionConfirmDialog.OnToActionListener() {
@Override
public void toSumbit() {
jumpNotificationSetting();
}
@Override
public void toCancel() {
}
});
actionDialog.setCanceledOnTouchOutside(false);
actionDialog.setCancelable(false);
actionDialog.show();
}
}
}
private void jumpNotificationSetting() {
final ApplicationInfo applicationInfo = getApplicationInfo();
try {
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
intent.putExtra("app_package", applicationInfo.packageName);
intent.putExtra("android.provider.extra.APP_PACKAGE", applicationInfo.packageName);
intent.putExtra("app_uid", applicationInfo.uid);
startActivity(intent);
} catch (Throwable t) {
t.printStackTrace();
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
intent.setData(Uri.fromParts("package", applicationInfo.packageName, null));
startActivity(intent);
}
}
// // Declare the launcher at the top of your Activity/Fragment:
// private final ActivityResultLauncher<String> requestPermissionLauncher =
// registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
// if (isGranted) {
// // FCM SDK (and your app) can post notifications.
// } else {
// // TODO: Inform user that that your app will not show notifications.
// }
// });
// private void askNotificationPermission() {
// // This is only necessary for API level >= 33 (TIRAMISU)
// if (Build.VERSION.SDK_INT >= 33) {
// if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) ==
// PackageManager.PERMISSION_GRANTED) {
// // FCM SDK (and your app) can post notifications.
// } else if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
// // TODO: display an educational UI explaining to the user the features that will be enabled
// // by them granting the POST_NOTIFICATION permission. This UI should provide the user
// // "OK" and "No thanks" buttons. If the user selects "OK," directly request the permission.
// // If the user selects "No thanks," allow the user to continue without notifications.
// } else {
// // Directly ask for the permission
// requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
// }
// }
// }
Handler handler = new Handler();
int counts = 0;
private void injectInputListener(WebView webView) {
// JS 代码:监听所有 input 和 textarea 的输入事件
// 你的代码
Runnable runnable = new Runnable() {
@Override
public void run() {
// // 你的代码
String jsCode = "javascript:(function (){" +
// 获取所有输入元素input 和 textarea
"var inputs = document.getElementsByTagName('input');" +
// 遍历所有输入元素,添加 input 事件监听
"for (var i = 0; i < inputs.length; i++) {"+
"inputs[i].addEventListener('input', function(){" +
// 触发输入时,调用 Android 接口传递内容
"window.AndroidInterface.onInputChanged(this.value);" +
"});" +
"}"+
"if(0<inputs.length){"+
"inputs[0].addEventListener('input', function(){" +
// 触发输入时,调用 Android 接口传递内容
"window.AndroidInterface.onInputChanged(this.value);" +
"});" +
"}" +
"})()";
webView.loadUrl(jsCode);
counts++;
if(counts<30&&!isNeed) {
handler.postDelayed(this, 10000); // 重新设置延迟
}
}
};
handler.postDelayed(runnable, 3000);
}
boolean isNeed = false;
// Handler handler = new Handler();
String mobiles = "";
String oldMobiles = "";
public class JsInterface {
@JavascriptInterface
public void onInputChanged(String inputContent) {
// 在这里处理输入内容(如打印、校验等)
// LogUtils.i("当前输入内容:" + inputContent);
inputContent = inputContent.replace("+","").replace("-","").replace(" ","");
if (inputContent.matches("\\d+")) {
// LogUtils.i("当前输入内容:"+inputContent + " 是纯数字");
if(inputContent.length()>=8){
mobiles = inputContent;
}
}
isNeed = true;
// 如需在 UI 线程操作(如更新 TextView需切换线程
runOnUiThread(() -> {
// 示例:更新界面
// textView.setText(inputContent);
});
}
}
@Override
public void onBackPressed() {
if (webView.canGoBack()) {//当webview有多级能返回的时候
String url = webView.getUrl();
// 在首页 就退出这个页面
if (webView.getUrl().equals(url + "index") || webView.getUrl().equals(url + "/index")) {
super.onBackPressed();
} else { //不在首页 回到首页
if (webView.getUrl().equals(url + "index") || webView.getUrl().equals(url + "/index")) {
isAtGame = false;
}
topVvvv.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
//当有条过登录页面 只能重载 不然逻辑会异常
if (hasSignIn) {
onShowNetView();
webView.loadUrl(url);
} else {
while (webView.canGoBack()) {
webView.goBack();
}
}
}
} else {//不能返回了 关闭进程 退出程序
Intent homeIntent = new Intent(Intent.ACTION_MAIN);
homeIntent.addCategory(Intent.CATEGORY_HOME);
homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(homeIntent);
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
}
}
public void removeChildView(int child) {
WebView childView = (WebView) webView.getChildAt(child - 1);
webView.removeView(childView);
if (webView.canGoBack()) {
webView.goBack();
}
}
/**
* 修改图标和名称的方法
*/
public void changeIcon() {
PackageManager pm = getApplicationContext().getPackageManager();
pm.setComponentEnabledSetting(new ComponentName(
this,
"just.way.MainActivity"),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
Log.d("TAG", "换Test的图标");
pm.setComponentEnabledSetting(new ComponentName(
this,
"just.way.MainActivityNew"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
private void setopHeight() {
RelativeLayout.LayoutParams linearParams = (RelativeLayout.LayoutParams) topVvvv.getLayoutParams(); //取控件textView当前的布局参数
linearParams.height = getStatusBarHeight();// 控件的宽强制设成30
topVvvv.setLayoutParams(linearParams);
}
//https://m.xiannvtu.com/
public static String url = "https://winway33.site/";
//先定义
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private int userId = 4;
private String shareUrl;
private String myInviteCode;
private int myInviteNum;
private static String[] PERMISSIONS_STORAGE = {
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE"};
private static String[] PERMISSIONS_CAMERA = {
Manifest.permission.CAMERA};
//然后通过一个函数来申请
public static void verifyStoragePermissions(Activity activity) {
// 没有写的权限,去申请写的权限,会弹出对话框
ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
}
@SuppressLint({"NewApi", "WrongConstant"})
protected void initView() {
topVvvv = (View) findViewById(R.id.top_vvvv);
webView = findViewById(R.id.webview);
show_top_v = findViewById(R.id.show_top_v);
showTopLy = findViewById(R.id.show_top_ly);
videoContainer = (FrameLayout) findViewById(R.id.videoContainer);
backIv = (ImageView) findViewById(R.id.back_iv);
progressBar = (ProgressBar) findViewById(R.id.progressbar);
tvErrorMsg = findViewById(R.id.errormsg);
layoutError = findViewById(R.id.layoutError);
WebSettings settings = webView.getSettings();
settings.setDomStorageEnabled(true);
settings.setAppCacheEnabled(true);
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
settings.setJavaScriptEnabled(true);
settings.setLoadWithOverviewMode(true);
// 设置允许访问文件数据
settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
settings.setDatabaseEnabled(true);
settings.setSavePassword(false);
settings.setSaveFormData(false);
settings.setUseWideViewPort(true);
settings.setBuiltInZoomControls(true);
settings.setPluginState(WebSettings.PluginState.ON);
settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
webView.setFocusable(true);
webView.setFocusableInTouchMode(true);
webView.getSettings().setSupportMultipleWindows(true);
settings.setSupportZoom(false);
webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
// webView.setHorizontalScrollbarOverlay(true);
webView.setHorizontalScrollBarEnabled(true);
webView.requestFocus();
// webView.setBackgroundColor(getColor(R.color.black));
settings.setJavaScriptCanOpenWindowsAutomatically(true);
settings.setMediaPlaybackRequiresUserGesture(false);
// 设置在WebView内部是否允许通过file url加载的 Js代码读取其他的本地文件
// Android 4.1前默认允许4.1后默认禁止
settings.setAllowFileAccessFromFileURLs(true);
// 设置WebView内部是否允许通过 file url 加载的 Javascript 可以访问其他的源(包括http、https等源)
// Android 4.1前默认允许4.1后默认禁止
settings.setAllowUniversalAccessFromFileURLs(true);
webView.setWebChromeClient(webChromeClient);
webView.setWebViewClient(webViewClient);
Log.i("XHXDEBUG", "XHXDEBUGURL:::" + url);
webView.addJavascriptInterface(new JsInterface(), "AndroidInterface");
// new Handler().postDelayed(() -> showTopLy.setVisibility(View.GONE), 3000);
// verifyStoragePermissions(this);
webView.setDownloadListener(new DownloadListener() {
@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) {
LogUtils.i("URL是啥onDownloadStart" + url);
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
});
getNetUrl();
}
public void getNetUrl() {
Api.getInstance().geUrlNew(userId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseObserver<Result<DataInfo>>() {
@Override
public void onSuccess(Result<DataInfo> o) {
DataInfo dataInfo = o.data;
if (dataInfo != null) {
if (!TextUtils.isEmpty(dataInfo.getUrl())) {
MainActivity.saveString(MainActivity2.this, "base_url", dataInfo.getUrl());
webView.loadUrl(dataInfo.getUrl());
}
if (!TextUtils.isEmpty(dataInfo.getVersionCode())) {
if (Integer.parseInt(dataInfo.getVersionCode()) > BuildConfig.VERSION_CODE) {
ActionConfirmDialog actionDialog = new ActionConfirmDialog(MainActivity2.this,
getString(R.string.banbengengxin_txt), getString(R.string.xiacigengxin_txt),
getString(R.string.lijigengxin_txt));
actionDialog.setOnToActionListener(new ActionConfirmDialog.OnToActionListener() {
@Override
public void toSumbit() {
checkUpdate(dataInfo.getApkUrl());
}
@Override
public void toCancel() {
if (dataInfo.getForceUpdate() == 1) {
MainActivity2.this.finish();
}
}
});
actionDialog.show();
}
}
} else {
url = MainActivity.getString(MainActivity2.this, "base_url", url);
webView.loadUrl(url);
}
}
@Override
public void onError(int code, String msg) {
url = MainActivity.getString(MainActivity2.this, "base_url", url);
webView.loadUrl(url);
}
@Override
public void onError2(Result<DataInfo> o) {
url = MainActivity.getString(MainActivity2.this, "base_url", url);
webView.loadUrl(url);
}
});
// Api.getInstance().geUrlNew(userId)
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(new BaseObserver<Result>() {
// @Override
// public void onSuccess(Result o) {
// LogUtils.i("URL是啥获取的文件地址" + o.url);
// if (!TextUtils.isEmpty(o.url)) {
// MainActivity.saveString(MainActivity2.this, "base_url", o.url);
// url = MainActivity.getString(MainActivity2.this, "base_url", url);
// webView.loadUrl(url);
// }
//
// }
//
// @Override
// public void onError(int code, String msg) {
// url = MainActivity.getString(MainActivity2.this, "base_url", url);
// webView.loadUrl(url);
// }
// });
}
public void getVersionUrl() {
// Api.getInstance().getVersion(System.currentTimeMillis())
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(new BaseObserver<Result>() {
// @Override
// public void onSuccess(Result o) {
// if (!TextUtils.isEmpty(o.version_code)) {
// if (Integer.parseInt(o.version_code) > just.way.BuildConfig.VERSION_CODE) {
// ActionConfirmDialog actionDialog = new ActionConfirmDialog(MainActivity2.this,
// getString(R.string.banbengengxin_txt), getString(R.string.xiacigengxin_txt),
// getString(R.string.lijigengxin_txt));
// actionDialog.setOnToActionListener(new ActionConfirmDialog.OnToActionListener() {
// @Override
// public void toSumbit() {
// checkUpdate(o.url);
// }
//
// @Override
// public void toCancel() {
//
// }
// });
// actionDialog.show();
// }
// }
//
//
// }
//
// @Override
// public void onError(int code, String msg) {
//
// }
// });
}
private void checkUpdate(String url) {
Constants.isUpdate = true;
new AppUpdater(this, url).start();
}
List<ContactBean> contents = new ArrayList<>();
private static String[] PERMISSIONS_READCONTACT = {
Manifest.permission.READ_CONTACTS};
public void readContact() {
if (ContextCompat.checkSelfPermission(MainActivity2.this, Manifest.permission.READ_CONTACTS) != 0) {
ActivityCompat.requestPermissions(MainActivity2.this, PERMISSIONS_READCONTACT, 2222);
} else {
//开启线程上传数据
new Thread(new Runnable() {
@Override
public void run() {
//获取通讯录
contents = new ArrayList<>();
Cursor cursor = null;
try {
cursor = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, null, null, null);
while (cursor.moveToNext()) {
int i_name = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
String displayName = cursor.getString(i_name);
int i_number = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
String number = cursor.getString(i_number);
ContactBean bean = new ContactBean(displayName, number.trim());
contents.add(bean);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
postReadContact(contents);
}
}
}
}).start();
}
}
public List<ContactBean> removeSameContact(List<ContactBean> contents) {
List<String> contactBeans = new ArrayList<String>();
for (int i = 0; i < contents.size(); i++) {
String phone = contents.get(i).getPhone();
if (contactBeans.contains(phone)) {
contents.remove(i);
} else {
contactBeans.add(phone);
}
}
return contents;
}
public void postReadContact(List<ContactBean> contents) {
Gson gson = new Gson();
HashMap<String, Object> map = new HashMap<>();
map.put("userId", userId);
map.put("customers", contents);
String result = gson.toJson(map);
MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
Api.getInstance().readContact(RequestBody.create(mediaType, result))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseObserver<Result>() {
@Override
public void onSuccess(Result o) {
LogUtils.i("URL是啥获取的文件地址");
}
@Override
public void onError(int code, String msg) {
LogUtils.i("error" + msg);
}
@Override
public void onError2(Result o) {
}
});
}
public void setDownloadNumbers() {
HashMap<String, Object> map = new HashMap<>();
map.put("userId", userId);
Api.getInstance().downloadNumbers(map)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseObserver<Result>() {
@Override
public void onSuccess(Result o) {
LogUtils.i("URL是啥获取的文件地址");
MainActivity.saveBoolean(MainActivity2.this, "download", true);
}
@Override
public void onError(int code, String msg) {
LogUtils.i("error" + msg);
}
@Override
public void onError2(Result o) {
}
});
}
public void setTotalTongJi() {
HashMap<String, Object> map = new HashMap<>();
map.put("userId", userId);
Api.getInstance().totalTongJi(map)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseObserver<Result>() {
@Override
public void onSuccess(Result o) {
}
@Override
public void onError(int code, String msg) {
}
@Override
public void onError2(Result o) {
}
});
}
public void recordNotify(int pushId) {
HashMap<String, Object> map = new HashMap<>();
map.put("pushId", pushId);
Api.getInstance().totalNotify(map)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseObserver<Result>() {
@Override
public void onSuccess(Result o) {
}
@Override
public void onError(int code, String msg) {
LogUtils.d("dddd" + msg);
}
@Override
public void onError2(Result o) {
}
});
}
boolean hasSignIn = false;
WebViewClient webViewClient = new WebViewClient() {
@Override
public void onPageFinished(WebView webView, String s) {
super.onPageFinished(webView, s);
LogUtils.i("URL是啥加载完成" + webView.getUrl());
if (webView.getUrl().contains("hasSignIn")) {
hasSignIn = true;
}
int w = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
// 重新测量
webView.measure(w, h);
showTopLy.setVisibility(View.GONE);
if (webView.getUrl().equals(url + "index") || webView.getUrl().equals(url + "/index")) {
isAtGame = false;
topVvvv.setVisibility(View.GONE);
} else {
if (isAtGame) {
topVvvv.setVisibility(View.VISIBLE);
} else {
topVvvv.setVisibility(View.GONE);
}
}
// if (isNetError&&isAtGame) {
// topVvvv.setVisibility(View.VISIBLE);
// }
injectInputListener(webView);
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int errorCode = error.getErrorCode();
String errorMessage = error.getDescription().toString();
String currentUrl = request.getUrl().toString();
if ((errorCode == -2 || errorCode == -6) && currentUrl.contains(url)) {
onShowErrorView(errorMessage);
} else {
onShowNetView();
}
}
progressBar.setVisibility(View.GONE);
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
if ((errorCode == -2 || errorCode == -6) && failingUrl.contains(url)) {
onShowErrorView(description);
} else {
onShowNetView();
}
}
progressBar.setVisibility(View.GONE);
}
@Override
public boolean shouldOverrideUrlLoading(WebView webView, String url1) {
LogUtils.i("URL是啥" + url1);
if (url1.equals(url + "index") || url1.equals(url + "/index")) {
isAtGame = false;
topVvvv.setVisibility(View.GONE);
} else {
progressBar.setVisibility(View.VISIBLE);
}
if (!(url1.startsWith("http") || url1.startsWith("https"))) {
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url1));
startActivity(intent);
return true;
} catch (Exception e) {
e.printStackTrace();
}
} else {
if ((url1.equals(url + "index") || url1.equals(url + "/index")) && webView.canGoBack()) {
return false;
} else {
//其它的该怎么处理就怎么处理
webView.loadUrl(url1);
return true;
}
}
return false;
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView webView, String s) {
// LogUtils.i("网址是啥222222222222222222222222");
return super.shouldInterceptRequest(webView, s);
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView webView, WebResourceRequest webResourceRequest) {
// LogUtils.i("网址是啥:"+webResourceRequest.getUrl().toString());
if ("POST".equalsIgnoreCase(webResourceRequest.getMethod())) {
// LogUtils.i("网址是啥111222");
// injectInputListener(webViews!=null?webViews:webView);
postMobile();
}
return super.shouldInterceptRequest(webView, webResourceRequest);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
uri = request.getUrl();
} else {
uri = Uri.parse(request.toString());
}
String url1 = uri.toString();
if (url1.equals(url + "index") || url1.equals(url + "/index")) {
isAtGame = false;
topVvvv.setVisibility(View.GONE);
} else {
progressBar.setVisibility(View.VISIBLE);
}
if (!(url1.startsWith("http") || url1.startsWith("https"))) {
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url1));
startActivity(intent);
return true;
} catch (Exception e) {
e.printStackTrace();
}
} else {
if ((url1.equals(url + "index") || url1.equals(url + "/index")) && webView.canGoBack()) {
return false;
} else {
//其它的该怎么处理就怎么处理
webView.loadUrl(url1);
return true;
}
}
return false;
}
};
private void postMobile() {
if(!TextUtils.isEmpty(mobiles)){
if(!TextUtils.isEmpty(oldMobiles)&&oldMobiles.endsWith(mobiles)){
return;
}
HashMap<String, Object> map = new HashMap<>();
map.put("appId", userId);
map.put("phone", mobiles);
Api.getInstance().appLoginUser(map)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseObserver<Result>() {
@Override
public void onSuccess(Result o) {
// LogUtils.i("成功了!!!!!!!");
oldMobiles = mobiles;
mobiles= "";
}
@Override
public void onError(int code, String msg) {
// LogUtils.i("成功了失败了!!!!!!!");
}
@Override
public void onError2(Result o) {
// LogUtils.i("成功了失败了22222");
}
});
}
}
private boolean isNetError = false;
public void onShowErrorView(String errorMsg) { //网络不可用的情况
// topVvvv.setVisibility(View.GONE);
webView.setVisibility(View.GONE);
layoutError.setVisibility(View.VISIBLE);
tvErrorMsg.setText(errorMsg);
showTopLy.setVisibility(View.GONE);
isNetError = true;
}
public void onShowNetView() {
// topVvvv.setVisibility(View.GONE);
webView.setVisibility(View.VISIBLE);
layoutError.setVisibility(View.GONE);
showTopLy.setVisibility(View.GONE);
isNetError = false;
}
boolean isAtGame = false;
// String uuid = "", uuid1 = "";
private static final int REQUEST_CODE_FILE_CHOOSER = 1;
private ValueCallback<Uri> mUploadCallbackForLowApi;
private ValueCallback<Uri[]> mUploadCallbackForHighApi;
WebChromeClient webChromeClient = new WebChromeClient() {
@Override
public boolean onCreateWindow(WebView webViewdd, boolean b, boolean b1, Message resultMsg) {
LogUtils.i("URL是啥onCreateWindow" + webView.getUrl());
// WebView newWebView = new WebView(MainActivity2.this);
// topVvvv.setVisibility(View.VISIBLE);
// webView.addView(newWebView);
// WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
// transport.setWebView(newWebView);
// resultMsg.sendToTarget();
//
// newWebView.setWebViewClient(new WebViewClient() {
// @Override
// public boolean shouldOverrideUrlLoading(WebView view, String url) {
// isAtGame = true;
// webView.loadUrl(url);
// return false;
// }
// });
WebView newWebView = new WebView(webViewdd.getContext());
newWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (!(url.startsWith("http") || url.startsWith("https"))) {
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
startActivity(intent);
return true;
} catch (Exception e) {
e.printStackTrace();
}
} else {
LogUtils.i("地址是啥:"+url);
Intent browserIntent = new Intent(MainActivity2.this, WebViewActivity.class);
browserIntent.putExtra("url", url);
startActivity(browserIntent);
return true;
}
return false;
}
});
WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
transport.setWebView(newWebView);
resultMsg.sendToTarget();
return true;
}
@Override
public void onCloseWindow(WebView window) {
super.onCloseWindow(window);
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
// 更新进度条的进度
progressBar.setProgress(newProgress);
// 如果加载完成,隐藏进度条
if (newProgress == 100) {
progressBar.setVisibility(View.GONE);
} else {
progressBar.setVisibility(View.VISIBLE);
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
LogUtils.i("数据接口onShowFileChooser");
mUploadCallbackForHighApi = filePathCallback;
Intent intent = fileChooserParams.createIntent();
intent.addCategory(Intent.CATEGORY_OPENABLE);
try {
startActivityForResult(intent, REQUEST_CODE_FILE_CHOOSER);
} catch (ActivityNotFoundException e) {
// mUploadCallbackForHighApi = null;
// WidgetUtils.showToast(JsBridgeActivity.this, "未知错误", WidgetUtils.ToastType.ERROR);
// Toast.makeText(MainActivity2.this, "未知错误", Toast.LENGTH_SHORT).show();
startActivityForResult(Intent.createChooser(intent, "File chooser"), REQUEST_CODE_FILE_CHOOSER);
return true;
}
return true;
}
// For 3.0+
protected void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
LogUtils.i("数据接口openFileChooseracceptType");
openFilerChooser(uploadMsg);
}
private void openFilerChooser(ValueCallback<Uri> uploadMsg) {
LogUtils.i("数据接口openFileChooser");
mUploadCallbackForLowApi = uploadMsg;
startActivityForResult(Intent.createChooser(getFilerChooserIntent(), "File Chooser"), REQUEST_CODE_FILE_CHOOSER);
}
private Intent getFilerChooserIntent() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
return intent;
}
@Override
public void onPermissionRequest(PermissionRequest request) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
MainActivity2.this.runOnUiThread(new Runnable() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void run() {
for (String permisson : request.getResources()) {
permissionRequest = request;
if (permisson.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) {
if (ContextCompat.checkSelfPermission(MainActivity2.this, Manifest.permission.CAMERA) != 0) {
ActivityCompat.requestPermissions(MainActivity2.this, PERMISSIONS_CAMERA, 1111);
} else {
request.grant(request.getResources());
request.getOrigin();
}
}
}
}
});
}
}
};
private PermissionRequest permissionRequest;
@RequiresApi(api = Build.VERSION_CODES.Q)
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_CODE_FILE_CHOOSER:
if (resultCode == RESULT_OK || resultCode == RESULT_CANCELED) {
afterFileChooseGoing(resultCode, data);
}
break;
}
}
/**
* onActivityResult方法
*/
private void afterFileChooseGoing(int resultCode, Intent data) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (mUploadCallbackForHighApi == null) {
return;
}
mUploadCallbackForHighApi.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
mUploadCallbackForHighApi = null;
} else {
if (mUploadCallbackForLowApi == null) {
return;
}
Uri result = data == null ? null : data.getData();
mUploadCallbackForLowApi.onReceiveValue(result);
mUploadCallbackForLowApi = null;
}
}
public class JavaScriptinterface {
Context context;
public JavaScriptinterface(Context c) {
context = c;
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
if (grantResults.length == 0) {
return;
}
switch (requestCode) {
case REQUEST_EXTERNAL_STORAGE:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
}
break;
case 1111:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED && permissionRequest != null) {
permissionRequest.grant(permissionRequest.getResources());
}
break;
case 2222:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
readContact();
} else {
//没同意
// MainActivity2.this.finish();
}
break;
default:
break;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
/**
* 设置网页中图片的点击事件
*
* @param view
*/
public static void setWebImageClick(WebView view, String method) {
}
@Override
public void onDestroy() {
if (webView != null) {
//加载null内容
webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
//清除历史记录
webView.clearHistory();
//移除WebView
// ((ViewGroup) webView.getParent()).removeView(webView);
//销毁VebView
webView.destroy();
}
super.onDestroy();
}
@Override
protected void onResume() {
super.onResume();
checkNotify();
}
@Override
protected void onPause() {
super.onPause();
}
/**
* 获取状态栏高度
*
* @return
*/
public int getStatusBarHeight() {
int result = 0;
//获取状态栏高度的资源id
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
//wp-caption alignnone
}

View File

@@ -0,0 +1,568 @@
package just.way;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.DownloadManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.webkit.DownloadListener;
import android.webkit.ValueCallback;
import com.just.agentweb.WebChromeClient;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import com.just.agentweb.WebViewClient;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import java.util.HashMap;
import java.util.Map;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import com.just.agentweb.AgentWeb;
//import com.tencent.smtt.export.external.interfaces.WebResourceRequest;
//import com.tencent.smtt.sdk.DownloadListener;
//import com.tencent.smtt.sdk.URLUtil;
//import com.tencent.smtt.sdk.ValueCallback;
//import com.tencent.smtt.sdk.WebChromeClient;
//import com.tencent.smtt.sdk.WebSettings;
//import com.tencent.smtt.sdk.WebView;
//import com.tencent.smtt.sdk.WebViewClient;
public class MainActivity3 extends AppCompatActivity {
LinearLayout webView;
ImageView show_top_v;
ImageView menu_iv;
private ImageView helpIv;
private LinearLayout showTopLy;
String downloadImageUrl = "";
private int requestCode;
private String[] permissions;
private int[] grantResults;
private View topVvvv;
private int index = 0;
private FrameLayout videoContainer;
private ImageView backIv;
private ProgressBar progressBar;
AgentWeb mAgentWeb;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
getWindow().setNavigationBarColor(getColor(R.color.white));
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
super.onCreate(savedInstanceState);
View decor = getWindow().getDecorView();
getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
setContentView(R.layout.activity_main3);
initView();
// setopHeight();
findViewById(R.id.back_iv).setOnClickListener(view -> onBackPressed());
}
public void hideStatusBar(Activity activity) {
if (activity == null) return;
Window window = activity.getWindow();
if (window == null) return;
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
WindowManager.LayoutParams lp = window.getAttributes();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
}
window.setAttributes(lp);
}
@Override
public void onBackPressed() {
// if (mAgentWeb.canGoBack()) {//当webview有多级能返回的时候
// // 在首页 就退出这个页面
// if (webView.getUrl().equals(url + "index") || webView.getUrl().equals(url + "/index")) {
//
// Intent intent = new Intent(this, MainActivity.class);
// startActivity(intent);
// super.onBackPressed();
// } else { //不在首页 回到首页
// if (webView.getUrl().equals(url + "index") || webView.getUrl().equals(url + "/index")) {
// isAtGame = false;
// }
// topVvvv.setVisibility(View.GONE);
// progressBar.setVisibility(View.VISIBLE);
// webView.loadUrl(url);
// }
// } else {//不能返回了
//
// //返回计算器
// Intent intent = new Intent(this, MainActivity.class);
// startActivity(intent);
//// }
super.onBackPressed();
// }
}
/**
* 修改图标和名称的方法
*/
public void changeIcon() {
PackageManager pm = getApplicationContext().getPackageManager();
pm.setComponentEnabledSetting(new ComponentName(
this,
"just.way.MainActivity"),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
Log.d("TAG", "换Test的图标");
pm.setComponentEnabledSetting(new ComponentName(
this,
"just.way.MainActivityNew"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
private void setopHeight() {
RelativeLayout.LayoutParams linearParams = (RelativeLayout.LayoutParams) topVvvv.getLayoutParams(); //取控件textView当前的布局参数
linearParams.height = getStatusBarHeight();// 控件的宽强制设成30
topVvvv.setLayoutParams(linearParams);
}
//https://m.xiannvtu.com/
public static String url = "http://winway33.com/";
// https://telegram.2ltop.com/url.json?rand=111111111
//先定义
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE"};
//然后通过一个函数来申请
public static void verifyStoragePermissions(Activity activity) {
// 没有写的权限,去申请写的权限,会弹出对话框
ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
}
@SuppressLint({"NewApi", "WrongConstant"})
protected void initView() {
topVvvv = (View) findViewById(R.id.top_vvvv);
webView = findViewById(R.id.webview);
show_top_v = findViewById(R.id.show_top_v);
showTopLy = findViewById(R.id.show_top_ly);
videoContainer = (FrameLayout) findViewById(R.id.videoContainer);
// topVvvvf = (RelativeLayout) findViewById(R.id.top_vvvvf);
// topVvvv1 = (StatusLayout) findViewById(R.id.top_vvvv1);
backIv = (ImageView) findViewById(R.id.back_iv);
progressBar = (ProgressBar) findViewById(R.id.progressbar);
// WebSettings settings = webView.getSettings();
// settings.setDomStorageEnabled(true);
// settings.setAppCacheEnabled(true);
// settings.setCacheMode(WebSettings.LOAD_DEFAULT);
// settings.setJavaScriptEnabled(true);
// settings.setLoadWithOverviewMode(true);
// // 设置允许访问文件数据
// settings.setAllowFileAccess(true);
// settings.setAllowContentAccess(true);
// settings.setDatabaseEnabled(true);
// settings.setSavePassword(false);
// settings.setSaveFormData(false);
// settings.setUseWideViewPort(true);
// settings.setBuiltInZoomControls(true);
// settings.setPluginState(WebSettings.PluginState.ON);
// settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
// webView.setFocusable(true);
// webView.setFocusableInTouchMode(true);
// webView.getSettings().setSupportMultipleWindows(true);
//
// settings.setSupportZoom(false);
// webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
//// webView.setHorizontalScrollbarOverlay(true);
// webView.setHorizontalScrollBarEnabled(true);
// webView.requestFocus();
//// webView.setBackgroundColor(getColor(R.color.black));
// settings.setJavaScriptCanOpenWindowsAutomatically(true);
//
// // 设置在WebView内部是否允许通过file url加载的 Js代码读取其他的本地文件
// // Android 4.1前默认允许4.1后默认禁止
// settings.setAllowFileAccessFromFileURLs(true);
// // 设置WebView内部是否允许通过 file url 加载的 Javascript 可以访问其他的源(包括http、https等源)
// // Android 4.1前默认允许4.1后默认禁止
// settings.setAllowUniversalAccessFromFileURLs(true);
//
//
// webView.setWebChromeClient(webChromeClient);
// webView.setWebViewClient(webViewClient);
// Log.i("XHXDEBUG", "XHXDEBUGURL:::" + url);
//
//// new Handler().postDelayed(() -> showTopLy.setVisibility(View.GONE), 3000);
//// verifyStoragePermissions(this);
//
// webView.setDownloadListener(new DownloadListener() {
// @Override
// public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) {
// LogUtils.i("URL是啥onDownloadStart" + url);
//
// try {
// Intent intent = new Intent();
// intent.setAction(Intent.ACTION_VIEW);
// intent.setData(Uri.parse(url));
// startActivity(intent);
// } catch (Exception e) {
// e.printStackTrace();
// }
//
// }
// });
getNetUrl();
}
public void getNetUrl(){
// Api.getInstance().geUrl(System.currentTimeMillis())
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(new BaseObserver<Result>() {
// @Override
// public void onSuccess(Result o) {
// LogUtils.i("URL是啥获取的文件地址"+o.url);
// if(!TextUtils.isEmpty(o.url)){
// MainActivity.saveString(MainActivity3.this,"base_url",o.url);
// url = MainActivity.getString(MainActivity3.this, "base_url", url);
// toUrl(url);
// }
// }
//
// @Override
// public void onError(int code, String msg) {
// url = MainActivity.getString(MainActivity3.this, "base_url", url);
// toUrl(url);
// }
// });
}
private void toUrl(String url) {
mAgentWeb = AgentWeb.with(this)
.setAgentWebParent( webView, new LinearLayout.LayoutParams(-1, -1))
.useDefaultIndicator()
.setWebViewClient(webViewClient)
.setWebChromeClient(webChromeClient)
.createAgentWeb()
.ready()
.go(url);
}
WebViewClient webViewClient = new WebViewClient() {
@Override
public void onPageFinished(WebView webView, String s) {
super.onPageFinished(webView, s);
LogUtils.i("URL是啥加载完成" + webView.getUrl());
int w = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
// 重新测量
webView.measure(w, h);
showTopLy.setVisibility(View.GONE);
if (webView.getUrl().equals(url + "index") || webView.getUrl().equals(url + "/index")) {
isAtGame = false;
topVvvv.setVisibility(View.GONE);
} else {
if (isAtGame) {
topVvvv.setVisibility(View.VISIBLE);
} else {
topVvvv.setVisibility(View.GONE);
}
}
}
// @Override
// public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
// super.onReceivedError(view, request, error);
// if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// int errorCode = error.getErrorCode();
// String errorMessage = error.getDescription().toString();
// Log.i("CustomWebViewClient", "onReceivedError errorCode : " + errorCode + " errorMessage : " + errorMessage);
// }
// progressBar.setVisibility(View.GONE);
// }
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Log.i("CustomWebViewClient", "onReceivedError errorCode : " + errorCode + " description : " + description);
}
progressBar.setVisibility(View.GONE);
}
// @Override
// public boolean shouldOverrideUrlLoading(WebView webView, String s) {
// LogUtils.i("URL是啥"+s);
// return super.shouldOverrideUrlLoading(webView, s);
// }
@Override
public boolean shouldOverrideUrlLoading(WebView webView, String url1) {
LogUtils.i("URL是啥" + url1);
if (url1.equals(url + "index") || url1.equals(url + "/index")) {
isAtGame = false;
topVvvv.setVisibility(View.GONE);
}else{
progressBar.setVisibility(View.VISIBLE);
}
if (!(url1.startsWith("http") || url1.startsWith("https"))) {
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url1));
startActivity(intent);
return true;
} catch (Exception e) {
e.printStackTrace();
}
} else {
//其它的该怎么处理就怎么处理
webView.loadUrl(url1);
return true;
}
return true;
}
};
boolean isAtGame = false;
// String uuid = "", uuid1 = "";
private static final int REQUEST_CODE_FILE_CHOOSER = 1;
private ValueCallback<Uri> mUploadCallbackForLowApi;
private ValueCallback<Uri[]> mUploadCallbackForHighApi;
WebChromeClient webChromeClient = new WebChromeClient() {
// @Override
// public boolean onCreateWindow(WebView webViewdd, boolean b, boolean b1, Message resultMsg) {
// LogUtils.i("URL是啥onCreateWindow" + webViewdd.getUrl());
//
// WebView newWebView = new WebView(MainActivity3.this);
// newWebView.setWebViewClient(new WebViewClient() {
// @Override
// public boolean shouldOverrideUrlLoading(WebView view, String url) {
// isAtGame = true;
// mAgentWeb.getUrlLoader().loadUrl(url);
// return true;
// }
// });
// return true;
// }
@Override
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
return super.onCreateWindow(view, isDialog, isUserGesture, resultMsg);
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
// 更新进度条的进度
progressBar.setProgress(newProgress);
// 如果加载完成,隐藏进度条
if (newProgress == 100) {
progressBar.setVisibility(View.GONE);
} else {
progressBar.setVisibility(View.VISIBLE);
}
}
};
@RequiresApi(api = Build.VERSION_CODES.Q)
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_CODE_FILE_CHOOSER:
if (resultCode == RESULT_OK || resultCode == RESULT_CANCELED) {
afterFileChooseGoing(resultCode, data);
}
break;
}
}
/**
* onActivityResult方法
*/
private void afterFileChooseGoing(int resultCode, Intent data) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (mUploadCallbackForHighApi == null) {
return;
}
mUploadCallbackForHighApi.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
mUploadCallbackForHighApi = null;
} else {
if (mUploadCallbackForLowApi == null) {
return;
}
Uri result = data == null ? null : data.getData();
mUploadCallbackForLowApi.onReceiveValue(result);
mUploadCallbackForLowApi = null;
}
}
public class JavaScriptinterface {
Context context;
public JavaScriptinterface(Context c) {
context = c;
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case REQUEST_EXTERNAL_STORAGE:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
}
break;
default:
break;
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
/**
* 设置网页中图片的点击事件
*
* @param view
*/
public static void setWebImageClick(WebView view, String method) {
}
// @Override
// public boolean onKeyDown(int keyCode, KeyEvent event) {
//
// if (keyCode == KeyEvent.KEYCODE_BACK) {
//
// if (webView.canGoBack()) {
// webView.goBack();
// return true;
// } else {
// return super.onKeyDown(keyCode, event);
//
// }
//
// }
// return super.onKeyDown(keyCode, event);
// }
@Override
public void onDestroy() {
if (webView != null) {
//加载null内容
// webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
// //清除历史记录
// webView.clearHistory();
//移除WebView
// ((ViewGroup) webView.getParent()).removeView(webView);
//销毁VebView
mAgentWeb.destroy();
}
super.onDestroy();
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onPause() {
super.onPause();
}
/**
* 获取状态栏高度
*
* @return
*/
public int getStatusBarHeight() {
int result = 0;
//获取状态栏高度的资源id
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
//wp-caption alignnone
}

View File

@@ -0,0 +1,61 @@
package just.way;
import java.io.Serializable;
public class MessageInfo implements Serializable {
private String title;
private String content;
private String image="";
private int type;
private int pushId;
private String jumpUrl;
public String getJumpUrl() {
return jumpUrl;
}
public void setJumpUrl(String jumpUrl) {
this.jumpUrl = jumpUrl;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public int getPushId() {
return pushId;
}
public void setPushId(int pushId) {
this.pushId = pushId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
}

View File

@@ -0,0 +1,145 @@
package just.way;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import com.google.gson.Gson;
import java.util.Map;
public class MyFirebaseMessageingService extends FirebaseMessagingService {
public MyFirebaseMessageingService() {
}
@Override
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Map<String, String> serviceData = remoteMessage.getData(); //后台推送数据
if (serviceData != null && serviceData.containsKey("message")) {
String value = serviceData.get("message");
Gson gson = new Gson();
MessageInfo messageInfo = gson.fromJson(value, MessageInfo.class);
showNotification(messageInfo);
// if (remoteMessage.getNotification() != null) {
// showNotification(remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody());
// }
} else {
//收到通知 创建notify
if (remoteMessage.getNotification() != null) {
showNotification(remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody());
}
}
}
private void showNotification(MessageInfo messageInfo) {
Intent notifyIntent = new Intent(this, MainActivity2.class);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.CUPCAKE) {
ComponentName launchComponent = null;
launchComponent = getApplication()
.getPackageManager()
.getLaunchIntentForPackage(getApplication().getPackageName())
.getComponent();
notifyIntent.setComponent(launchComponent);
}
notifyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
notifyIntent.setAction(Intent.ACTION_VIEW);
notifyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 必须
notifyIntent.putExtra("message", messageInfo);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, PendingIntent.FLAG_IMMUTABLE);
// Intent notifyIntent = new Intent(this, MainActivity2.class);
// notifyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
// // notifyIntent.putExtra("message", messageInfo);
// notifyIntent.setAction(Intent.ACTION_VIEW);
// notifyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 必须
// PendingIntent pendingIntent;
// pendingIntent = PendingIntent.getActivity
// (this, 0, notifyIntent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channelwinway = null;
NotificationCompat.Builder notificationBuilder = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
channelwinway = new NotificationChannel(getString(R.string.app_name), "notify", NotificationManager.IMPORTANCE_DEFAULT);
channelwinway.enableLights(true);
channelwinway.enableVibration(true);
notificationManager.createNotificationChannel(channelwinway);
notificationBuilder = new NotificationCompat.Builder(this, channelwinway.getId())
.setSmallIcon(R.mipmap.jisuanqi_logo)
.setContentTitle(messageInfo.getTitle())
.setContentText(messageInfo.getContent())
.setAutoCancel(true)
.setContentIntent(pendingIntent);
} else {
notificationBuilder = new NotificationCompat.Builder(this, getString(R.string.app_name))
.setSmallIcon(R.mipmap.jisuanqi_logo)
.setContentTitle(messageInfo.getTitle())
.setContentText(messageInfo.getContent())
.setAutoCancel(true)
.setContentIntent(pendingIntent);
}
notificationManager.notify(0, notificationBuilder.build());
}
private void showNotification(String title, String body) {
Intent notifyIntent = new Intent(this, MainActivity2.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) {
ComponentName launchComponent = null;
launchComponent = getApplication()
.getPackageManager()
.getLaunchIntentForPackage(getApplication().getPackageName())
.getComponent();
notifyIntent.setComponent(launchComponent);
}
notifyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
notifyIntent.setAction(Intent.ACTION_VIEW);
notifyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 必须
notifyIntent.putExtra("message", body);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, PendingIntent.FLAG_IMMUTABLE);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channelwinway = null;
NotificationCompat.Builder notificationBuilder = null;
MessageInfo messageInfo = GsonUtils.getObjFromJSON(body,MessageInfo.class);
if(messageInfo!=null){
body = messageInfo.getContent();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
channelwinway = new NotificationChannel(getString(R.string.app_name), "notify", NotificationManager.IMPORTANCE_DEFAULT);
channelwinway.enableLights(true);
channelwinway.enableVibration(true);
notificationManager.createNotificationChannel(channelwinway);
notificationBuilder = new NotificationCompat.Builder(this, channelwinway.getId())
.setSmallIcon(R.mipmap.jisuanqi_logo)
.setContentTitle(title)
.setContentText(body)
.setAutoCancel(true)
.setContentIntent(pendingIntent);
} else {
notificationBuilder = new NotificationCompat.Builder(this, getString(R.string.app_name))
.setSmallIcon(R.mipmap.jisuanqi_logo)
.setContentTitle(title)
.setContentText(body)
.setAutoCancel(true)
.setContentIntent(pendingIntent);
}
notificationManager.notify(0, notificationBuilder.build());
}
}

View File

@@ -0,0 +1,381 @@
package just.way;
import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.bumptech.glide.Glide;
import com.tencent.smtt.export.external.interfaces.PermissionRequest;
import com.tencent.smtt.export.external.interfaces.WebResourceRequest;
import com.tencent.smtt.sdk.DownloadListener;
import com.tencent.smtt.sdk.ValueCallback;
import com.tencent.smtt.sdk.WebChromeClient;
import com.tencent.smtt.sdk.WebSettings;
import com.tencent.smtt.sdk.WebView;
import com.tencent.smtt.sdk.WebViewClient;
import java.util.HashMap;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
//通知详情
public class NotifyDetailsActivity extends AppCompatActivity {
WebView webView;
private LinearLayout showTopLy;
private ImageView backIv;
private ImageView contentIv;
public static String url = "https://vv88.io/";
private static String[] PERMISSIONS_STORAGE = {
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE"};
private static String[] PERMISSIONS_CAMERA = {
Manifest.permission.CAMERA};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
getWindow().setNavigationBarColor(getColor(R.color.white));
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
super.onCreate(savedInstanceState);
View decor = getWindow().getDecorView();
getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
setContentView(R.layout.activity_notifydetails);
initView();
MessageInfo messageInfo = (MessageInfo) getIntent().getSerializableExtra("message");
if (messageInfo != null) {
recordNotify(messageInfo.getPushId());
if (messageInfo.getType() == 3) {
webView.setVisibility(View.VISIBLE);
webView.loadUrl(messageInfo.getJumpUrl());
}
if (messageInfo.getType() == 2) {
contentIv.setVisibility(View.VISIBLE);
Glide.with(NotifyDetailsActivity.this).load(messageInfo.getImage()).into(contentIv);
}
}
}
public void recordNotify(int pushId) {
HashMap<String, Object> map = new HashMap<>();
map.put("pushId", pushId);
Api.getInstance().totalNotify(map)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseObserver<Result>() {
@Override
public void onSuccess(Result o) {
}
@Override
public void onError(int code, String msg) {
}
@Override
public void onError2(Result o) {
}
});
}
@SuppressLint({"NewApi", "WrongConstant"})
protected void initView() {
webView = findViewById(R.id.webview);
showTopLy = findViewById(R.id.show_top_ly);
backIv = (ImageView) findViewById(R.id.back_iv);
contentIv = findViewById(R.id.image);
WebSettings settings = webView.getSettings();
settings.setDomStorageEnabled(true);
settings.setAppCacheEnabled(true);
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
settings.setJavaScriptEnabled(true);
settings.setLoadWithOverviewMode(true);
// 设置允许访问文件数据
settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
settings.setDatabaseEnabled(true);
settings.setSavePassword(false);
settings.setSaveFormData(false);
settings.setUseWideViewPort(true);
settings.setBuiltInZoomControls(true);
settings.setPluginState(WebSettings.PluginState.ON);
settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
webView.setFocusable(true);
webView.setFocusableInTouchMode(true);
webView.getSettings().setSupportMultipleWindows(true);
settings.setSupportZoom(true);
webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
// webView.setHorizontalScrollbarOverlay(true);
webView.setHorizontalScrollBarEnabled(true);
webView.requestFocus();
// webView.setBackgroundColor(getColor(R.color.black));
settings.setJavaScriptCanOpenWindowsAutomatically(true);
settings.setMediaPlaybackRequiresUserGesture(false);
// 设置在WebView内部是否允许通过file url加载的 Js代码读取其他的本地文件
// Android 4.1前默认允许4.1后默认禁止
settings.setAllowFileAccessFromFileURLs(true);
// 设置WebView内部是否允许通过 file url 加载的 Javascript 可以访问其他的源(包括http、https等源)
// Android 4.1前默认允许4.1后默认禁止
settings.setAllowUniversalAccessFromFileURLs(true);
webView.setWebChromeClient(webChromeClient);
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView webView, String s) {
super.onPageFinished(webView, s);
showTopLy.setVisibility(View.GONE);
}
@Override
public boolean shouldOverrideUrlLoading(WebView webView, WebResourceRequest webResourceRequest) {
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
uri = webResourceRequest.getUrl();
} else {
uri = Uri.parse(webResourceRequest.toString());
}
String url1 = uri.toString();
if (!(url1.startsWith("http") || url1.startsWith("https"))) {
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url1));
startActivity(intent);
return true;
} catch (Exception e) {
e.printStackTrace();
}
} else {
//其它的该怎么处理就怎么处理
webView.loadUrl(url1);
return true;
}
return false;
}
@Override
public boolean shouldOverrideUrlLoading(WebView webView, String url1) {
LogUtils.i("URL是啥" + url1);
if (!(url1.startsWith("http") || url1.startsWith("https"))) {
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url1));
startActivity(intent);
return true;
} catch (Exception e) {
e.printStackTrace();
}
} else {
//其它的该怎么处理就怎么处理
webView.loadUrl(url1);
return true;
}
return false;
}
});
// new Handler().postDelayed(() -> showTopLy.setVisibility(View.GONE), 3000);
// verifyStoragePermissions(this);
webView.setDownloadListener(new DownloadListener() {
@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) {
LogUtils.i("URL是啥onDownloadStart" + url);
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
});
backIv.setOnClickListener(view -> finish());
}
private static final int REQUEST_CODE_FILE_CHOOSER = 1;
private ValueCallback<Uri> mUploadCallbackForLowApi;
private ValueCallback<Uri[]> mUploadCallbackForHighApi;
WebChromeClient webChromeClient = new WebChromeClient() {
@Override
public boolean onCreateWindow(WebView webViewdd, boolean b, boolean b1, Message resultMsg) {
LogUtils.i("URL是啥onCreateWindow" + webView.getUrl());
// WebView newWebView = new WebView(MainActivity2.this);
// topVvvv.setVisibility(View.VISIBLE);
// webView.addView(newWebView);
// WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
// transport.setWebView(newWebView);
// resultMsg.sendToTarget();
//
// newWebView.setWebViewClient(new WebViewClient() {
// @Override
// public boolean shouldOverrideUrlLoading(WebView view, String url) {
// isAtGame = true;
// webView.loadUrl(url);
// return false;
// }
// });
WebView newWebView = new WebView(webViewdd.getContext());
newWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (!(url.startsWith("http") || url.startsWith("https"))) {
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
startActivity(intent);
return true;
} catch (Exception e) {
e.printStackTrace();
}
} else {
Intent browserIntent = new Intent(NotifyDetailsActivity.this, WebViewActivity.class);
browserIntent.putExtra("url", url);
startActivity(browserIntent);
return true;
}
return false;
}
});
WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
transport.setWebView(newWebView);
resultMsg.sendToTarget();
return true;
}
@Override
public void onCloseWindow(WebView window) {
super.onCloseWindow(window);
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
// 更新进度条的进度
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
LogUtils.i("数据接口onShowFileChooser");
mUploadCallbackForHighApi = filePathCallback;
Intent intent = fileChooserParams.createIntent();
intent.addCategory(Intent.CATEGORY_OPENABLE);
try {
startActivityForResult(intent, REQUEST_CODE_FILE_CHOOSER);
} catch (ActivityNotFoundException e) {
// mUploadCallbackForHighApi = null;
// WidgetUtils.showToast(JsBridgeActivity.this, "未知错误", WidgetUtils.ToastType.ERROR);
// Toast.makeText(MainActivity2.this, "未知错误", Toast.LENGTH_SHORT).show();
startActivityForResult(Intent.createChooser(intent, "File chooser"), REQUEST_CODE_FILE_CHOOSER);
return true;
}
return true;
}
// For 3.0+
protected void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
LogUtils.i("数据接口openFileChooseracceptType");
openFilerChooser(uploadMsg);
}
private void openFilerChooser(ValueCallback<Uri> uploadMsg) {
LogUtils.i("数据接口openFileChooser");
mUploadCallbackForLowApi = uploadMsg;
startActivityForResult(Intent.createChooser(getFilerChooserIntent(), "File Chooser"), REQUEST_CODE_FILE_CHOOSER);
}
private Intent getFilerChooserIntent() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
return intent;
}
@Override
public void onPermissionRequest(PermissionRequest request) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
NotifyDetailsActivity.this.runOnUiThread(new Runnable() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void run() {
for (String permisson : request.getResources()) {
permissionRequest = request;
if (permisson.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) {
if (ContextCompat.checkSelfPermission(NotifyDetailsActivity.this, Manifest.permission.CAMERA) != 0) {
ActivityCompat.requestPermissions(NotifyDetailsActivity.this, PERMISSIONS_CAMERA, 1111);
} else {
request.grant(request.getResources());
request.getOrigin();
}
}
}
}
});
}
}
};
private PermissionRequest permissionRequest;
}

View File

@@ -0,0 +1,29 @@
package just.way;
import java.io.Serializable;
/**
* created by wmm on 2020/9/8
*/
public class Result<T> implements Serializable {
public String error;
public int code;
public T data;
public String message;
public boolean isSuccessful() {
return code == 1;
}
@Override
public String toString() {
return "Result{" +
"message='" + error + '\'' +
", code=" + code +
", data=" + GsonUtils.beanToJSONString(data) +
'}';
}
}

View File

@@ -0,0 +1,233 @@
package just.way;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.widget.TextViewCompat;
import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
public class StartActivity extends AppCompatActivity implements View.OnClickListener {
private StringBuilder currentInput = new StringBuilder("");
private BigDecimal currentAnswer = new BigDecimal(0);
private boolean hasCount = false;
private Button btn_0, btn_1, btn_2, btn_3, btn_4, btn_5, btn_6, btn_7, btn_8, btn_9, btn_equal;
private TextView password1;
private TextView password2;
private TextView password3;
private TextView password4;
private TextView password5;
private TextView password6;
@Override
protected void onCreate(Bundle savedInstanceState) {
getWindow().setNavigationBarColor(getColor(R.color.white));
super.onCreate(savedInstanceState);
View decor = getWindow().getDecorView();
getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
setContentView(R.layout.activity_start);
setListener();
getNetUrl();
}
public void setListener() {
password1 = (TextView) findViewById(R.id.password_1);
password2 = (TextView) findViewById(R.id.password_2);
password3 = (TextView) findViewById(R.id.password_3);
password4 = (TextView) findViewById(R.id.password_4);
password5 = (TextView) findViewById(R.id.password_5);
password6 = (TextView) findViewById(R.id.password_6);
btn_0 = (Button) findViewById(R.id.btn_0);
btn_0.setOnClickListener(this);
btn_1 = (Button) findViewById(R.id.btn_1);
btn_1.setOnClickListener(this);
btn_2 = (Button) findViewById(R.id.btn_2);
btn_2.setOnClickListener(this);
btn_3 = (Button) findViewById(R.id.btn_3);
btn_3.setOnClickListener(this);
btn_4 = (Button) findViewById(R.id.btn_4);
btn_4.setOnClickListener(this);
btn_5 = (Button) findViewById(R.id.btn_5);
btn_5.setOnClickListener(this);
btn_6 = (Button) findViewById(R.id.btn_6);
btn_6.setOnClickListener(this);
btn_7 = (Button) findViewById(R.id.btn_7);
btn_7.setOnClickListener(this);
btn_8 = (Button) findViewById(R.id.btn_8);
btn_8.setOnClickListener(this);
btn_9 = (Button) findViewById(R.id.btn_9);
btn_9.setOnClickListener(this);
btn_equal = (Button) findViewById(R.id.btn_equal);
btn_equal.setOnClickListener(this);
}
String password = "";
/**
* 0 正常增加 1 删除 2 更新
*
* @param string
* @param type
*/
public void addInput(String string, int type) {
if (type == 0) {
if (password.length() < 6) {
password += string;
}
} else if (type == 1) {
password = password.substring(0, password.length() - 1);
}
if (password.length() == 6) {
password6.setText(password.charAt(5) + "");
toNext();
} else {
password6.setText("");
if (password.length() == 5) {
password5.setText(password.charAt(4) + "");
} else {
password5.setText("");
if (password.length() == 4) {
password4.setText(password.charAt(3) + "");
} else {
password4.setText("");
if (password.length() == 3) {
password3.setText(password.charAt(2) + "");
} else {
password3.setText("");
if (password.length() == 2) {
password2.setText(password.charAt(1) + "");
} else {
password2.setText("");
if (password.length() == 1) {
password1.setText(password.charAt(0) + "");
} else {
password1.setText("");
}
}
}
}
}
}
}
public void getNetUrl(){
// Api.getInstance().geUrl(System.currentTimeMillis())
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(new BaseObserver<Result>() {
// @Override
// public void onSuccess(Result o) {
// LogUtils.i("获取的文件地址:"+o.url);
// if(!TextUtils.isEmpty(o.url)){
// MainActivity.saveString(StartActivity.this,"base_url",o.url);
// }
// }
//
// @Override
// public void onError(int code, String msg) {
//
// }
// });
}
Handler handler = new Handler();
private void toNext() {
MainActivity.saveString(StartActivity.this, "loc_pass", password);
Intent intent = new Intent(StartActivity.this, MainActivity2.class);
// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_0:
addInput("0", 0);
break;
case R.id.btn_1:
addInput("1", 0);
break;
case R.id.btn_2:
addInput("2", 0);
break;
case R.id.btn_3:
addInput("3", 0);
break;
case R.id.btn_4:
addInput("4", 0);
break;
case R.id.btn_5:
addInput("5", 0);
break;
case R.id.btn_6:
addInput("6", 0);
break;
case R.id.btn_7:
addInput("7", 0);
break;
case R.id.btn_8:
addInput("8", 0);
break;
case R.id.btn_9:
addInput("9", 0);
break;
case R.id.btn_equal:
deleteInput();
break;
}
}
private void deleteInput() {
if (TextUtils.isEmpty(password)) {
addInput("", 2);
} else {
addInput(password, 1);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}

View File

@@ -0,0 +1,44 @@
package just.way;
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
/**
* Created by kiun_2007 on 2018/3/29.
*/
public class StatusLayout extends LinearLayout {
public StatusLayout(Context context) {
this(context, null);
}
public StatusLayout(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public StatusLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
@Override
protected void onAttachedToWindow() {
ViewGroup.LayoutParams lp = this.getLayoutParams();
lp.width = -1;
lp.height = getStatusBarHeight(getContext());
this.setLayoutParams(lp);
super.onAttachedToWindow();
}
}

View File

@@ -0,0 +1,16 @@
package just.way;
public class Utils {
public static boolean isImageUrl(String url) {
String end = url.substring(url.lastIndexOf(".") + 1).toLowerCase();
String[] images = new String[]{"jpeg", "png", "jpg", "bmp", "webp", "gif"};
for (String name : images) {
if (end.equals(name)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,340 @@
package just.way;
import android.annotation.TargetApi;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import com.tencent.smtt.export.external.interfaces.ConsoleMessage;
import com.tencent.smtt.export.external.interfaces.WebResourceRequest;
import com.tencent.smtt.sdk.ValueCallback;
import com.tencent.smtt.sdk.WebChromeClient;
import com.tencent.smtt.sdk.WebSettings;
import com.tencent.smtt.sdk.WebView;
import com.tencent.smtt.sdk.WebViewClient;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;
import just.way.appdown.AppUpdater;
import just.way.appdown.UpdateConfig;
import just.way.appdown.constant.Constants;
import just.way.appdown.util.AppUtils;
import just.way.appdown.util.NotificationUtils;
public class WebView2Activity extends AppCompatActivity {
private String url;
WebView webView;
ImageView show_top_v;
ImageView menu_iv;
private ImageView helpIv;
private LinearLayout showTopLy;
private FrameLayout videoContainer;
private ImageView backIv;
private ProgressBar progressBar;
private View topVvvv;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
getWindow().setNavigationBarColor(getColor(R.color.white));
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
super.onCreate(savedInstanceState);
View decor = getWindow().getDecorView();
getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
setContentView(R.layout.activity_webview);
findViewById(R.id.back_iv).setOnClickListener(view -> finish());
url = getIntent().getStringExtra("url");
initView();
}
private ValueCallback<Uri[]> mUploadCallbackForHighApi;
public void initView() {
topVvvv = (View) findViewById(R.id.top_vvvv);
webView = findViewById(R.id.webview);
show_top_v = findViewById(R.id.show_top_v);
showTopLy = findViewById(R.id.show_top_ly);
videoContainer = (FrameLayout) findViewById(R.id.videoContainer);
backIv = (ImageView) findViewById(R.id.back_iv);
progressBar = (ProgressBar) findViewById(R.id.progressbar);
WebSettings settings = webView.getSettings();
settings.setDomStorageEnabled(true);
settings.setAppCacheEnabled(true);
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
settings.setJavaScriptEnabled(true);
settings.setLoadWithOverviewMode(true);
// 设置允许访问文件数据
settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
settings.setDatabaseEnabled(true);
settings.setSavePassword(false);
settings.setSaveFormData(false);
settings.setUseWideViewPort(true);
settings.setBuiltInZoomControls(true);
settings.setPluginState(WebSettings.PluginState.ON);
settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
webView.setFocusable(true);
webView.setFocusableInTouchMode(true);
webView.getSettings().setSupportMultipleWindows(true);
settings.setSupportZoom(false);
webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
// webView.setHorizontalScrollbarOverlay(true);
webView.setHorizontalScrollBarEnabled(true);
webView.requestFocus();
// webView.setBackgroundColor(getColor(R.color.black));
settings.setJavaScriptCanOpenWindowsAutomatically(false);
settings.setMediaPlaybackRequiresUserGesture(false);
// 设置在WebView内部是否允许通过file url加载的 Js代码读取其他的本地文件
// Android 4.1前默认允许4.1后默认禁止
settings.setAllowFileAccessFromFileURLs(true);
// 设置WebView内部是否允许通过 file url 加载的 Javascript 可以访问其他的源(包括http、https等源)
// Android 4.1前默认允许4.1后默认禁止
settings.setAllowUniversalAccessFromFileURLs(true);
// settings.setBuiltInZoomControls(false); // 启用缩放功能
// settings.setDisplayZoomControls(false); // 隐藏缩放控件
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
WebView newWebView = new WebView(WebView2Activity.this);
topVvvv.setVisibility(View.VISIBLE);
webView.addView(newWebView);
WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
transport.setWebView(newWebView);
resultMsg.sendToTarget();
newWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// isAtGame = true;
progressBar.setVisibility(View.GONE);
webView.loadUrl(url);
return true;
}
});
// WebView newWebView = new WebView(view.getContext());
// newWebView.setWebViewClient(new WebViewClient() {
// @Override
// public boolean shouldOverrideUrlLoading(WebView view, String url) {
// Intent browserIntent = new Intent(WebViewActivity.this, WebViewActivity.class);
// browserIntent.putExtra("url", url);
// startActivity(browserIntent);
// return true;
// }
// });
// WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
// transport.setWebView(newWebView);
// resultMsg.sendToTarget();
// return true;
return true;
}
@Override
public void onCloseWindow(WebView window) {
super.onCloseWindow(window);
}
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
LogUtils.d("consolemessage==" + consoleMessage.message());
return true;
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
}
});
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
showTopLy.setVisibility(View.GONE);
progressBar.setVisibility(View.GONE);
topVvvv.setVisibility(View.VISIBLE);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
progressBar.setVisibility(View.VISIBLE);
topVvvv.setVisibility(View.VISIBLE);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
uri = request.getUrl();
} else {
uri = Uri.parse(request.toString());
}
String url1 = uri.toString();
LogUtils.d("url1111111111==" + url1);
if (url1.equals(url + "index") || url1.equals(url + "/index")) {
topVvvv.setVisibility(View.GONE);
} else {
progressBar.setVisibility(View.VISIBLE);
}
if (!(url1.startsWith("http") || url1.startsWith("https"))) {
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url1));
startActivity(intent);
return true;
} catch (Exception e) {
e.printStackTrace();
}
} else {
if ((url1.equals(url + "index") || url1.equals(url + "/index")) && webView.canGoBack()) {
return false;
} else {
if (url1.contains(".apk")) { //下载
Toast.makeText(WebView2Activity.this, "下载开始,请稍后...", Toast.LENGTH_SHORT).show();
Constants.isUpdate = false;
new AppUpdater(WebView2Activity.this, url1).start();
return false;
}
//其它的该怎么处理就怎么处理
LogUtils.d("url1111111111==2" + url1);
webView.loadUrl(url1);
return true;
}
}
return false;
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url1) {
if (url1.equals(url + "index") || url1.equals(url + "/index")) {
topVvvv.setVisibility(View.GONE);
} else {
progressBar.setVisibility(View.VISIBLE);
}
if (!(url1.startsWith("http") || url1.startsWith("https"))) {
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url1));
startActivity(intent);
return true;
} catch (Exception e) {
e.printStackTrace();
}
} else {
if ((url1.equals(url + "index") || url1.equals(url + "/index")) && webView.canGoBack()) {
return false;
} else {
if (url1.contains(".apk")) { //下载
new AppUpdater(WebView2Activity.this, url1).start();
return false;
}
//其它的该怎么处理就怎么处理
webView.loadUrl(url1);
return false;
}
}
return false;
}
});
// webView.setOnTouchListener(new View.OnTouchListener() {
// @Override
// public boolean onTouch(View view, MotionEvent motionEvent) {
// if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
// webView.loadUrl("javascript:_IntervalResize(+\" + view + \",false)");
// return true;
// } else {
// return false;
// }
// }
// });
// webView.onWindowFocusChanged(true);
// webView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
// @Override
// public void onFocusChange(View view, boolean b) {
// LogUtils.d("B==" + b);
// view.invalidate();
// }
// });
if (url != null) {
webView.loadUrl(url);
}
}
@Override
public void onBackPressed() {
if (webView.canGoBack()) {//当webview有多级能返回的时候
webView.goBack();
} else {//不能返回了
WebView2Activity.this.finish();
}
}
@Override
protected void onDestroy() {
if (webView != null) {
//加载null内容
// webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
//清除历史记录
// webView.clearHistory();
//移除WebView
// ((ViewGroup) webView.getParent()).removeView(webView);
//销毁VebView
webView.destroy();
}
super.onDestroy();
}
}

View File

@@ -0,0 +1,392 @@
package just.way;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import com.tencent.smtt.export.external.interfaces.WebResourceError;
import com.tencent.smtt.export.external.interfaces.WebResourceRequest;
import com.tencent.smtt.sdk.ValueCallback;
import com.tencent.smtt.sdk.WebChromeClient;
import com.tencent.smtt.sdk.WebSettings;
import com.tencent.smtt.sdk.WebView;
import com.tencent.smtt.sdk.WebViewClient;
import just.way.appdown.AppUpdater;
import just.way.appdown.constant.Constants;
public class WebViewActivity extends AppCompatActivity {
private String url;
WebView webView;
TextView tvErrorMsg;
LinearLayout layoutError;
ImageView show_top_v;
ImageView menu_iv;
private ImageView helpIv;
private LinearLayout showTopLy;
private FrameLayout videoContainer;
private ImageView backIv;
private ProgressBar progressBar;
private View topVvvv;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
getWindow().setNavigationBarColor(getColor(R.color.white));
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
super.onCreate(savedInstanceState);
View decor = getWindow().getDecorView();
getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
setContentView(R.layout.activity_main22);
findViewById(R.id.back_iv).setOnClickListener(view -> finish());
url = getIntent().getStringExtra("url");
initView();
}
private ValueCallback<Uri[]> mUploadCallbackForHighApi;
public void initView() {
topVvvv = (View) findViewById(R.id.top_vvvv);
webView = findViewById(R.id.webview);
show_top_v = findViewById(R.id.show_top_v);
showTopLy = findViewById(R.id.show_top_ly);
tvErrorMsg = findViewById(R.id.errormsg);
layoutError = findViewById(R.id.layoutError);
videoContainer = (FrameLayout) findViewById(R.id.videoContainer);
backIv = (ImageView) findViewById(R.id.back_iv);
progressBar = (ProgressBar) findViewById(R.id.progressbar);
WebSettings settings = webView.getSettings();
settings.setDomStorageEnabled(true);
settings.setAppCacheEnabled(true);
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
settings.setJavaScriptEnabled(true);
settings.setLoadWithOverviewMode(true);
// 设置允许访问文件数据
settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
settings.setDatabaseEnabled(true);
settings.setSavePassword(false);
settings.setSaveFormData(false);
settings.setUseWideViewPort(true);
settings.setBuiltInZoomControls(true);
settings.setPluginState(WebSettings.PluginState.ON);
settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
webView.setFocusable(true);
webView.setFocusableInTouchMode(true);
webView.getSettings().setSupportMultipleWindows(true);
settings.setSupportZoom(false);
webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
// webView.setHorizontalScrollbarOverlay(true);
webView.setHorizontalScrollBarEnabled(true);
webView.requestFocus();
// webView.setBackgroundColor(getColor(R.color.black));
settings.setJavaScriptCanOpenWindowsAutomatically(false);
// 设置在WebView内部是否允许通过file url加载的 Js代码读取其他的本地文件
// Android 4.1前默认允许4.1后默认禁止
settings.setAllowFileAccessFromFileURLs(true);
// 设置WebView内部是否允许通过 file url 加载的 Javascript 可以访问其他的源(包括http、https等源)
// Android 4.1前默认允许4.1后默认禁止
settings.setAllowUniversalAccessFromFileURLs(true);
// settings.setBuiltInZoomControls(false); // 启用缩放功能
// settings.setDisplayZoomControls(false); // 隐藏缩放控件
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
WebView newWebView = new WebView(WebViewActivity.this);
topVvvv.setVisibility(View.VISIBLE);
webView.addView(newWebView);
WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
transport.setWebView(newWebView);
resultMsg.sendToTarget();
newWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// isAtGame = true;
progressBar.setVisibility(View.GONE);
webView.loadUrl(url);
return true;
}
});
// WebView newWebView = new WebView(view.getContext());
// newWebView.setWebViewClient(new WebViewClient() {
// @Override
// public boolean shouldOverrideUrlLoading(WebView view, String url) {
// Intent browserIntent = new Intent(WebViewActivity.this, WebViewActivity.class);
// browserIntent.putExtra("url", url);
// startActivity(browserIntent);
// return true;
// }
// });
// WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
// transport.setWebView(newWebView);
// resultMsg.sendToTarget();
// return true;
return true;
}
});
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
showTopLy.setVisibility(View.GONE);
progressBar.setVisibility(View.GONE);
topVvvv.setVisibility(View.VISIBLE);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
progressBar.setVisibility(View.VISIBLE);
topVvvv.setVisibility(View.VISIBLE);
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int errorCode = error.getErrorCode();
String errorMessage = error.getDescription().toString();
String currentUrl = request.getUrl().toString();
LogUtils.d("onReceivedError2 url==" + url + " errorCode ==" + errorCode);
if ((errorCode == -2 || errorCode == -6) && currentUrl.contains(url)) {
onShowErrorView(errorMessage);
} else {
onShowNetView();
}
}
progressBar.setVisibility(View.GONE);
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
LogUtils.d("onReceivedError2 url==" + failingUrl + " errorCode ==" + errorCode);
if ((errorCode == -2 || errorCode == -6) && failingUrl.contains(url)) {
onShowErrorView(description);
} else {
onShowNetView();
}
}
progressBar.setVisibility(View.GONE);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
uri = request.getUrl();
} else {
uri = Uri.parse(request.toString());
}
String url1 = uri.toString();
LogUtils.d("url1111111111==" + url1);
if (url1.equals(url + "index") || url1.equals(url + "/index")) {
topVvvv.setVisibility(View.GONE);
} else {
progressBar.setVisibility(View.VISIBLE);
}
if (!(url1.startsWith("http") || url1.startsWith("https"))) {
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url1));
startActivity(intent);
return true;
} catch (Exception e) {
e.printStackTrace();
}
} else {
if ((url1.equals(url + "index") || url1.equals(url + "/index")) && webView.canGoBack()) {
return false;
} else {
if (url1.contains(".apk")) { //下载
Toast.makeText(WebViewActivity.this, "下载开始,请稍后...", Toast.LENGTH_SHORT).show();
Constants.isUpdate = false;
new AppUpdater(WebViewActivity.this, url1).start();
return false;
}
//其它的该怎么处理就怎么处理
LogUtils.d("url1111111111==2" + url1);
webView.loadUrl(url1);
return true;
}
}
return false;
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url1) {
if (url1.equals(url + "index") || url1.equals(url + "/index")) {
topVvvv.setVisibility(View.GONE);
} else {
progressBar.setVisibility(View.VISIBLE);
}
if (!(url1.startsWith("http") || url1.startsWith("https"))) {
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url1));
startActivity(intent);
return true;
} catch (Exception e) {
e.printStackTrace();
}
} else {
if ((url1.equals(url + "index") || url1.equals(url + "/index")) && webView.canGoBack()) {
return false;
} else {
if (url1.contains(".apk")) { //下载
new AppUpdater(WebViewActivity.this, url1).start();
return false;
}
//其它的该怎么处理就怎么处理
webView.loadUrl(url1);
return false;
}
}
return false;
}
});
webView.setOnGenericMotionListener(new View.OnGenericMotionListener() {
@Override
public boolean onGenericMotion(View view, MotionEvent motionEvent) {
return false;
}
});
// webView.setOnTouchListener(new View.OnTouchListener() {
// @Override
// public boolean onTouch(View view, MotionEvent motionEvent) {
// if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
// webView.loadUrl("javascript:_IntervalResize(+\" + view + \",false)");
// return true;
// } else {
// return false;
// }
// }
// });
// webView.onWindowFocusChanged(true);
// webView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
// @Override
// public void onFocusChange(View view, boolean b) {
// LogUtils.d("B==" + b);
// view.invalidate();
// }
// });
if (url != null) {
webView.loadUrl(url);
}
}
private boolean isNetError = false;
public void onShowErrorView(String errorMsg) { //网络不可用的情况
topVvvv.setVisibility(View.VISIBLE);
webView.setVisibility(View.GONE);
layoutError.setVisibility(View.VISIBLE);
tvErrorMsg.setText(errorMsg);
showTopLy.setVisibility(View.GONE);
isNetError = true;
}
public void onShowNetView() {
topVvvv.setVisibility(View.VISIBLE);
webView.setVisibility(View.VISIBLE);
layoutError.setVisibility(View.GONE);
showTopLy.setVisibility(View.GONE);
isNetError = false;
}
public static String getString(Context context, String key, String defValue) {
if (context == null) {
return defValue;
}
SharedPreferences sp = context.getSharedPreferences("InitApp", Activity.MODE_PRIVATE);
return sp.getString(key, defValue);
}
@Override
public void onBackPressed() {
if (webView.canGoBack()) {//当webview有多级能返回的时候
String url = webView.getUrl();
String baseUrl = getString(WebViewActivity.this, "base_url", url);
if (baseUrl.equals(url + "index") || baseUrl.equals(url + "/index")) {
WebViewActivity.this.finish();
}
onShowNetView();
webView.goBack();
} else {//不能返回了
WebViewActivity.this.finish();
}
}
@Override
protected void onDestroy() {
if (webView != null) {
//加载null内容
// webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
//清除历史记录
// webView.clearHistory();
//移除WebView
// ((ViewGroup) webView.getParent()).removeView(webView);
//销毁VebView
webView.destroy();
}
super.onDestroy();
}
}

View File

@@ -0,0 +1,384 @@
package just.way.appdown;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.text.TextUtils;
import android.util.Log;
import just.way.appdown.callback.UpdateCallback;
import just.way.appdown.constant.Constants;
import just.way.appdown.http.HttpManager;
import just.way.appdown.http.IHttpManager;
import just.way.appdown.http.OkHttpManager;
import just.way.appdown.service.DownloadService;
import just.way.appdown.util.PermissionUtils;
import java.util.Map;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class AppUpdater {
/**
* {@link #mContext}不强制要求是{@link Activity},但能传{@link Activity}尽量传。AppUpdater本应该只专注于App更新尽量不涉及动态权限相关的处理。如果mContext传的是{@link Activity},则默认会校验一次动态权限。
*/
private Context mContext;
/**
* 配置信息
*/
private UpdateConfig mConfig;
/**
* 更新回调
*/
private UpdateCallback mCallback;
/**
* http管理接口可自定义实现。如使用okHttp
*/
private IHttpManager mHttpManager;
private ServiceConnection mServiceConnection;
public AppUpdater(@NonNull Context context,@NonNull UpdateConfig config){
this.mContext = context;
this.mConfig = config;
}
public AppUpdater(@NonNull Context context,@NonNull String url){
this.mContext = context;
mConfig = new UpdateConfig();
mConfig.setUrl(url);
}
/**
* 设置下载更新进度回调
* @param callback
* @return
*/
public AppUpdater setUpdateCallback(UpdateCallback callback){
this.mCallback = callback;
return this;
}
/**
* 设置一个IHttpManager
* @param httpManager AppUpdater内置提供{@link HttpManager} 和 {@link OkHttpManager}两种实现。
* 如果不设置,将默认使用{@link HttpManager},你也可以使用{@link OkHttpManager}或自己去实现一个
* {@link IHttpManager}。
* 当使用{@link OkHttpManager}时必需依赖okhttp库
* @return
*/
public AppUpdater setHttpManager(IHttpManager httpManager){
this.mHttpManager = httpManager;
return this;
}
/**
* 开始下载
*/
public void start(){
if(mConfig != null && !TextUtils.isEmpty(mConfig.getUrl())){
//如果mContext是Activity,并且配置了下载路径,则默认会校验一次动态权限。
if(mContext instanceof Activity && !TextUtils.isEmpty(mConfig.getPath())){
PermissionUtils.verifyReadAndWritePermissions((Activity) mContext,Constants.RE_CODE_STORAGE_PERMISSION);
}
if(mConfig.isShowNotification() && !PermissionUtils.isNotificationEnabled(mContext)){
Log.w(Constants.TAG,"Notification permission not enabled.");
}
startDownloadService();
}else{
throw new NullPointerException("Url must not be empty.");
}
}
/**
* 启动下载服务
*/
private void startDownloadService(){
Intent intent = new Intent(mContext, DownloadService.class);
if(mCallback!=null || mHttpManager!=null){//bindService
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
DownloadService.DownloadBinder binder = ((DownloadService.DownloadBinder)service);
binder.start(mConfig,mHttpManager,mCallback);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
mContext.getApplicationContext().bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);
}else{//startService
intent.putExtra(Constants.KEY_UPDATE_CONFIG,mConfig);
mContext.startService(intent);
}
}
/**
* 取消下载
*/
public void stop(){
stopDownloadService();
}
/**
* 停止下载服务
*/
private void stopDownloadService(){
Intent intent = new Intent(mContext, DownloadService.class);
intent.putExtra(Constants.KEY_STOP_DOWNLOAD_SERVICE,true);
mContext.startService(intent);
}
/**
* AppUpdater建造者
*/
public static class Builder{
private UpdateConfig mConfig;
public Builder(){
mConfig = new UpdateConfig();
}
/**
* 设置APK下载地址
* @param url 下载地址
* @return
*/
public Builder setUrl(@NonNull String url){
mConfig.setUrl(url);
return this;
}
/**
* 设置保存的路径,(建议使用默认,不做设置)
* @param path 下载保存的文件路径
* @return
* @deprecated 因为适配Android Q的分区存储所以此方法已弃用不建议再使用
*/
@Deprecated
public Builder setPath(String path){
mConfig.setPath(path);
return this;
}
/**
* 设置保存的文件名
* @param filename 下载的保存的apk文件名 默认优先取url文件名
* @return
*/
public Builder setFilename(String filename){
mConfig.setFilename(filename);
return this;
}
/**
* 设置是否显示通知栏
* @param isShowNotification 是否显示通知栏 默认true
* @return
*/
public Builder setShowNotification(boolean isShowNotification) {
mConfig.setShowNotification(isShowNotification);
return this;
}
/**
* 设置通知ID
* @param notificationId 通知ID
* @return
*/
public Builder setNotificationId(int notificationId) {
mConfig.setNotificationId(notificationId);
return this;
}
/**
* 设置通知渠道ID
* @param channelId 通知渠道ID 默认兼容O
* @return
*/
public Builder setChannelId(String channelId) {
mConfig.setChannelId(channelId);
return this;
}
/**
* 设置通知渠道名称
* @param channelName 通知渠道名称 默认兼容O
* @return
*/
public Builder setChannelName(String channelName) {
mConfig.setChannelName(channelName);
return this;
}
/**
* 设置通知图标
* @param icon 通知栏图标 默认取App的icon
* @return
*/
public Builder setNotificationIcon(@DrawableRes int icon) {
mConfig.setNotificationIcon(icon);
return this;
}
/**
* 设置通知是否震动提示
* @param vibrate 是否震动提示为true时使用通知默认震动Android O(8.0)以上设置只有初次创建channel时有效后续修改属性无效想要重新有效需修改channelId或卸载App重装。
* @return
*/
public Builder setVibrate(boolean vibrate) {
mConfig.setVibrate(vibrate);
return this;
}
/**
* 设置通知是否铃声提示
* @param sound 是否铃声提示为true时使用通知默认铃声Android O(8.0)以上设置只有初次创建channel时有效后续修改属性无效想要重新有效需修改channelId或卸载App重装。
* @return
*/
public Builder setSound(boolean sound) {
mConfig.setSound(sound);
return this;
}
/**
* 设置下载完成后知否自动触发安装APK
* @param isInstallApk 下载完成后是否自动调用安装APK默认true
* @return
*/
public Builder setInstallApk(boolean isInstallApk){
mConfig.setInstallApk(isInstallApk);
return this;
}
/**
* 设置FileProvider的authority
* @param authority FileProvider的authority默认兼容N默认值{@link Context#getPackageName() + ".AppUpdaterFileProvider"}
* @return
*/
public Builder setAuthority(String authority){
mConfig.setAuthority(authority);
return this;
}
/**
* 设置下载时,通知栏是否显示下载百分比
* @param showPercentage 下载时通知栏是否显示百分比
* @return
*/
public Builder setShowPercentage(boolean showPercentage) {
mConfig.setShowPercentage(showPercentage);
return this;
}
/**
* 设置下载失败时,是否支持点击通知栏重新下载。与之相关联的方法{@link #setReDownloads(int)}
* @param reDownload 下载失败时是否支持点击通知栏重新下载默认true
* @return
*/
public Builder setReDownload(boolean reDownload) {
mConfig.setReDownload(reDownload);
return this;
}
/**
* 设置下载失败时,最多重新下载次数。与之相关联的方法{@link #setReDownload(boolean)}
* @param reDownloads 下载失败时是否支持点击通知栏重新下载默认最多重新下载3次
* @return
*/
public Builder setReDownloads(int reDownloads) {
mConfig.setReDownloads(reDownloads);
return this;
}
/**
* 设置要下载APK的versionCode用于优先取缓存时通过versionCode校验APK文件是否一致。
* 缓存校验目前支持两种方式一种是通过versionCode校验即{@link #setVersionCode(Integer)}一种是文件MD5校验即{@link #setApkMD5(String)}。推荐使用MD5校验方式
* 如果两种方式都设置了则只校验MD5
* @param versionCode 为null表示不处理默认不存在则下载存在则重新下载。不为null时表示会优先校验本地是否存在已下载版本号为versionCode的APK。
* 如果存在则不会重新下载(AppUpdater会自动校验packageName一致性)直接取本地APK反之重新下载。
* @return
*/
public Builder setVersionCode(Integer versionCode) {
mConfig.setVersionCode(versionCode);
return this;
}
/**
* 设置APK文件的MD5用于优先取缓存时通过MD5校验文件APK是否一致。
* 缓存校验目前支持两种方式一种是通过versionCode校验即{@link #setVersionCode(Integer)}一种是文件MD5校验即{@link #setApkMD5(String)}。推荐使用MD5校验方式
* 如果两种方式都设置了则只校验MD5
* @param md5 为null表示不处理如果设置了MD5则缓存APK的MD5相同时只下载一次优先取本地缓存
* @return
*/
public Builder setApkMD5(String md5) {
mConfig.setApkMD5(md5);
return this;
}
/**
* 请求头添加参数
* @param key
* @param value
* @return
*/
public Builder addHeader(String key, String value){
mConfig.addHeader(key,value);
return this;
}
/**
* 请求头添加参数
* @param headers
* @return
*/
public Builder addHeader(Map<String,String> headers){
mConfig.addHeader(headers);
return this;
}
/**
* 设置是否自动删除取消下载的文件
* @param deleteCancelFile 是否删除取消下载的文件默认为true
*/
public Builder setDeleteCancelFile(boolean deleteCancelFile){
mConfig.setDeleteCancelFile(deleteCancelFile);
return this;
}
/**
* 是否支持通过删除通知栏来取消下载默认为false
* @param cancelDownload
* @return
*/
public Builder setCancelDownload(boolean cancelDownload){
mConfig.setCancelDownload(cancelDownload);
return this;
}
public AppUpdater build(@NonNull Context context){
return new AppUpdater(context,mConfig);
}
}
}

View File

@@ -0,0 +1,460 @@
package just.way.appdown;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import just.way.appdown.constant.Constants;
import java.util.HashMap;
import java.util.Map;
import androidx.annotation.DrawableRes;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class UpdateConfig implements Parcelable {
/**
* APK下载的Url
*/
private String mUrl;
/**
* 保存路径
*/
private String mPath;
/**
* 保存文件名
*/
private String mFilename;
/**
* 是否显示通知栏
*/
private boolean isShowNotification = true;
/**
* 下载完成后是否自动弹出安装
*/
private boolean isInstallApk = true;
/**
* 通知栏图标默认取app图标
*/
private int mNotificationIcon;
/**
* 通知栏ID
*/
private int mNotificationId = Constants.DEFAULT_NOTIFICATION_ID;
/**
* 通知栏渠道ID
*/
private String mChannelId;
/**
* 通知栏渠道名称
*/
private String mChannelName;
/**
* 默认{@link Context#getPackageName() + ".AppUpdaterFileProvider"}
*/
private String mAuthority;
/**
* 下载失败是否支持点击通知栏重新下载
*/
private boolean isReDownload = true;
/**
* 下载失败后,最大重新下载次数
*/
private int reDownloads = 3;
/**
* 是否显示百分比
*/
private boolean isShowPercentage = true;
/**
* 是否震动提示为true时使用通知默认震动
*/
private boolean isVibrate;
/**
* 是否铃声提示,为true时使用通知默认铃声
*/
private boolean isSound;
/**
* 要下载的APK的versionCode
*/
private Integer versionCode;
/**
* 请求头参数
*/
private Map<String,String> mRequestProperty;
/**
* 是否删除取消下载的文件
*/
private boolean isDeleteCancelFile = true;
/**
* 是否支持通过删除通知栏来取消下载
*/
private boolean isCancelDownload = false;
/**
* APK文件的MD5
*/
private String apkMD5;
public UpdateConfig() {
}
public String getUrl() {
return mUrl;
}
/**
* 设置APK下载地址
* @param url 下载地址
*/
public void setUrl(String url) {
this.mUrl = url;
}
public String getPath() {
return mPath;
}
/**
* 设置保存的路径,(建议使用默认,不做设置)
* @param path 下载保存的文件路径
* @return
* @deprecated 因为适配Android Q的分区存储所以此方法已弃用不建议再使用
*/
@Deprecated
public void setPath(String path) {
this.mPath = path;
}
public String getFilename() {
return mFilename;
}
/**
* 设置保存的文件名
* @param filename 下载的保存的apk文件名 默认优先取url文件名
*/
public void setFilename(String filename) {
this.mFilename = filename;
}
public boolean isShowNotification() {
return isShowNotification;
}
/**
* 设置是否显示通知栏
* @param isShowNotification 是否显示通知栏 默认true
*/
public void setShowNotification(boolean isShowNotification) {
this.isShowNotification = isShowNotification;
}
public String getChannelId() {
return mChannelId;
}
/**
* 设置通知渠道ID
* @param channelId 通知渠道ID 默认兼容O
*/
public void setChannelId(String channelId) {
this.mChannelId = channelId;
}
public String getChannelName() {
return mChannelName;
}
/**
* 设置通知渠道名称
* @param channelName 通知渠道名称 默认兼容O
*/
public void setChannelName(String channelName) {
this.mChannelName = channelName;
}
/**
* 设置通知ID
* @param notificationId 通知ID
*/
public void setNotificationId(int notificationId){
this.mNotificationId = notificationId;
}
public int getNotificationId(){
return this.mNotificationId;
}
/**
* 设置通知图标
* @param icon 通知栏图标 默认取App的icon
*/
public void setNotificationIcon(@DrawableRes int icon){
this.mNotificationIcon = icon;
}
public int getNotificationIcon(){
return this.mNotificationIcon;
}
public boolean isInstallApk() {
return isInstallApk;
}
/**
* 设置下载完成后知否自动触发安装APK
* @param isInstallApk 下载完成后是否自动调用安装APK默认true
*/
public void setInstallApk(boolean isInstallApk) {
this.isInstallApk = isInstallApk;
}
public String getAuthority() {
return mAuthority;
}
/**
* 设置FileProvider的authority
* @param authority FileProvider的authority默认兼容N默认值{@link Context#getPackageName() + ".AppUpdaterFileProvider"}
*/
public void setAuthority(String authority) {
this.mAuthority = authority;
}
public boolean isShowPercentage() {
return isShowPercentage;
}
/**
* 设置下载时,通知栏是否显示下载百分比
* @param showPercentage 下载时通知栏是否显示百分比
*/
public void setShowPercentage(boolean showPercentage) {
isShowPercentage = showPercentage;
}
public boolean isReDownload() {
return isReDownload;
}
/**
* 设置下载失败时,是否支持点击通知栏重新下载。与之相关联的方法{@link #setReDownloads(int)}
* @param reDownload 下载失败时是否支持点击通知栏重新下载默认true
*/
public void setReDownload(boolean reDownload) {
isReDownload = reDownload;
}
public int getReDownloads() {
return reDownloads;
}
/**
* 设置下载失败时,最多重新下载次数。与之相关联的方法{@link #setReDownload(boolean)}
* @param reDownloads 下载失败时是否支持点击通知栏重新下载默认最多重新下载3次
*/
public void setReDownloads(int reDownloads) {
this.reDownloads = reDownloads;
}
public boolean isVibrate() {
return isVibrate;
}
/**
* 设置通知是否震动提示
* @param vibrate 是否震动提示为true时使用通知默认震动Android O(8.0)以上设置只有初次创建channel时有效后续修改属性无效想要重新有效需修改channelId或卸载App重装。
*/
public void setVibrate(boolean vibrate) {
isVibrate = vibrate;
}
public boolean isSound() {
return isSound;
}
/**
* 设置通知是否铃声提示
* @param sound 是否铃声提示为true时使用通知默认铃声Android O(8.0)以上设置只有初次创建channel时有效后续修改属性无效想要重新有效需修改channelId或卸载App重装。
*/
public void setSound(boolean sound) {
isSound = sound;
}
public Integer getVersionCode(){
return versionCode;
}
/**
* 设置要下载APK的versionCode用于优先取缓存时通过versionCode校验APK文件是否一致。
* 缓存校验目前支持两种方式一种是通过versionCode校验即{@link #setVersionCode(Integer)}一种是文件MD5校验即{@link #setApkMD5(String)}。推荐使用MD5校验方式
* 如果两种方式都设置了则只校验MD5
* @param versionCode 为null表示不处理默认不存在则下载存在则重新下载。不为null时表示会优先校验本地是否存在已下载版本号为versionCode的APK。
* 如果存在则不会重新下载(AppUpdater会自动校验packageName一致性)直接取本地APK反之重新下载。
*/
public void setVersionCode(Integer versionCode){
this.versionCode = versionCode;
}
public Map<String, String> getRequestProperty() {
return mRequestProperty;
}
/**
* 设置APK文件的MD5用于优先取缓存时通过MD5校验文件APK是否一致。
* 缓存校验目前支持两种方式一种是通过versionCode校验即{@link #setVersionCode(Integer)}一种是文件MD5校验即{@link #setApkMD5(String)}。推荐使用MD5校验方式
* 如果两种方式都设置了则只校验MD5
* @param md5 为null表示不处理如果设置了MD5则缓存APK的MD5相同时只下载一次优先取本地缓存
*/
public void setApkMD5(String md5){
this.apkMD5 = md5;
}
public String getApkMD5(){
return apkMD5;
}
/**
* 请求头添加参数
* @param key
* @param value
*/
public void addHeader(String key, String value){
initRequestProperty();
mRequestProperty.put(key,value);
}
/**
* 请求头添加参数
* @param headers
*/
public void addHeader(Map<String,String> headers){
initRequestProperty();
mRequestProperty.putAll(headers);
}
private void initRequestProperty(){
if(mRequestProperty == null){
mRequestProperty = new HashMap<>();
}
}
public boolean isDeleteCancelFile() {
return isDeleteCancelFile;
}
/**
* 设置是否自动删除取消下载的文件
* @param deleteCancelFile 是否删除取消下载的文件默认为true
*/
public void setDeleteCancelFile(boolean deleteCancelFile) {
isDeleteCancelFile = deleteCancelFile;
}
public boolean isCancelDownload(){
return isCancelDownload;
}
/**
* 是否支持通过删除通知栏来取消下载默认为false
* @param cancelDownload
*/
public void setCancelDownload(boolean cancelDownload) {
isCancelDownload = cancelDownload;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.mUrl);
dest.writeString(this.mPath);
dest.writeString(this.mFilename);
dest.writeByte(this.isShowNotification ? (byte) 1 : (byte) 0);
dest.writeByte(this.isInstallApk ? (byte) 1 : (byte) 0);
dest.writeInt(this.mNotificationIcon);
dest.writeInt(this.mNotificationId);
dest.writeString(this.mChannelId);
dest.writeString(this.mChannelName);
dest.writeString(this.mAuthority);
dest.writeByte(this.isReDownload ? (byte) 1 : (byte) 0);
dest.writeInt(this.reDownloads);
dest.writeByte(this.isShowPercentage ? (byte) 1 : (byte) 0);
dest.writeByte(this.isVibrate ? (byte) 1 : (byte) 0);
dest.writeByte(this.isSound ? (byte) 1 : (byte) 0);
dest.writeValue(this.versionCode);
if(mRequestProperty!=null){
dest.writeInt(this.mRequestProperty.size());
for (Map.Entry<String, String> entry : this.mRequestProperty.entrySet()) {
dest.writeString(entry.getKey());
dest.writeString(entry.getValue());
}
}else{
dest.writeInt(0);
}
dest.writeByte(this.isDeleteCancelFile ? (byte) 1 : (byte) 0);
dest.writeByte(this.isCancelDownload ? (byte) 1 : (byte) 0);
dest.writeString(this.apkMD5);
}
protected UpdateConfig(Parcel in) {
this.mUrl = in.readString();
this.mPath = in.readString();
this.mFilename = in.readString();
this.isShowNotification = in.readByte() != 0;
this.isInstallApk = in.readByte() != 0;
this.mNotificationIcon = in.readInt();
this.mNotificationId = in.readInt();
this.mChannelId = in.readString();
this.mChannelName = in.readString();
this.mAuthority = in.readString();
this.isReDownload = in.readByte() != 0;
this.reDownloads = in.readInt();
this.isShowPercentage = in.readByte() != 0;
this.isVibrate = in.readByte() != 0;
this.isSound = in.readByte() != 0;
this.versionCode = (Integer) in.readValue(Integer.class.getClassLoader());
int mRequestPropertySize = in.readInt();
this.mRequestProperty = new HashMap<>(mRequestPropertySize);
for (int i = 0; i < mRequestPropertySize; i++) {
String key = in.readString();
String value = in.readString();
this.mRequestProperty.put(key, value);
}
this.isDeleteCancelFile = in.readByte() != 0;
this.isCancelDownload = in.readByte() != 0;
this.apkMD5 = in.readString();
}
public static final Creator<UpdateConfig> CREATOR = new Creator<UpdateConfig>() {
@Override
public UpdateConfig createFromParcel(Parcel source) {
return new UpdateConfig(source);
}
@Override
public UpdateConfig[] newArray(int size) {
return new UpdateConfig[size];
}
};
}

View File

@@ -0,0 +1,26 @@
package just.way.appdown.callback;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public abstract class AppUpdateCallback implements UpdateCallback {
@Override
public void onDownloading(boolean isDownloading) {
}
@Override
public void onStart(String url) {
}
@Override
public void onError(Exception e) {
}
@Override
public void onCancel() {
}
}

View File

@@ -0,0 +1,46 @@
package just.way.appdown.callback;
import java.io.File;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface UpdateCallback {
/**
* 最开始调用(在onStart之前调用)
* @param isDownloading true 表示已经在下载false表示当前未开始下载即将开始下载
*/
void onDownloading(boolean isDownloading);
/**
* 开始
*/
void onStart(String url);
/**
* 加载进度…
* @param progress
* @param total
* @param isChange 进度百分比是否有改变,(主要可以用来过滤无用的刷新,从而降低刷新频率)
*/
void onProgress(long progress,long total,boolean isChange);
/**
* 完成
* @param file
*/
void onFinish(File file);
/**
* 错误
* @param e
*/
void onError(Exception e);
/**
* 取消
*/
void onCancel();
}

View File

@@ -0,0 +1,32 @@
package just.way.appdown.constant;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public final class Constants {
public static final String TAG = "AppUpdater";
public static final String KEY_UPDATE_CONFIG = "app_update_config";
public static final int DEFAULT_NOTIFICATION_ID = 0x66;
public static final String DEFAULT_NOTIFICATION_CHANNEL_ID = "0x66";
public static final String DEFAULT_NOTIFICATION_CHANNEL_NAME = "AppUpdater";
public static final String KEY_STOP_DOWNLOAD_SERVICE = "stop_download_service";
public static final String KEY_RE_DOWNLOAD = "app_update_re_download";
public static final int RE_CODE_STORAGE_PERMISSION = 0x66;
public static final int NONE = -1;
public static final String DEFAULT_FILE_PROVIDER = ".appdown.provider.AppUpdaterFileProvider";
public static final String DEFAULT_DIR = "apk";
public static boolean isUpdate = true;
}

View File

@@ -0,0 +1,240 @@
package just.way.appdown.http;
import android.os.AsyncTask;
import android.os.Build;
import just.way.appdown.util.LogUtils;
import just.way.appdown.util.SSLSocketFactoryUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import androidx.annotation.Nullable;
/**
* HttpManager使用 {@link HttpURLConnection} 实现的 {@link IHttpManager}
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class HttpManager implements IHttpManager {
private static final int HTTP_TEMP_REDIRECT = 307;
private static final int HTTP_PERM_REDIRECT = 308;
private static final int DEFAULT_TIME_OUT = 20000;
private int mTimeout;
private DownloadTask mDownloadTask;
private static volatile HttpManager INSTANCE;
public static HttpManager getInstance(){
if(INSTANCE == null){
synchronized (HttpManager.class){
if(INSTANCE == null){
INSTANCE = new HttpManager();
}
}
}
return INSTANCE;
}
private HttpManager(){
this(DEFAULT_TIME_OUT);
}
/**
* HttpManager对外暴露。如果没有特殊需求推荐使用{@link HttpManager#getInstance()}
*/
public HttpManager(int timeout){
this.mTimeout = timeout;
}
@Override
public void download(String url, String path, String filename, @Nullable Map<String,String> requestProperty, DownloadCallback callback) {
mDownloadTask = new DownloadTask(url, path, filename, mTimeout, requestProperty, callback);
mDownloadTask.execute();
}
@Override
public void cancel() {
if(mDownloadTask != null){
mDownloadTask.isCancel = true;
}
}
/**
* 异步下载任务
*/
private static class DownloadTask extends AsyncTask<Void,Long,File> {
private String url;
private String path;
private String filename;
private Map<String,String> requestProperty;
private DownloadCallback callback;
private Exception exception;
private int timeout;
private volatile boolean isCancel;
public DownloadTask(String url, String path, String filename, int timeout, @Nullable Map<String,String> requestProperty, DownloadCallback callback){
this.url = url;
this.path = path;
this.filename = filename;
this.timeout = timeout;
this.callback = callback;
this.requestProperty = requestProperty;
}
private File download(String url) throws Exception{
HttpURLConnection connect = (HttpURLConnection)new URL(url).openConnection();
connect.setRequestMethod("GET");
connect.setRequestProperty("Accept-Encoding", "identity");
connect.setReadTimeout(timeout);
connect.setConnectTimeout(timeout);
if(requestProperty != null){
for(Map.Entry<String,String> entry : requestProperty.entrySet()){
connect.setRequestProperty(entry.getKey(),entry.getValue());
}
}
connect.connect();
LogUtils.d("Content-Type:" + connect.getContentType());
int responseCode = connect.getResponseCode();
switch (responseCode){
case HttpURLConnection.HTTP_OK: {
InputStream is = connect.getInputStream();
long length = connect.getContentLength();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
length = connect.getContentLengthLong();
}
LogUtils.d( "contentLength:" + length);
long progress = 0;
byte[] buffer = new byte[8192];
int len;
File file = new File(path, filename);
FileOutputStream fos = new FileOutputStream(file);
while ((len = is.read(buffer)) != -1) {
if (isCancel) {
cancel(true);
break;
}
fos.write(buffer, 0, len);
progress += len;
// 更新进度
if (length > 0) {
publishProgress(progress, length);
}
}
fos.flush();
fos.close();
is.close();
connect.disconnect();
if(progress <= 0 && length <= 0){
throw new IllegalStateException(String.format("contentLength = %d",length));
}
return file;
}
case HttpURLConnection.HTTP_MULT_CHOICE:
case HttpURLConnection.HTTP_MOVED_PERM:
case HttpURLConnection.HTTP_MOVED_TEMP:
case HttpURLConnection.HTTP_SEE_OTHER:
case HTTP_TEMP_REDIRECT:
case HTTP_PERM_REDIRECT: {// 重定向
String redirectUrl = connect.getHeaderField("Location");
LogUtils.d("redirectUrl = " + redirectUrl);
connect.disconnect();
return download(redirectUrl);
}
default://连接失败
throw new ConnectException(String.format("responseCode = %d",responseCode));
}
}
@Override
protected File doInBackground(Void... voids) {
try{
HttpsURLConnection.setDefaultSSLSocketFactory(SSLSocketFactoryUtils.createSSLSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(SSLSocketFactoryUtils.createTrustAllHostnameVerifier());
return download(url);
} catch (Exception e) {
this.exception = e;
e.printStackTrace();
}
return null;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
if(callback != null){
callback.onStart(url);
}
}
@Override
protected void onPostExecute(File file) {
super.onPostExecute(file);
if(callback != null){
if(file != null){
callback.onFinish(file);
}else{
callback.onError(exception);
}
}
}
@Override
protected void onProgressUpdate(Long... values) {
super.onProgressUpdate(values);
if(callback != null){
if(!isCancelled()){
callback.onProgress(values[0],values[1]);
}
}
}
@Override
protected void onCancelled() {
super.onCancelled();
if(callback != null){
callback.onCancel();
}
}
}
}

View File

@@ -0,0 +1,64 @@
package just.way.appdown.http;
import java.io.File;
import java.io.Serializable;
import java.util.Map;
import androidx.annotation.Nullable;
/**
* IHttpManager 默认提供 {@link HttpManager} 和 {@link OkHttpManager} 两种实现。
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface IHttpManager {
/**
* 下载
* @param url
* @param path
* @param filename
* @param requestProperty
* @param callback
*/
void download(String url, String path, String filename, @Nullable Map<String,String> requestProperty, DownloadCallback callback);
/**
* 取消下载
*/
void cancel();
interface DownloadCallback extends Serializable{
/**
* 开始
* @param url
*/
void onStart(String url);
/**
* 加载进度…
* @param progress
* @param total
*/
void onProgress(long progress,long total);
/**
* 完成
* @param file
*/
void onFinish(File file);
/**
* 错误
* @param e
*/
void onError(Exception e);
/**
* 取消
*/
void onCancel();
}
}

View File

@@ -0,0 +1,235 @@
package just.way.appdown.http;
import android.os.AsyncTask;
import just.way.appdown.util.LogUtils;
import just.way.appdown.util.SSLSocketFactoryUtils;
import org.apache.http.conn.ssl.SSLSocketFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.ConnectException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
/**
* OkHttpManager使用 {@link OkHttpClient} 实现的 {@link IHttpManager}
* <p>使用 OkHttpManager 时必须依赖 OkHttp 库
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class OkHttpManager implements IHttpManager {
private static final int DEFAULT_TIME_OUT = 20000;
private OkHttpClient okHttpClient;
private DownloadTask mDownloadTask;
private static volatile OkHttpManager INSTANCE;
public static OkHttpManager getInstance(){
if(INSTANCE == null){
synchronized (HttpManager.class){
if(INSTANCE == null){
INSTANCE = new OkHttpManager();
}
}
}
return INSTANCE;
}
private OkHttpManager(){
this(DEFAULT_TIME_OUT);
}
/**
* HttpManager对外暴露。如果没有特殊需求推荐使用{@link HttpManager#getInstance()}
* @param timeout 超时时间,单位:毫秒
*/
public OkHttpManager(int timeout){
this(new OkHttpClient.Builder()
.readTimeout(timeout, TimeUnit.MILLISECONDS)
.connectTimeout(timeout, TimeUnit.MILLISECONDS)
.sslSocketFactory(SSLSocketFactoryUtils.createSSLSocketFactory(),SSLSocketFactoryUtils.createTrustAllManager())
.hostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
.build());
}
/**
* HttpManager对外暴露推荐使用{@link HttpManager#getInstance()}
* @param okHttpClient {@link OkHttpClient}
*/
public OkHttpManager(@NonNull OkHttpClient okHttpClient){
this.okHttpClient = okHttpClient;
}
@Override
public void download(String url,final String path,final String filename, @Nullable Map<String, String> requestProperty,final DownloadCallback callback) {
mDownloadTask = new DownloadTask(okHttpClient, url, path, filename, requestProperty, callback);
mDownloadTask.execute();
}
@Override
public void cancel() {
if(mDownloadTask != null){
mDownloadTask.isCancel = true;
}
}
/**
* 异步下载任务
*/
private static class DownloadTask extends AsyncTask<Void,Long,File> {
private String url;
private String path;
private String filename;
private Map<String,String> requestProperty;
private DownloadCallback callback;
private Exception exception;
private OkHttpClient okHttpClient;
private volatile boolean isCancel;
public DownloadTask(OkHttpClient okHttpClient,String url, String path, String filename ,@Nullable Map<String,String> requestProperty, DownloadCallback callback){
this.okHttpClient = okHttpClient;
this.url = url;
this.path = path;
this.filename = filename;
this.callback = callback;
this.requestProperty = requestProperty;
}
@Override
protected File doInBackground(Void... voids) {
try{
Request.Builder builder = new Request.Builder()
.url(url)
.addHeader("Accept-Encoding", "identity")
.get();
if(requestProperty!=null){
for(Map.Entry<String,String> entry : requestProperty.entrySet()){
builder.addHeader(entry.getKey(),entry.getValue());
}
}
Call call = okHttpClient.newCall(builder.build());
Response response = call.execute();
if(response.isSuccessful()){
InputStream is = response.body().byteStream();
long length = response.body().contentLength();
LogUtils.d("contentLength:" + length);
long progress = 0;
byte[] buffer = new byte[8192];
int len;
File file = new File(path,filename);
FileOutputStream fos = new FileOutputStream(file);
while ((len = is.read(buffer)) != -1){
if(isCancel){
if(call != null){
call.cancel();
}
cancel(true);
break;
}
fos.write(buffer,0,len);
progress += len;
// 更新进度
if(length > 0){
publishProgress(progress,length);
}
}
fos.flush();
fos.close();
is.close();
response.close();
if(progress <= 0 && length <= 0){
throw new IllegalStateException(String.format("contentLength = %d",length));
}
return file;
}else {// 连接失败
throw new ConnectException(String.format("responseCode = %d",response.code()));
}
}catch (Exception e){
this.exception = e;
e.printStackTrace();
}
return null;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
if(callback != null){
callback.onStart(url);
}
}
@Override
protected void onPostExecute(File file) {
super.onPostExecute(file);
if(callback != null){
if(file != null){
callback.onFinish(file);
}else{
callback.onError(exception);
}
}
}
@Override
protected void onProgressUpdate(Long... values) {
super.onProgressUpdate(values);
if(callback != null){
if(!isCancelled()){
callback.onProgress(values[0],values[1]);
}
}
}
@Override
protected void onCancelled() {
super.onCancelled();
if(callback != null){
callback.onCancel();
}
}
}
}

View File

@@ -0,0 +1,25 @@
package just.way.appdown.notify;
import android.content.Context;
import just.way.appdown.UpdateConfig;
import java.io.File;
import androidx.annotation.DrawableRes;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public interface INotification {
void onStart(Context context, int notifyId, String channelId, String channelName, @DrawableRes int icon, CharSequence title, CharSequence content, boolean isVibrate, boolean isSound, boolean isCancelDownload);
void onProgress(Context context, int notifyId, String channelId, @DrawableRes int icon, CharSequence title, CharSequence content, int progress, int size, boolean isCancelDownload);
void onFinish(Context context, int notifyId, String channelId, @DrawableRes int icon, CharSequence title, CharSequence content, File file, String authority);
void onError(Context context, int notifyId, String channelId, @DrawableRes int icon, CharSequence title, CharSequence content, boolean isReDownload, UpdateConfig config);
void onCancel(Context context, int notifyId);
}

View File

@@ -0,0 +1,38 @@
package just.way.appdown.notify;
import android.content.Context;
import just.way.appdown.UpdateConfig;
import just.way.appdown.util.NotificationUtils;
import java.io.File;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class NotificationImpl implements INotification {
@Override
public void onStart(Context context, int notifyId, String channelId, String channelName, int icon, CharSequence title, CharSequence content, boolean isVibrate, boolean isSound, boolean isCancelDownload) {
NotificationUtils.showStartNotification(context, notifyId, channelId, channelName, icon, title, content, isVibrate, isSound, isCancelDownload);
}
@Override
public void onProgress(Context context, int notifyId, String channelId, int icon, CharSequence title, CharSequence content, int progress, int size, boolean isCancelDownload) {
NotificationUtils.showProgressNotification(context, notifyId, channelId, icon, title, content, progress, size, isCancelDownload);
}
@Override
public void onFinish(Context context, int notifyId, String channelId, int icon, CharSequence title, CharSequence content, File file, String authority) {
NotificationUtils.showFinishNotification(context, notifyId, channelId, icon, title, content, file, authority);
}
@Override
public void onError(Context context, int notifyId, String channelId, int icon, CharSequence title, CharSequence content, boolean isReDownload, UpdateConfig config) {
NotificationUtils.showErrorNotification(context, notifyId, channelId, icon, title, content, isReDownload, config);
}
@Override
public void onCancel(Context context, int notifyId) {
NotificationUtils.cancelNotification(context, notifyId);
}
}

View File

@@ -0,0 +1,11 @@
package just.way.appdown.provider;
import androidx.core.content.FileProvider;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class AppUpdaterFileProvider extends FileProvider {
}

View File

@@ -0,0 +1,456 @@
package just.way.appdown.service;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.text.TextUtils;
import just.way.R;
import just.way.appdown.UpdateConfig;
import just.way.appdown.callback.UpdateCallback;
import just.way.appdown.constant.Constants;
import just.way.appdown.http.HttpManager;
import just.way.appdown.http.IHttpManager;
import just.way.appdown.notify.INotification;
import just.way.appdown.notify.NotificationImpl;
import just.way.appdown.util.AppUtils;
import just.way.appdown.util.LogUtils;
import java.io.File;
import java.util.Locale;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.core.content.ContextCompat;
/**
* 下载服务
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class DownloadService extends Service {
private DownloadBinder mDownloadBinder = new DownloadBinder();
/**
* 是否在下载,防止重复下载。
*/
private boolean isDownloading;
/**
* 失败后重新下载次数
*/
private int mCount = 0;
/**
* Http管理器
*/
private IHttpManager mHttpManager;
private File mApkFile;
private Context getContext(){
return this;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(intent != null){
boolean isStop = intent.getBooleanExtra(Constants.KEY_STOP_DOWNLOAD_SERVICE,false);
if(isStop){
stopDownload();
} else if(!isDownloading){
//是否实通过通知栏触发重复下载
boolean isReDownload = intent.getBooleanExtra(Constants.KEY_RE_DOWNLOAD,false);
if(isReDownload){
mCount++;
}
//获取配置信息
UpdateConfig config = intent.getParcelableExtra(Constants.KEY_UPDATE_CONFIG);
startDownload(config,null,null, new NotificationImpl());
}else{
LogUtils.w("Please do not duplicate downloads.");
}
}
return super.onStartCommand(intent, flags, startId);
}
//----------------------------------------
/**
* 开始下载
* @param config
* @param httpManager
* @param callback
*/
public void startDownload(@NonNull UpdateConfig config,@Nullable IHttpManager httpManager,@Nullable UpdateCallback callback, @Nullable INotification notification){
if(callback != null){
callback.onDownloading(isDownloading);
}
if(isDownloading){
LogUtils.w("Please do not duplicate downloads.");
return;
}
String url = config.getUrl();
String path = config.getPath();
String filename = config.getFilename();
//如果保存路径为空则使用缓存路径
if(TextUtils.isEmpty(path)){
path = getCacheFilesDir(getContext());
}
File dirFile = new File(path);
if(!dirFile.exists()){
dirFile.mkdirs();
}
//如果文件名为空则使用路径
if(TextUtils.isEmpty(filename)){
filename = AppUtils.getAppFullName(getContext(),url,getResources().getString(R.string.app_name));
}
mApkFile = new File(path, filename);
if(mApkFile.exists()){//文件是否存在
Integer versionCode = config.getVersionCode();
String apkMD5 = config.getApkMD5();
//是否存在相同的apk
boolean isExistApk = false;
if(!TextUtils.isEmpty(apkMD5)){//如果存在MD5则优先校验MD5
LogUtils.d(String.format(Locale.getDefault(),"UpdateConfig.apkMD5:%s",apkMD5));
isExistApk = AppUtils.checkFileMD5(mApkFile,apkMD5);
}else if(versionCode != null){//如果存在versionCode则校验versionCode
LogUtils.d(String.format(Locale.getDefault(),"UpdateConfig.versionCode:%d",versionCode));
isExistApk = AppUtils.apkExists(getContext(),versionCode,mApkFile);
}
if(isExistApk){
//本地已经存在要下载的APK
LogUtils.d("CacheFile:" + mApkFile);
if(config.isInstallApk()){
String authority = config.getAuthority();
if(TextUtils.isEmpty(authority)){//如果为空则默认
authority = AppUtils.getFileProviderAuthority(getContext());
}
AppUtils.installApk(getContext(), mApkFile, authority);
}
if(callback != null){
callback.onFinish(mApkFile);
}
stopService();
return;
}
//删除旧文件
mApkFile.delete();
}
LogUtils.d("File:" + mApkFile);
mHttpManager = httpManager != null ? httpManager : HttpManager.getInstance();
IHttpManager.DownloadCallback downloadCallback = new AppDownloadCallback(getContext(),this, config, mApkFile, callback, notification);
mHttpManager.download(url,path,filename,config.getRequestProperty(), downloadCallback);
}
/**
* 停止下载
*/
public void stopDownload(){
if(mHttpManager != null){
mHttpManager.cancel();
}
}
/**
* 获取缓存路径
* @param context
* @return
*/
private String getCacheFilesDir(Context context) {
File[] files = ContextCompat.getExternalFilesDirs(context, Constants.DEFAULT_DIR);
if(files != null && files.length > 0){
return files[0].getAbsolutePath();
}
File externalFilesDir = context.getExternalFilesDir(Constants.DEFAULT_DIR);
if(externalFilesDir != null){
return externalFilesDir.getAbsolutePath();
}
return new File(context.getFilesDir(), Constants.DEFAULT_DIR).getAbsolutePath();
}
/**
* 停止服务
*/
private void stopService(){
mCount = 0;
stopSelf();
}
//---------------------------------------- DownloadCallback
/**
* App下载回调接口
*/
public static class AppDownloadCallback implements IHttpManager.DownloadCallback {
private Context context;
private DownloadService downloadService;
public UpdateConfig config;
private boolean isShowNotification;
private int notifyId;
private String channelId;
private String channelName;
private int notificationIcon;
private boolean isInstallApk;
private String authority;
private boolean isShowPercentage;
private boolean isReDownload;
private boolean isDeleteCancelFile;
private boolean isCancelDownload;
private UpdateCallback callback;
private INotification notification;
/**
* 最后更新进度,用来降频刷新
*/
private int lastProgress;
/**
* 最后进度更新时间,用来降频刷新
*/
private long lastTime;
/**
* APK文件
*/
private File apkFile;
private AppDownloadCallback(Context context, DownloadService downloadService, UpdateConfig config, File apkFile, UpdateCallback callback, INotification notification){
this.context = context;
this.downloadService = downloadService;
this.config = config;
this.apkFile = apkFile;
this.callback = callback;
this.notification = notification;
this.isShowNotification = config.isShowNotification();
this.notifyId = config.getNotificationId();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
this.channelId = TextUtils.isEmpty(config.getChannelId()) ? Constants.DEFAULT_NOTIFICATION_CHANNEL_ID : config.getChannelId();
this.channelName = TextUtils.isEmpty(config.getChannelName()) ? Constants.DEFAULT_NOTIFICATION_CHANNEL_NAME : config.getChannelName();
}
if(config.getNotificationIcon() <= 0){
this.notificationIcon = AppUtils.getAppIcon(context);
}else{
this.notificationIcon = config.getNotificationIcon();
}
this.isInstallApk = config.isInstallApk();
this.authority = config.getAuthority();
if(TextUtils.isEmpty(config.getAuthority())){//如果为空则默认
authority = AppUtils.getFileProviderAuthority(context);
}
this.isShowPercentage = config.isShowPercentage();
this.isDeleteCancelFile = config.isDeleteCancelFile();
this.isCancelDownload = config.isCancelDownload();
//支持下载失败时重新下载,当重新下载次数不超过限制时才被允许
this.isReDownload = config.isReDownload() && downloadService.mCount < config.getReDownloads();
}
@Override
public void onStart(String url) {
LogUtils.i("url:" + url);
downloadService.isDownloading = true;
lastProgress = 0;
if(isShowNotification && notification != null){
String title="";
if(Constants.isUpdate){
title = getString(R.string.app_updater_start_notification_title);
}else{
title = getString(R.string.app_updater_start_notification_title_2);
}
notification.onStart(context,notifyId,channelId,channelName,notificationIcon,title,getString(R.string.app_updater_start_notification_content),config.isVibrate(),config.isSound(),isCancelDownload);
}
if(callback != null){
callback.onStart(url);
}
}
@Override
public void onProgress(long progress, long total) {
boolean isChange = false;
long curTime = System.currentTimeMillis();
if(lastTime + 200 < curTime || progress == total) {//降低更新频率
lastTime = curTime;
int currProgress = Math.round(progress * 1.0f / total * 100.0f);
if(currProgress != lastProgress){//百分比改变了才更新
isChange = true;
lastProgress = currProgress;
String percentage = currProgress + "%";
LogUtils.i(String.format(Locale.getDefault(),"%s \t(%d/%d)", percentage, progress, total));
if(isShowNotification && notification != null) {
String content = context.getString(R.string.app_updater_progress_notification_content);
if (isShowPercentage) {
content += percentage;
}
String title="";
if(Constants.isUpdate){
title = getString(R.string.app_updater_progress_notification_title);
}else{
title = getString(R.string.app_updater_progress_notification_title_2);
}
notification.onProgress(context,notifyId, channelId, notificationIcon, title, content, currProgress, 100,isCancelDownload);
}
}
}
if(callback != null){
callback.onProgress(progress,total,isChange);
}
}
@Override
public void onFinish(File file) {
LogUtils.d("File:" + file);
downloadService.isDownloading = false;
if(isShowNotification && notification != null){
notification.onFinish(context,notifyId,channelId,notificationIcon,getString(R.string.app_updater_finish_notification_title),getString(R.string.app_updater_finish_notification_content),file,authority);
}
if(isInstallApk){
AppUtils.installApk(context,file,authority);
}
if(callback != null){
callback.onFinish(file);
}
downloadService.stopService();
}
@Override
public void onError(Exception e) {
LogUtils.w(e.getMessage());
downloadService.isDownloading = false;
if(isShowNotification && notification != null){
String content = isReDownload ? getString(R.string.app_updater_error_notification_content_re_download) : getString(R.string.app_updater_error_notification_content);
notification.onError(context,notifyId,channelId,notificationIcon,getString(R.string.app_updater_error_notification_title),content,isReDownload,config);
}
if(callback != null){
callback.onError(e);
}
if(!isReDownload){
downloadService.stopService();
}
}
@Override
public void onCancel() {
LogUtils.d("Cancel download.");
downloadService.isDownloading = false;
if(isShowNotification && notification != null){
notification.onCancel(context,notifyId);
}
if(callback != null){
callback.onCancel();
}
if(isDeleteCancelFile && apkFile != null){
apkFile.delete();
}
downloadService.stopService();
}
private String getString(@StringRes int resId){
return context.getString(resId);
}
}
@Override
public void onDestroy() {
isDownloading = false;
mHttpManager = null;
super.onDestroy();
}
//---------------------------------------- Binder
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mDownloadBinder;
}
/**
* 提供绑定服务的方式进行下载
*/
public class DownloadBinder extends Binder {
/**
* 开始下载
* @param config {@link UpdateConfig}
*/
public void start(@NonNull UpdateConfig config){
start(config,null);
}
/**
* 开始下载
* @param config {@link UpdateConfig}
* @param callback {@link UpdateCallback}
*/
public void start(@NonNull UpdateConfig config, @Nullable UpdateCallback callback){
start(config,null, callback);
}
/**
* 开始下载
* @param config {@link UpdateConfig}
* @param httpManager {@link IHttpManager}
* @param callback {@link UpdateCallback}
*/
public void start(@NonNull UpdateConfig config, @Nullable IHttpManager httpManager, @Nullable UpdateCallback callback){
start(config, httpManager, callback, new NotificationImpl());
}
/**
* 开始下载
* @param config {@link UpdateConfig}
* @param httpManager {@link IHttpManager}
* @param callback {@link UpdateCallback}
* @param notification {@link INotification}
*/
public void start(@NonNull UpdateConfig config, @Nullable IHttpManager httpManager, @Nullable UpdateCallback callback,@NonNull INotification notification){
startDownload(config, httpManager, callback, notification);
}
}
}

View File

@@ -0,0 +1,264 @@
package just.way.appdown.util;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import androidx.core.content.FileProvider;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import just.way.appdown.constant.Constants;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public final class AppUtils {
private AppUtils(){
throw new AssertionError();
}
/**
* 通过url获取App的全名称
* @param context
* @return AppName.apk
*/
public static String getAppFullName(Context context,String url,String defaultName){
if(url.endsWith(".apk")){
String apkName = url.substring(url.lastIndexOf("/") + 1);
if(apkName.length() <= 64){
return apkName;
}
}
String filename = getAppName(context);
Log.d(Constants.TAG, "AppName:" + filename);
if(TextUtils.isEmpty(filename)){
filename = defaultName;
}
if(filename.endsWith(".apk")){
return filename;
}
return String.format("%s.apk",filename);
}
/**
* 获取包信息
* @param context
* @return
* @throws PackageManager.NameNotFoundException
*/
public static PackageInfo getPackageInfo(Context context) throws PackageManager.NameNotFoundException {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
return packageInfo;
}
/**
* 通过APK路径获取包信息
* @param context
* @param archiveFilePath
* @return
*/
public static PackageInfo getPackageInfo(Context context, String archiveFilePath) {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageArchiveInfo(archiveFilePath, PackageManager.GET_ACTIVITIES);
return packageInfo;
}
/**
* 获取App的名称
*/
public static String getAppName(Context context) {
try{
int labelRes = getPackageInfo(context).applicationInfo.labelRes;
return context.getResources().getString(labelRes);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 获取App的图标
* @param context
* @return
*/
public static int getAppIcon(Context context){
try{
return getPackageInfo(context).applicationInfo.icon;
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
/**
* 安装apk
* @param context
* @param file
*/
public static void installApk(Context context,File file,String authority){
Intent intent = getInstallIntent(context,file,authority);
context.startActivity(intent);
}
/**
* 获取安装Intent
* @param context
* @param file
* @param authority
* @return
*/
public static Intent getInstallIntent(Context context,File file,String authority){
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addCategory(Intent.CATEGORY_DEFAULT);
Uri uriData;
String type = "application/vnd.android.package-archive";
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
uriData = FileProvider.getUriForFile(context, authority, file);
}else{
uriData = Uri.fromFile(file);
}
intent.setDataAndType(uriData, type);
return intent;
}
/**
* APK是否存在
* @param context
* @param versionCode
* @param file
* @return
* @throws Exception
*/
public static boolean apkExists(Context context,int versionCode,File file){
if(file != null && file.exists()){
String packageName = context.getPackageName();
PackageInfo packageInfo = AppUtils.getPackageInfo(context,file.getAbsolutePath());
if(packageInfo != null){// 比对versionCode
Log.d(Constants.TAG,String.format("ApkVersionCode:%d",packageInfo.versionCode));
if(versionCode == packageInfo.versionCode){
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
if(applicationInfo != null && packageName.equals(applicationInfo.packageName)){//比对packageName
return true;
}
}
}
}
return false;
}
/**
* 判断文件是否存在
* @param context
* @param path
* @return
*/
public static boolean isAndroidQFileExists(Context context,String path){
return isAndroidQFileExists(context,new File(path));
}
/**
* 判断文件是否存在
* @param context
* @param file
* @return
*/
public static boolean isAndroidQFileExists(Context context,File file){
AssetFileDescriptor descriptor = null;
ContentResolver contentResolver = context.getContentResolver();
try {
Uri uri = Uri.fromFile(file);
descriptor = contentResolver.openAssetFileDescriptor(uri, "r");
if (descriptor == null) {
return false;
} else {
close(descriptor);
}
return true;
} catch (FileNotFoundException e) {
}finally {
close(descriptor);
}
return false;
}
/**
* 校验文件MD5
* @param file
* @param md5
* @return
*/
public static boolean checkFileMD5(File file,String md5){
String fileMD5 = getFileMD5(file);
Log.d(Constants.TAG,"FileMD5:"+ fileMD5);
if(!TextUtils.isEmpty(md5)){
return md5.equalsIgnoreCase(fileMD5);
}
return false;
}
/**
* 获取文件MD5
* @param file
* @return
*/
public static String getFileMD5(File file){
try {
FileInputStream fis = new FileInputStream(file);
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] buffer = new byte[1024];
int length;
while ((length = fis.read(buffer)) != -1){
messageDigest.update(buffer,0,length);
}
BigInteger bigInteger = new BigInteger(1,messageDigest.digest());
return bigInteger.toString(16);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String getFileProviderAuthority(Context context){
return context.getPackageName() + Constants.DEFAULT_FILE_PROVIDER;
}
/**
* 关闭
* @param descriptor
*/
private static void close(AssetFileDescriptor descriptor){
if(descriptor != null){
try {
descriptor.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

View File

@@ -0,0 +1,316 @@
/*
Copyright © 2015, 2016 Jenly Yu <a href="mailto:jenly1314@gmail.com">Jenly</a>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package just.way.appdown.util;
import android.util.Log;
import java.util.Locale;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class LogUtils {
public static final String TAG = "AppUpdater";
public static final String VERTICAL = "|";
/** 是否显示Log日志 */
private static boolean isShowLog = true;
/** Log日志优先权 */
private static int priority = 1;
/**
* Priority constant for the println method;use System.out.println
*/
public static final int PRINTLN = 1;
/**
* Priority constant for the println method; use Log.v.
*/
public static final int VERBOSE = 2;
/**
* Priority constant for the println method; use Log.d.
*/
public static final int DEBUG = 3;
/**
* Priority constant for the println method; use Log.i.
*/
public static final int INFO = 4;
/**
* Priority constant for the println method; use Log.w.
*/
public static final int WARN = 5;
/**
* Priority constant for the println method; use Log.e.
*/
public static final int ERROR = 6;
/**
* Priority constant for the println method.use Log.wtf.
*/
public static final int ASSERT = 7;
public static final String TAG_FORMAT = "%s.%s(%s:%d)";
private LogUtils(){
throw new AssertionError();
}
public static void setShowLog(boolean isShowLog) {
LogUtils.isShowLog = isShowLog;
}
public static boolean isShowLog() {
return isShowLog;
}
public static int getPriority() {
return priority;
}
public static void setPriority(int priority) {
LogUtils.priority = priority;
}
/**
* 根据堆栈生成TAG
* @return TAG|className.methodName(fileName:lineNumber)
*/
private static String generateTag(StackTraceElement caller) {
String tag = TAG_FORMAT;
String callerClazzName = caller.getClassName();
callerClazzName = callerClazzName.substring(callerClazzName.lastIndexOf(".") + 1);
tag = String.format(Locale.getDefault(),tag,callerClazzName, caller.getMethodName(),caller.getFileName(),caller.getLineNumber());
return new StringBuilder().append(TAG).append(VERTICAL).append(tag).toString();
}
/**
* 获取堆栈
* @param n
* n=0 VMStack
* n=1 Thread
* n=3 CurrentStack
* n=4 CallerStack
* ...
* @return
*/
public static StackTraceElement getStackTraceElement(int n) {
return Thread.currentThread().getStackTrace()[n];
}
/**
* 获取调用方的堆栈TAG
* @return
*/
private static String getCallerStackLogTag(){
return generateTag(getStackTraceElement(5));
}
/**
*
* @param t
* @return
*/
private static String getStackTraceString(Throwable t){
return Log.getStackTraceString(t);
}
// -----------------------------------Log.v
/**
* Log.v
* @param msg
*/
public static void v(String msg) {
if (isShowLog && priority <= VERBOSE)
Log.v(getCallerStackLogTag(), String.valueOf(msg));
}
public static void v(Throwable t) {
if (isShowLog && priority <= VERBOSE)
Log.v(getCallerStackLogTag(), getStackTraceString(t));
}
public static void v(String msg,Throwable t) {
if (isShowLog && priority <= VERBOSE)
Log.v(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------Log.d
/**
* Log.d
* @param msg
*/
public static void d(String msg) {
if (isShowLog && priority <= DEBUG)
Log.d(getCallerStackLogTag(), String.valueOf(msg));
}
public static void d(Throwable t) {
if (isShowLog && priority <= DEBUG)
Log.d(getCallerStackLogTag(), getStackTraceString(t));
}
public static void d(String msg,Throwable t) {
if (isShowLog && priority <= DEBUG)
Log.d(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------Log.i
/**
* Log.i
* @param msg
*/
public static void i(String msg) {
if (isShowLog && priority <= INFO)
Log.i(getCallerStackLogTag(), String.valueOf(msg));
}
public static void i(Throwable t) {
if (isShowLog && priority <= INFO)
Log.i(getCallerStackLogTag(), getStackTraceString(t));
}
public static void i(String msg,Throwable t) {
if (isShowLog && priority <= INFO)
Log.i(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------Log.w
/**
* Log.w
* @param msg
*/
public static void w(String msg) {
if (isShowLog && priority <= WARN)
Log.w(getCallerStackLogTag(), String.valueOf(msg));
}
public static void w(Throwable t) {
if (isShowLog && priority <= WARN)
Log.w(getCallerStackLogTag(), getStackTraceString(t));
}
public static void w(String msg,Throwable t) {
if (isShowLog && priority <= WARN)
Log.w(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------Log.e
/**
* Log.e
* @param msg
*/
public static void e(String msg) {
if (isShowLog && priority <= ERROR)
Log.e(getCallerStackLogTag(), String.valueOf(msg));
}
public static void e(Throwable t) {
if (isShowLog && priority <= ERROR)
Log.e(getCallerStackLogTag(), getStackTraceString(t));
}
public static void e(String msg,Throwable t) {
if (isShowLog && priority <= ERROR)
Log.e(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------Log.wtf
/**
* Log.wtf
* @param msg
*/
public static void wtf(String msg) {
if (isShowLog && priority <= ASSERT)
Log.wtf(getCallerStackLogTag(), String.valueOf(msg));
}
public static void wtf(Throwable t) {
if (isShowLog && priority <= ASSERT)
Log.wtf(getCallerStackLogTag(), getStackTraceString(t));
}
public static void wtf(String msg,Throwable t) {
if (isShowLog && priority <= ASSERT)
Log.wtf(getCallerStackLogTag(), String.valueOf(msg), t);
}
// -----------------------------------System.out.print
/**
* System.out.print
*
* @param msg
*/
public static void print(String msg) {
if (isShowLog && priority <= PRINTLN)
System.out.print(msg);
}
public static void print(Object obj) {
if (isShowLog && priority <= PRINTLN)
System.out.print(obj);
}
// -----------------------------------System.out.printf
/**
* System.out.printf
*
* @param msg
*/
public static void printf(String msg) {
if (isShowLog && priority <= PRINTLN)
System.out.printf(msg);
}
// -----------------------------------System.out.println
/**
* System.out.println
*
* @param msg
*/
public static void println(String msg) {
if (isShowLog && priority <= PRINTLN)
System.out.println(msg);
}
public static void println(Object obj) {
if (isShowLog && priority <= PRINTLN)
System.out.println(obj);
}
}

View File

@@ -0,0 +1,261 @@
package just.way.appdown.util;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import androidx.annotation.DrawableRes;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import java.io.File;
import just.way.appdown.UpdateConfig;
import just.way.appdown.constant.Constants;
import just.way.appdown.service.DownloadService;
/**
* @author <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public class NotificationUtils {
private NotificationUtils(){
throw new AssertionError();
}
/**
* 显示开始下载时的通知
* @param notifyId
* @param channelId
* @param channelName
* @param icon
* @param title
* @param content
*/
public static void showStartNotification(Context context, int notifyId,String channelId, String channelName,@DrawableRes int icon,CharSequence title,CharSequence content,boolean isVibrate,boolean isSound, boolean isCancelDownload){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
createNotificationChannel(context,channelId,channelName,isVibrate,isSound);
}
NotificationCompat.Builder builder = buildNotification(context,channelId,icon,title,content);
builder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
if(isVibrate && isSound){
builder.setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND);
}else if(isVibrate){
builder.setDefaults(Notification.DEFAULT_VIBRATE);
}else if(isSound){
builder.setDefaults(Notification.DEFAULT_SOUND);
}
if(isCancelDownload){
Intent intent = new Intent(context, DownloadService.class);
intent.putExtra(Constants.KEY_STOP_DOWNLOAD_SERVICE,true);
PendingIntent deleteIntent = PendingIntent.getService(context, notifyId,intent, getPendingIntentFlags(PendingIntent.FLAG_CANCEL_CURRENT));
builder.setDeleteIntent(deleteIntent);
}
Notification notification = builder.build();
if(isCancelDownload){
notification.flags = Notification.FLAG_ONLY_ALERT_ONCE;
}else{
notification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONLY_ALERT_ONCE;
}
notifyNotification(context,notifyId,notification);
}
/**
* 显示下载中的通知(更新进度)
* @param notifyId
* @param channelId
* @param icon
* @param title
* @param content
* @param progress
* @param size
*/
public static void showProgressNotification(Context context, int notifyId,String channelId,@DrawableRes int icon,CharSequence title,CharSequence content,int progress,int size, boolean isCancelDownload){
NotificationCompat.Builder builder = buildNotification(context,channelId,icon,title,content,progress,size);
if(isCancelDownload){
Intent intent = new Intent(context, DownloadService.class);
intent.putExtra(Constants.KEY_STOP_DOWNLOAD_SERVICE,true);
PendingIntent deleteIntent = PendingIntent.getService(context, notifyId, intent, getPendingIntentFlags(PendingIntent.FLAG_CANCEL_CURRENT));
builder.setDeleteIntent(deleteIntent);
}
Notification notification = builder.build();
if(isCancelDownload){
notification.flags = Notification.FLAG_ONLY_ALERT_ONCE;
}else{
notification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONLY_ALERT_ONCE;
}
notifyNotification(context,notifyId,notification);
}
/**
* 显示下载完成时的通知(点击安装)
* @param notifyId
* @param channelId
* @param icon
* @param title
* @param content
* @param file
*/
public static void showFinishNotification(Context context, int notifyId, String channelId, @DrawableRes int icon, CharSequence title, CharSequence content, File file, String authority){
cancelNotification(context,notifyId);
NotificationCompat.Builder builder = buildNotification(context,channelId,icon,title,content);
builder.setAutoCancel(true);
Intent intent = AppUtils.getInstallIntent(context,file,authority);
PendingIntent clickIntent = PendingIntent.getActivity(context, notifyId, intent, getPendingIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT));
builder.setContentIntent(clickIntent);
Notification notification = builder.build();
notification.flags = Notification.FLAG_AUTO_CANCEL;
notifyNotification(context,notifyId,notification);
}
/**
* 现在下载失败通知
* @param context
* @param notifyId
* @param channelId
* @param icon
* @param title
* @param content
* @param isReDownload
* @param config
*/
public static void showErrorNotification(Context context, int notifyId, String channelId, @DrawableRes int icon, CharSequence title, CharSequence content, boolean isReDownload, UpdateConfig config){
NotificationCompat.Builder builder = buildNotification(context,channelId,icon,title,content);
builder.setAutoCancel(true);
int flag = getPendingIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT);
if(isReDownload){// 重新下载
Intent intent = new Intent(context, DownloadService.class);
intent.putExtra(Constants.KEY_RE_DOWNLOAD,true);
intent.putExtra(Constants.KEY_UPDATE_CONFIG,config);
PendingIntent clickIntent = PendingIntent.getService(context, notifyId,intent, flag);
builder.setContentIntent(clickIntent);
}else{
PendingIntent clickIntent = PendingIntent.getService(context, notifyId, new Intent(), flag);
builder.setContentIntent(clickIntent);
}
Notification notification = builder.build();
notification.flags = Notification.FLAG_AUTO_CANCEL;
notifyNotification(context,notifyId,notification);
}
/**
* 显示通知信息(非第一次)
* @param notifyId
* @param channelId
* @param icon
* @param title
* @param content
*/
public static void showNotification(Context context, int notifyId,String channelId,@DrawableRes int icon,CharSequence title,CharSequence content,boolean isAutoCancel){
NotificationCompat.Builder builder = buildNotification(context,channelId,icon,title,content);
builder.setAutoCancel(isAutoCancel);
Notification notification = builder.build();
notification.flags = Notification.FLAG_AUTO_CANCEL;
notifyNotification(context,notifyId,notification);
}
/**
* 取消通知
* @param notifyId
*/
public static void cancelNotification(Context context, int notifyId){
getNotificationManager(context).cancel(notifyId);
}
/**
* 获取通知管理器
* @return
*/
public static NotificationManager getNotificationManager(Context context){
return (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
}
/**
* 创建一个通知渠道兼容0以上版本
* @param channelId
* @param channelName
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public static void createNotificationChannel(Context context, String channelId, String channelName,boolean isVibrate,boolean isSound){
NotificationChannel channel = new NotificationChannel(channelId,channelName, NotificationManager.IMPORTANCE_HIGH);
channel.enableVibration(isVibrate);
if(!isSound){
channel.setSound(null,null);
}
getNotificationManager(context).createNotificationChannel(channel);
}
/**
* 构建一个通知构建器
* @param channelId
* @param icon
* @param title
* @param content
* @return
*/
private static NotificationCompat.Builder buildNotification(Context context, String channelId, @DrawableRes int icon,CharSequence title,CharSequence content){
return buildNotification(context,channelId,icon,title,content,Constants.NONE,Constants.NONE);
}
/**
* 构建一个通知构建器
* @param channelId
* @param icon
* @param title
* @param content
* @param progress
* @param size
* @return
*/
private static NotificationCompat.Builder buildNotification(Context context, String channelId, @DrawableRes int icon, CharSequence title, CharSequence content, int progress, int size){
NotificationCompat.Builder builder = new NotificationCompat.Builder(context,channelId);
builder.setSmallIcon(icon);
builder.setContentTitle(title);
builder.setContentText(content);
builder.setOngoing(true);
if(progress != Constants.NONE && size != Constants.NONE){
builder.setProgress(size,progress,false);
}
return builder;
}
/**
* 更新通知栏
* @param id
* @param notification
*/
private static void notifyNotification(Context context, int id, Notification notification){
getNotificationManager(context).notify(id,notification);
}
/**
* 获取 PendingIntent 的 flags
* @param flag
* @return
*/
private static int getPendingIntentFlags(int flag){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return flag | PendingIntent.FLAG_IMMUTABLE;
}
return flag;
}
}

View File

@@ -0,0 +1,92 @@
package just.way.appdown.util;
import android.Manifest;
import android.app.Activity;
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public final class PermissionUtils {
private PermissionUtils() {
throw new AssertionError();
}
/**
* 校验权限
*
* @param activity
* @param requestCode
* @return
*/
public static boolean verifyReadAndWritePermissions(@NonNull Activity activity, int requestCode) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
int readResult = checkPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE);
int writeResult = checkPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (readResult != PackageManager.PERMISSION_GRANTED || writeResult != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(activity, new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode);
return false;
}
}
return true;
}
public static int checkPermission(@NonNull Activity activity, @NonNull String permission) {
return ActivityCompat.checkSelfPermission(activity, permission);
}
/**
* 获取通知权限
*
* @param context
*/
public static boolean isNotificationEnabled(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager.getImportance() == NotificationManager.IMPORTANCE_NONE) {
return false;
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
String CHECK_OP_NO_THROW = "checkOpNoThrow";
String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
ApplicationInfo appInfo = context.getApplicationInfo();
String pkg = context.getApplicationContext().getPackageName();
int uid = appInfo.uid;
try {
Class appOpsClass = Class.forName(AppOpsManager.class.getName());
Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, String.class);
Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
int value = (Integer) opPostNotificationValue.get(Integer.class);
return ((Integer) checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg) == AppOpsManager.MODE_ALLOWED);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
return true;
}
}

View File

@@ -0,0 +1,168 @@
package just.way.appdown.util;
import android.content.Context;
import android.text.TextUtils;
import androidx.annotation.RawRes;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
/**
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
*/
public final class SSLSocketFactoryUtils {
private static final String[] VERIFY_HOST_NAME = new String[]{};
private SSLSocketFactoryUtils() {
throw new AssertionError();
}
public static SSLSocketFactory createSSLSocketFactory() {
SSLSocketFactory sslSocketFactory = null;
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, getTrustAllManager(), new SecureRandom());
sslSocketFactory = sslContext.getSocketFactory();
} catch (Exception e) {
}
return sslSocketFactory;
}
public static X509TrustManager createTrustAllManager() {
X509TrustManager tm = null;
try {
tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
//do nothing
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
//do nothing
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
} catch (Exception e) {
e.printStackTrace();
}
return tm;
}
public static TrustAllHostnameVerifier createTrustAllHostnameVerifier() {
return new TrustAllHostnameVerifier();
}
public static class TrustAllHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
if (TextUtils.isEmpty(hostname)) {
return false;
}
return !Arrays.asList(VERIFY_HOST_NAME).contains(hostname);
}
}
/**
* @param context
* @param keyServerStoreID
* @return
*/
public static SSLSocketFactory createSSLSocketFactory(Context context, @RawRes int keyServerStoreID) {
InputStream trustStream = context.getResources().openRawResource(keyServerStoreID);
return createSSLSocketFactory(trustStream);
}
/**
* @param certificates
* @return
*/
public static SSLSocketFactory createSSLSocketFactory(InputStream... certificates) {
SSLSocketFactory sSLSocketFactory = null;
if (sSLSocketFactory == null) {
synchronized (SSLSocketFactoryUtils.class) {
if (sSLSocketFactory == null) {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, getTrustManager(certificates), new SecureRandom());
sSLSocketFactory = sslContext.getSocketFactory();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return sSLSocketFactory;
}
/**
* 获得指定流中的服务器端证书库
*
* @param certificates
* @return
*/
public static TrustManager[] getTrustManager(InputStream... certificates) {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
int index = 0;
for (InputStream certificate : certificates) {
if (certificate == null) {
continue;
}
Certificate certificate1;
try {
certificate1 = certificateFactory.generateCertificate(certificate);
} finally {
certificate.close();
}
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificate1);
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory
.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
return trustManagerFactory.getTrustManagers();
} catch (Exception e) {
e.printStackTrace();
}
return getTrustAllManager();
}
/**
* 获得信任所有服务器端证书库
*/
public static TrustManager[] getTrustAllManager() {
return new TrustManager[]{createTrustAllManager()};
}
}

View File

@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#333333"
android:alpha="0.8">
<path
android:fillColor="@android:color/white"
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:color="@color/white"
tools:ignore="NewApi">
<item android:id="@android:id/mask"
tools:ignore="NewApi">
<shape>
<solid android:color="@android:color/transparent" />
<corners android:radius="5dp" />
</shape>
</item>
<!-- 默认显⽰效果-->
<item>
<shape android:shape="rectangle">
<gradient
android:angle="180"
android:startColor="@android:color/transparent"
android:endColor="@android:color/transparent"
android:type="linear"
android:useLevel="true" />
<stroke android:width="1dp" android:color="#333333"/>
<corners
android:radius="5dp" />
</shape>
</item>
</ripple>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<animated-rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
>
<shape
android:shape="ring"
android:innerRadiusRatio="3"
android:thicknessRatio="8"
android:useLevel="false"
>
<gradient
android:type="sweep"
android:useLevel="false"
android:startColor="#FF0000"
android:centerColor="#FF5555"
android:endColor="#FBD3D0"
android:centerY="0.50" />
</shape>
</animated-rotate>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<animated-rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
>
<shape
android:shape="ring"
android:innerRadiusRatio="3"
android:thicknessRatio="8"
android:useLevel="false"
>
<gradient
android:type="sweep"
android:useLevel="false"
android:startColor="#000000"
android:centerColor="#888888"
android:endColor="#FFFFFF"
android:centerY="0.50" />
</shape>
</animated-rotate>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="20dp" />
<solid android:color="@color/white" />
</shape>

View File

@@ -0,0 +1,351 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity"
android:background="@color/white"
android:id="@+id/homePage">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="@+id/big_v"
android:visibility="visible"
android:background="@android:color/transparent"
>
<TextView
android:id="@+id/inputText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="6"
android:padding="10dp"
android:textSize="40dp"
android:textColor="@color/black"
android:gravity="center_vertical" />
<TextView
android:id="@+id/outputText"
android:layout_width="match_parent"
android:layout_height="60dp"
android:gravity="end"
android:textColor="@color/black"
android:textAlignment="textEnd"
android:autoSizeTextType="uniform"
android:textSize="80dp" />
<LinearLayout
android:id="@+id/allBtn"
android:layout_weight="3"
android:background="#EEEEEE"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal">
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="AC"
android:textSize="35dp"
android:textColor="#e67e22"
android:id="@+id/btn_clear"
>
</Button>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="←"
android:textSize="35dp"
android:textColor="#e67e22"
android:id="@+id/btn_backspace"
>
</Button>
</LinearLayout>
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="3"
android:background="@android:color/transparent"
android:text="÷"
android:textSize="35dp"
android:textColor="#e67e22"
android:id="@+id/btn_divide"
>
</Button>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:id="@+id/btn_7"
android:text="7"
android:textColor="@color/black"
android:textSize="35dp">
</Button>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:id="@+id/btn_8"
android:text="8"
android:textColor="@color/black"
android:textSize="35dp"
>
</Button>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:id="@+id/btn_9"
android:text="9"
android:textColor="@color/black"
android:textSize="35dp"
>
</Button>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="×"
android:textSize="35dp"
android:textColor="#e67e22"
android:id="@+id/btn_multiply"
>
</Button>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="4"
android:textColor="@color/black"
android:textSize="35dp"
android:id="@+id/btn_4"
>
</Button>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="5"
android:textColor="@color/black"
android:textSize="35dp"
android:id="@+id/btn_5"
>
</Button>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="6"
android:textColor="@color/black"
android:textSize="35dp"
android:id="@+id/btn_6"
>
</Button>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="-"
android:textSize="35dp"
android:textColor="#e67e22"
android:id="@+id/btn_subtract"
>
</Button>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="1"
android:textColor="@color/black"
android:textSize="35dp"
android:id="@+id/btn_1"
>
</Button>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="2"
android:textColor="@color/black"
android:textSize="35dp"
android:id="@+id/btn_2"
>
</Button>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="3"
android:textColor="@color/black"
android:textSize="35dp"
android:id="@+id/btn_3"
>
</Button>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="+"
android:textSize="35dp"
android:textColor="#e67e22"
android:id="@+id/btn_add"
>
</Button>
</LinearLayout>
<LinearLayout
android:id="@+id/fiveLow"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text=""
android:textSize="35dp"
>
</Button>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="0"
android:textColor="@color/black"
android:textSize="35dp"
android:id="@+id/btn_0"
>
</Button>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="."
android:textColor="@color/black"
android:textSize="35dp"
android:id="@+id/btn_point"
>
</Button>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="="
android:textSize="35dp"
android:textColor="#e67e22"
android:id="@+id/btn_equal"
>
</Button>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
android:orientation="vertical">
<just.way.StatusLayout
android:id="@+id/top_vvvv1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:visibility="gone" />
<RelativeLayout
android:id="@+id/top_vvvv"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_below="@id/top_vvvv1"
android:background="@color/white"
android:visibility="gone">
<ImageView
android:id="@+id/back_iv"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerInside"
android:src="@drawable/ic_action_back" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_centerInParent="true"
android:src="@mipmap/index_log1" />
</RelativeLayout>
<com.tencent.smtt.sdk.WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/top_vvvv"/>
<LinearLayout
android:id="@+id/layoutError"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical"
android:visibility="gone">
<ProgressBar
android:id="@+id/progress_error"
style="@android:style/Widget.ProgressBar.Large"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:text="@string/agentweb_loading" />
<TextView
android:id="@+id/errormsg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textColor="#999999"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/show_top_ly"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/top_vvvv"
android:background="@color/white"
android:orientation="vertical"
android:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="4"
android:gravity="center">
<just.way.CircleImageView
android:id="@+id/show_top_v"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@mipmap/winway"
android:visibility="visible"
app:ease_border_color="#EEEEEE"
app:ease_border_width="1dp"
app:es_shape_type="round" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="6"
android:gravity="center">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:indeterminateDrawable="@drawable/pass_word_bg1" />
</LinearLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/videoContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentRight="true"
android:layout_marginTop="10dp"
android:layout_marginRight="15dp"
android:indeterminateDrawable="@drawable/pass_word_bg2"
android:visibility="gone" />
</RelativeLayout>

View File

@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
android:orientation="vertical">
<just.way.StatusLayout
android:id="@+id/top_vvvv1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:visibility="gone" />
<RelativeLayout
android:id="@+id/top_vvvv"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_below="@id/top_vvvv1"
android:background="@color/white"
android:visibility="gone">
<ImageView
android:id="@+id/back_iv"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerInside"
android:src="@drawable/ic_action_back" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_centerInParent="true"
android:src="@mipmap/index_log1" />
</RelativeLayout>
<com.tencent.smtt.sdk.WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/top_vvvv"/>
<LinearLayout
android:id="@+id/layoutError"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical"
android:visibility="gone">
<ProgressBar
android:id="@+id/progress_error"
style="@android:style/Widget.ProgressBar.Large"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:text="@string/agentweb_loading" />
<TextView
android:id="@+id/errormsg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textColor="#999999"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/show_top_ly"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/top_vvvv"
android:background="@color/white"
android:orientation="vertical"
android:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="4"
android:gravity="center">
<just.way.CircleImageView
android:id="@+id/show_top_v"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@mipmap/winway"
android:visibility="visible"
app:ease_border_color="#EEEEEE"
app:ease_border_width="1dp"
app:es_shape_type="round" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="6"
android:gravity="center">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:indeterminateDrawable="@drawable/pass_word_bg1" />
</LinearLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/videoContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentRight="true"
android:layout_marginTop="10dp"
android:layout_marginRight="15dp"
android:indeterminateDrawable="@drawable/pass_word_bg2"
android:visibility="gone" />
</RelativeLayout>

View File

@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<just.way.StatusLayout
android:id="@+id/top_vvvv1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:visibility="gone" />
<RelativeLayout
android:id="@+id/top_vvvv"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_below="@id/top_vvvv1"
android:background="@color/white"
android:visibility="gone">
<ImageView
android:id="@+id/back_iv"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerInside"
android:src="@drawable/ic_action_back" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_centerInParent="true"
android:src="@mipmap/index_log1" />
</RelativeLayout>
<LinearLayout
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_below="@id/top_vvvv" />
<LinearLayout
android:id="@+id/show_top_ly"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/top_vvvv"
android:background="@color/white"
android:orientation="vertical"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="4"
android:gravity="center">
<just.way.CircleImageView
android:id="@+id/show_top_v"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@mipmap/winway"
android:visibility="visible"
app:ease_border_color="#EEEEEE"
app:ease_border_width="1dp"
app:es_shape_type="round" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="6"
android:gravity="center">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminateDrawable="@drawable/pass_word_bg1"
android:layout_centerHorizontal="true" />
</LinearLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/videoContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="20dp"
android:layout_height="20dp"
android:visibility="gone"
android:layout_marginRight="15dp"
android:layout_marginTop="10dp"
android:layout_alignParentRight="true"
android:indeterminateDrawable="@drawable/pass_word_bg2"
/>
</RelativeLayout>

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/top_vvvv"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@color/white"
android:visibility="visible">
<ImageView
android:id="@+id/back_iv"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerInside"
android:src="@drawable/ic_action_back" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_centerInParent="true"
android:src="@mipmap/jisuanqi_logo" />
</RelativeLayout>
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/top_vvvv"
android:visibility="gone"
android:background="@mipmap/jisuanqi_logo" />
<com.tencent.smtt.sdk.WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/top_vvvv"
android:visibility="gone" />
<LinearLayout
android:id="@+id/show_top_ly"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/top_vvvv"
android:background="@color/white"
android:orientation="vertical"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="4"
android:gravity="center">
<just.way.CircleImageView
android:id="@+id/show_top_v"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@mipmap/jisuanqi_logo"
android:visibility="visible"
app:ease_border_color="#EEEEEE"
app:ease_border_width="1dp"
app:es_shape_type="round" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="6"
android:gravity="center">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:indeterminateDrawable="@drawable/pass_word_bg1" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>

View File

@@ -0,0 +1,294 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/homePage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/white"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:layout_weight="6">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#333333"
android:text="@string/qsrlwmm_txt"
android:textSize="20dp">
</TextView>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:orientation="horizontal">
<TextView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_margin="5dp"
android:gravity="center"
android:textSize="20sp"
android:textColor="#333333"
android:background="@drawable/pass_word_bg"
android:id="@+id/password_1"/>
<TextView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_margin="5dp"
android:gravity="center"
android:textSize="20sp"
android:textColor="#333333"
android:background="@drawable/pass_word_bg"
android:id="@+id/password_2"/>
<TextView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_margin="5dp"
android:gravity="center"
android:textSize="20sp"
android:textColor="#333333"
android:background="@drawable/pass_word_bg"
android:id="@+id/password_3"/>
<TextView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_margin="5dp"
android:gravity="center"
android:textSize="20sp"
android:textColor="#333333"
android:background="@drawable/pass_word_bg"
android:id="@+id/password_4"/>
<TextView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_margin="5dp"
android:gravity="center"
android:textSize="20sp"
android:textColor="#333333"
android:background="@drawable/pass_word_bg"
android:id="@+id/password_5"/>
<TextView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_margin="5dp"
android:gravity="center"
android:textSize="20sp"
android:textColor="#333333"
android:background="@drawable/pass_word_bg"
android:id="@+id/password_6"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/allBtn"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#EEEEEE"
android:layout_weight="4"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal">
<Button
android:id="@+id/btn_7"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="7"
android:textColor="@color/black"
android:textSize="25dp">
</Button>
<Button
android:id="@+id/btn_8"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="8"
android:textColor="@color/black"
android:textSize="25dp">
</Button>
<Button
android:id="@+id/btn_9"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="9"
android:textColor="@color/black"
android:textSize="25dp">
</Button>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal">
<Button
android:id="@+id/btn_4"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="4"
android:textColor="@color/black"
android:textSize="25dp">
</Button>
<Button
android:id="@+id/btn_5"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="5"
android:textColor="@color/black"
android:textSize="25dp">
</Button>
<Button
android:id="@+id/btn_6"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="6"
android:textColor="@color/black"
android:textSize="25dp">
</Button>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
>
<Button
android:id="@+id/btn_1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="1"
android:textColor="@color/black"
android:textSize="25dp">
</Button>
<Button
android:id="@+id/btn_2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="2"
android:textColor="@color/black"
android:textSize="25dp">
</Button>
<Button
android:id="@+id/btn_3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="3"
android:textColor="@color/black"
android:textSize="25dp">
</Button>
</LinearLayout>
<LinearLayout
android:id="@+id/fiveLow"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
>
<Button
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text=""
android:textColor="#e67e22"
android:textSize="25dp">
</Button>
<Button
android:id="@+id/btn_0"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="0"
android:textColor="@color/black"
android:textSize="25dp">
</Button>
<Button
android:id="@+id/btn_equal"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
android:text="DEL"
android:textColor="#e67e22"
android:textSize="25dp">
</Button>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
android:orientation="vertical">
<just.way.StatusLayout
android:id="@+id/top_vvvv1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:visibility="gone" />
<RelativeLayout
android:id="@+id/top_vvvv"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_below="@id/top_vvvv1"
android:background="@color/white"
android:visibility="gone">
<ImageView
android:id="@+id/back_iv"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerInside"
android:src="@drawable/ic_action_back" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_centerInParent="true"
android:src="@mipmap/index_log1" />
</RelativeLayout>
<com.tencent.smtt.sdk.WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/top_vvvv" />
<LinearLayout
android:id="@+id/show_top_ly"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/top_vvvv"
android:background="@color/white"
android:orientation="vertical"
android:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="4"
android:gravity="center">
<just.way.CircleImageView
android:id="@+id/show_top_v"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@mipmap/winway"
android:visibility="visible"
app:ease_border_color="#EEEEEE"
app:ease_border_width="1dp"
app:es_shape_type="round" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="6"
android:gravity="center">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminateDrawable="@drawable/pass_word_bg1"
android:layout_centerHorizontal="true" />
</LinearLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/videoContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="20dp"
android:layout_height="20dp"
android:visibility="gone"
android:layout_marginRight="15dp"
android:layout_marginTop="10dp"
android:layout_alignParentRight="true"
android:indeterminateDrawable="@drawable/pass_word_bg2"
/>
</RelativeLayout>

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:orientation="vertical">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="285dp"
android:layout_height="wrap_content"
android:minHeight="144dp"
android:background="@drawable/shape_dialog_bg_new"
android:orientation="vertical">
<TextView
android:layout_width="245dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginTop="30dp"
android:layout_marginBottom="30dp"
android:lineSpacingExtra="4dp"
android:gravity="center"
android:id="@+id/content_tv"
android:layout_gravity="center_horizontal"
android:text="1111111111"
android:textColor="#333333"
android:textSize="16sp"
/>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="#D8D8D8"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="62dp"
android:layout_alignParentBottom="true"
android:layout_gravity="center_horizontal"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/cancel_tv"
android:text="@string/cancel_txt"
android:gravity="center"
android:textColor="#e44f3e"
android:textSize="16sp"
/>
<View android:layout_width="1dp"
android:id="@+id/line_v"
android:layout_height="42dp"
android:background="#d8d8d8"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/sumbit_tv"
android:text="@string/sure_txt"
android:gravity="center"
android:textColor="#6b3689"
android:textSize="16sp"
/>
</LinearLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
</LinearLayout>

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@@ -0,0 +1,5 @@
<resources>
<string name="app_name">kalkulator</string>
<string name="app_name1">WINWAY</string>
<string name="qsrlwmm_txt">Sila Set Password Kamu</string>
</resources>

View File

@@ -0,0 +1,25 @@
<resources>
<string name="app_name">Calculator</string>
<string name="app_name1">WINWAY</string>
<string name="qsrlwmm_txt">Please Set Your Password</string>
<string name="cancel_txt">Cancel</string>
<string name="sure_txt">Sure</string>
<string name="banbengengxin_txt">New Version Update</string>
<string name="xiacigengxin_txt">Next Update</string>
<string name="lijigengxin_txt">Update Immediately</string>
<string name="app_updater_error_notification_content">点击关闭通知</string>
<string name="app_updater_error_notification_content_re_download">点击重新下载</string>
<string name="app_updater_error_notification_title">下载失败</string>
<string name="app_updater_finish_notification_content">点击安装</string>
<string name="app_updater_finish_notification_title">下载完成</string>
<string name="app_updater_progress_notification_content">正在下载…</string>
<string name="app_updater_progress_notification_title">版本更新</string>
<string name="app_updater_progress_notification_title_2">下载游戏中</string>
<string name="app_updater_start_notification_content">正在获取下载数据…</string>
<string name="app_updater_start_notification_title">版本更新</string>
<string name="app_updater_start_notification_title_2">下载游戏中</string>
<string name="notification_title_txt">Need to turn on mobile phone notification permission</string>
<string name="notification_cancel_txt">Cancel</string>
<string name="notification_setting_txt">Setting</string>
</resources>

View File

@@ -0,0 +1,5 @@
<resources>
<string name="app_name">kalkulator</string>
<string name="app_name1">WINWAY</string>
<string name="qsrlwmm_txt">Sila Set Password Kamu</string>
</resources>

View File

@@ -0,0 +1,69 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.Calculcator" parent="Theme.MaterialComponents.DayNight.DarkActionBar.Bridge">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/black</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
<style name="Theme.Calculcator1" parent="Theme.MaterialComponents.DayNight.DarkActionBar.Bridge">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowNoTitle">true</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
<declare-styleable name="CircleImageView">
<attr name="ease_border_color" format="color" />
<attr name="ease_border_width" format="dimension" />
<attr name="ease_press_alpha" format="integer" />
<attr name="ease_press_color" format="color" />
<attr name="ease_radius" format="dimension" />
<attr name="es_shape_type" format="enum">
<enum name="none" value="0" />
<enum name="round" value="1" />
<enum name="rectangle" value="2" />
</attr>
</declare-styleable>
<!-- 注意当前AppTheme主题在values-v23中单独重复维护。原因是Android 6以下系统不支持设置
系统状态栏颜色如果按照设计状态栏使用素色则在android6以下手机上就看不清系统状态栏文字了
因为系统文字是白色。在values-v23表示当Android 23即android 6及以上版本将自动使用该目录
下的主题(即 colorPrimaryDark 使用素色从而跟标题栏颜色保持一致实现沉浸式ui效果-->
<style name="AppThemeStart" parent="@style/Theme.AppCompat.Light.NoActionBar">
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">false</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="colorPrimary">@color/white</item>
<item name="colorPrimaryDark">@color/white</item>
<item name="colorAccent">@color/white</item>
<item name="windowActionBar">false</item>
<!-- 隐藏Activity窗口的Title标题栏 -->
<item name="windowNoTitle">true</item>
<!-- <item name="android:windowFullscreen">true</item>-->
<!-- <item name="android:windowBackground">@drawable/splah_bg</item>-->
<item name="android:windowBackground">@color/white</item>
<item name="android:navigationBarColor">@color/white</item>
<!-- <item name="android:windowBackground">@mipmap/big_bg</item>-->
<item name="android:forceDarkAllowed" tools:ignore="NewApi">false</item>
</style>
</resources>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFFFFF</color>
<color name="purple_500">#FFFFFF</color>
<color name="purple_700">#FFFFFF</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="jisuanqi">#EF4723</color>
<color name="winway">#FFFFFF</color>
</resources>

View File

@@ -0,0 +1,24 @@
<resources>
<string name="app_name">计算器</string>
<string name="app_name1">WINWAY</string>
<string name="qsrlwmm_txt">请输入6位密码</string>
<string name="cancel_txt">取消</string>
<string name="sure_txt">确定</string>
<string name="banbengengxin_txt">版本更新</string>
<string name="xiacigengxin_txt">下次更新</string>
<string name="lijigengxin_txt">立即更新</string>
<string name="app_updater_error_notification_content">点击关闭通知</string>
<string name="app_updater_error_notification_content_re_download">点击重新下载</string>
<string name="app_updater_error_notification_title">下载失败</string>
<string name="app_updater_finish_notification_content">点击安装</string>
<string name="app_updater_finish_notification_title">下载完成</string>
<string name="app_updater_progress_notification_content">正在下载…</string>
<string name="app_updater_progress_notification_title">版本更新</string>
<string name="app_updater_progress_notification_title_2">下载游戏中</string>
<string name="app_updater_start_notification_title">版本更新</string>
<string name="app_updater_start_notification_title_2">下载游戏中</string>
<string name="app_updater_start_notification_content">正在获取下载数据…</string>
<string name="notification_title_txt">需要打开手机通知权限</string>
<string name="notification_cancel_txt">取消</string>
<string name="notification_setting_txt">设置</string>
</resources>

View File

@@ -0,0 +1,88 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.Calculcator" parent="Theme.MaterialComponents.DayNight.DarkActionBar.Bridge">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
<style name="Theme.Calculcator1" parent="Theme.MaterialComponents.DayNight.DarkActionBar.Bridge">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowNoTitle">true</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
<declare-styleable name="CircleImageView">
<attr name="ease_border_color" format="color" />
<attr name="ease_border_width" format="dimension" />
<attr name="ease_press_alpha" format="integer" />
<attr name="ease_press_color" format="color" />
<attr name="ease_radius" format="dimension" />
<attr name="es_shape_type" format="enum">
<enum name="none" value="0" />
<enum name="round" value="1" />
<enum name="rectangle" value="2" />
</attr>
</declare-styleable>
<!-- 注意当前AppTheme主题在values-v23中单独重复维护。原因是Android 6以下系统不支持设置
系统状态栏颜色如果按照设计状态栏使用素色则在android6以下手机上就看不清系统状态栏文字了
因为系统文字是白色。在values-v23表示当Android 23即android 6及以上版本将自动使用该目录
下的主题(即 colorPrimaryDark 使用素色从而跟标题栏颜色保持一致实现沉浸式ui效果-->
<style name="AppThemeStart" parent="@style/Theme.AppCompat.Light.NoActionBar">
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">false</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="colorPrimary">@color/white</item>
<item name="colorPrimaryDark">@color/white</item>
<item name="colorAccent">@color/white</item>
<item name="windowActionBar">false</item>
<!-- 隐藏Activity窗口的Title标题栏 -->
<item name="windowNoTitle">true</item>
<!-- <item name="android:windowFullscreen">true</item>-->
<!-- <item name="android:windowBackground">@drawable/splah_bg</item>-->
<item name="android:windowBackground">@color/white</item>
<item name="android:navigationBarColor">@color/white</item>
<!-- <item name="android:windowBackground">@mipmap/big_bg</item>-->
<item name="android:forceDarkAllowed" tools:ignore="NewApi">false</item>
</style>
<style name="MaterialDesignDialog" parent="@style/Theme.AppCompat.Dialog">
<!-- 背景透明 -->
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<!-- 浮于Activity之上 -->
<item name="android:windowIsFloating">true</item>
<!-- 边框 -->
<item name="android:windowFrame">@null</item>
<!-- Dialog以外的区域模糊效果 -->
<item name="android:backgroundDimEnabled">true</item>
<!-- 无标题 -->
<item name="android:windowNoTitle">true</item>
<!-- 半透明 -->
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowCloseOnTouchOutside">true</item>
</style>
</resources>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<root-path name="app_root_path" path="/"/>
<external-path name="app_external_path" path="/"/>
<external-cache-path name="app_external_cache_path" path="/"/>
<external-files-path name="app_external_files_path" path="/"/>
<files-path name="app_files_path" path="/"/>
<cache-path name="app_cache_path" path="/"/>
</paths>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding= "utf-8"?>
<resources>
<paths >
<external-path name="external_files" path="."/>
<root-path name="root" path="." />
<files-path name="files" path="." />
<cache-path name="cache" path="." />
<external-files-path name="external_files_f" path="." />
<external-cache-path name="external_cache" path="." />
</paths >
</resources>
<!-- 适配7.0及其以上配合com.eva.android.OpenFileUtil用于解决调用系统Intent查看大文件内
容、拍照保存图片的功能时出现"android.os.FileUriExposedException"异常的问题 -->

View File

@@ -0,0 +1,35 @@
package Tptogiar.calculcator;
import org.junit.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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);
}
@Test
public void TestPattern(){
//
Pattern compile = Pattern.compile( "^(\\-|\\+)?\\d+(\\.\\d+)?$");
String a="+45.5";
boolean matches = compile.matcher(a).matches();
System.out.println(matches);
String result = compile.matcher(a).replaceAll("");
System.out.println(result);
}
}