diff --git a/app/build.gradle b/app/build.gradle index 837dff7..3e23499 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,8 +29,8 @@ android { defaultConfig { applicationId "com.aoleyun.sn" - versionCode 84 - versionName "6.4.4" + versionCode 86 + versionName "6.4.6" //There are no CERT files because If the mini sdk version is 23+, the AGP will ignore the V1 scheme signature. minSdkVersion 24 @@ -436,6 +436,7 @@ dependencies { // // kotlin扩展(可选) // implementation 'com.gyf.immersionbar:immersionbar-ktx:3.0.0' implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.24' + implementation 'com.facebook.rebound:rebound:0.3.8' } preBuild { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ad41f6d..ec1fd8c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -149,6 +149,21 @@ + + + + + + + + diff --git a/app/src/main/java/com/aoleyun/sn/activity/EyeProtectionActivity.java b/app/src/main/java/com/aoleyun/sn/activity/EyeProtectionActivity.java new file mode 100644 index 0000000..ea4f3be --- /dev/null +++ b/app/src/main/java/com/aoleyun/sn/activity/EyeProtectionActivity.java @@ -0,0 +1,99 @@ +package com.aoleyun.sn.activity; + +import android.provider.Settings; +import android.view.View; + +import androidx.constraintlayout.widget.ConstraintLayout; + +import com.aoleyun.sn.R; +import com.aoleyun.sn.base.BaseActivity; +import com.aoleyun.sn.utils.ToastUtil; +import com.aoleyun.sn.view.ToggleButton; + +import butterknife.BindView; +import butterknife.ButterKnife; + +public class EyeProtectionActivity extends BaseActivity { + + @BindView(R.id.cl_exit) + ConstraintLayout cl_exit; + @BindView(R.id.toggleButton1) + ToggleButton toggleButton1; + @BindView(R.id.toggleButton2) + ToggleButton toggleButton2; + @BindView(R.id.toggleButton3) + ToggleButton toggleButton3; + @BindView(R.id.toggleButton4) + ToggleButton toggleButton4; + @BindView(R.id.toggleButton5) + ToggleButton toggleButton5; + + @Override + public int getLayoutId() { + return R.layout.activity_eye_protection; + } + + + @Override + public void initView() { + ButterKnife.bind(this); + int nightDisplay = Settings.Secure.getInt(getContentResolver(), Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 0); + if (nightDisplay == 1) { + toggleButton1.setToggleOn(); + } else { + toggleButton1.setToggleOff(); + } + toggleButton1.setOnToggleChanged(new ToggleButton.OnToggleChanged() { + @Override + public void onToggle(boolean on) { + Settings.Secure.putInt(getContentResolver(), Settings.Secure.NIGHT_DISPLAY_ACTIVATED, on ? 1 : 0); + } + }); + + int accessibilityDisplay = Settings.Secure.getInt(getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0); + if (accessibilityDisplay == 1) { + toggleButton2.setToggleOn(); + } else { + toggleButton2.setToggleOff(); + } + toggleButton2.setOnToggleChanged(new ToggleButton.OnToggleChanged() { + @Override + public void onToggle(boolean on) { + Settings.Secure.putInt(getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, on ? 1 : 0); + } + }); + toggleButton3.setDisable(true); + toggleButton3.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + ToastUtil.show("此功能暂未开放"); + } + }); + toggleButton4.setDisable(true); + toggleButton4.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + ToastUtil.show("此功能暂未开放"); + } + }); + toggleButton5.setDisable(true); + toggleButton5.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + ToastUtil.show("此功能暂未开放"); + } + }); + + cl_exit.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + finish(); + } + }); + } + + @Override + public void initData() { + + } +} diff --git a/app/src/main/java/com/aoleyun/sn/activity/main/MainAContact.java b/app/src/main/java/com/aoleyun/sn/activity/main/MainAContact.java index fc13562..0c12755 100644 --- a/app/src/main/java/com/aoleyun/sn/activity/main/MainAContact.java +++ b/app/src/main/java/com/aoleyun/sn/activity/main/MainAContact.java @@ -16,8 +16,10 @@ public class MainAContact { void getQrCode(boolean loocked); /*获取学生信息*/ void getStudesInfo(); - //获取电子书包激活码 + /*获取电子书包激活码*/ void getEBagCode(); + /*获取学习应用*/ + void getExclusiveAdminApp(); /*获取公网ip*/ void getPublicIp(); /*手动获取设备信息更新*/ @@ -37,8 +39,10 @@ public class MainAContact { void setQrCode(Bitmap qrcode); /*获取学生信息*/ void setStudesInfo(StudentsInfo studesInfo); - //获取电子书包激活码 - void getEBagCodeFinish(); + /*获取电子书包激活码*/ + void getEBagCodeFinish(boolean activation); + /*获取学习应用*/ + void getExclusiveAdminAppFinish(); /*获取公网ip*/ void setPublicIp(String ip); /*手动获取设备信息更新*/ diff --git a/app/src/main/java/com/aoleyun/sn/activity/main/MainAPresenter.java b/app/src/main/java/com/aoleyun/sn/activity/main/MainAPresenter.java index 956c481..45b1f16 100644 --- a/app/src/main/java/com/aoleyun/sn/activity/main/MainAPresenter.java +++ b/app/src/main/java/com/aoleyun/sn/activity/main/MainAPresenter.java @@ -9,6 +9,7 @@ import android.util.Log; import android.view.View; import com.aoleyun.sn.BuildConfig; +import com.aoleyun.sn.bean.AdminAppInfo; import com.aoleyun.sn.bean.AppUpdateInfo; import com.aoleyun.sn.bean.BaseResponse; import com.aoleyun.sn.bean.StudentsInfo; @@ -24,6 +25,8 @@ import com.google.gson.JsonParser; import com.trello.rxlifecycle4.RxLifecycle; import com.trello.rxlifecycle4.android.ActivityEvent; +import java.util.List; + import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.Observer; @@ -114,11 +117,47 @@ public class MainAPresenter implements MainAContact.Presenter { NetInterfaceManager.getInstance().getEBagCode(true, getLifecycle(), new NetInterfaceManager.onCompleteCallback() { @Override public void onComplete() { - mView.getEBagCodeFinish(); + mView.getEBagCodeFinish(Settings.Global.getInt(mContext.getContentResolver(), CommonConfig.UIUI_ACTIVATION_KEY, 0) == 1); } }); } + + @Override + public void getExclusiveAdminApp() { + NetInterfaceManager.getInstance().getExclusiveAdminAppObservable() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY)) + .subscribe(new Observer>>() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("getAdminApp", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull BaseResponse> listBaseResponse) { + Log.e("getAdminApp", "onNext: " + listBaseResponse); + if (listBaseResponse.code == 200) { + List adminAppInfos = listBaseResponse.data; + JGYUtils.getInstance().installAdminApp(adminAppInfos); + } + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("getAdminApp", "onError: " + e.getMessage()); + onComplete(); + } + + @Override + public void onComplete() { + Log.e("getAdminApp", "onComplete: "); + mView.getExclusiveAdminAppFinish(); + } + }); + } + @Override public void getPublicIp() { NetInterfaceManager.getInstance().getPublicIp(lifecycle, ip -> mView.setPublicIp(ip)); diff --git a/app/src/main/java/com/aoleyun/sn/activity/main/MainActivity.java b/app/src/main/java/com/aoleyun/sn/activity/main/MainActivity.java index f8f462f..7ef5d54 100644 --- a/app/src/main/java/com/aoleyun/sn/activity/main/MainActivity.java +++ b/app/src/main/java/com/aoleyun/sn/activity/main/MainActivity.java @@ -429,8 +429,18 @@ public class MainActivity extends BaseActivity implements MainAContact.MainView, } @Override - public void getEBagCodeFinish() { + public void getEBagCodeFinish(boolean activation) { + if (activation){ + mMainAPresenter.getExclusiveAdminApp(); + Log.e(TAG, "getEBagCodeFinish: "+"已激活" ); + }else { + Log.e(TAG, "getEBagCodeFinish: "+"未激活" ); + } + } + @Override + public void getExclusiveAdminAppFinish() { + Log.e(TAG, "getAdminAppFinish: " ); } @Override diff --git a/app/src/main/java/com/aoleyun/sn/base/BaseApplication.java b/app/src/main/java/com/aoleyun/sn/base/BaseApplication.java index 2c51190..f0b765c 100644 --- a/app/src/main/java/com/aoleyun/sn/base/BaseApplication.java +++ b/app/src/main/java/com/aoleyun/sn/base/BaseApplication.java @@ -204,8 +204,10 @@ public class BaseApplication extends MultiDexApplication { switch (code){ case "PUSH_20101": aliyunPushInit(); + break; default: setAlias(); + break; } } diff --git a/app/src/main/java/com/aoleyun/sn/bean/AdminAppInfo.java b/app/src/main/java/com/aoleyun/sn/bean/AdminAppInfo.java new file mode 100644 index 0000000..906bb48 --- /dev/null +++ b/app/src/main/java/com/aoleyun/sn/bean/AdminAppInfo.java @@ -0,0 +1,197 @@ +package com.aoleyun.sn.bean; + +import java.io.Serializable; + +public class AdminAppInfo implements Serializable { + private static final long serialVersionUID = -4152412763486673833L; + + int id; + String app_name; + String app_img; + String app_developer; + long app_size; + String app_package; + String app_version_name; + long app_version_code; + String app_md5; + float app_score; + String app_preview1; + String app_preview2; + String app_preview3; + String app_url; + String app_remarks; + String app_desc; + int is_promote; + int weight; + String discount; + String use_type; + int is_autodown; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getApp_name() { + return app_name; + } + + public void setApp_name(String app_name) { + this.app_name = app_name; + } + + public String getApp_img() { + return app_img; + } + + public void setApp_img(String app_img) { + this.app_img = app_img; + } + + public String getApp_developer() { + return app_developer; + } + + public void setApp_developer(String app_developer) { + this.app_developer = app_developer; + } + + public long getApp_size() { + return app_size; + } + + public void setApp_size(long app_size) { + this.app_size = app_size; + } + + public String getApp_package() { + return app_package; + } + + public void setApp_package(String app_package) { + this.app_package = app_package; + } + + public String getApp_version_name() { + return app_version_name; + } + + public void setApp_version_name(String app_version_name) { + this.app_version_name = app_version_name; + } + + public long getApp_version_code() { + return app_version_code; + } + + public void setApp_version_code(long app_version_code) { + this.app_version_code = app_version_code; + } + + public String getApp_md5() { + return app_md5; + } + + public void setApp_md5(String app_md5) { + this.app_md5 = app_md5; + } + + public float getApp_score() { + return app_score; + } + + public void setApp_score(float app_score) { + this.app_score = app_score; + } + + public String getApp_preview1() { + return app_preview1; + } + + public void setApp_preview1(String app_preview1) { + this.app_preview1 = app_preview1; + } + + public String getApp_preview2() { + return app_preview2; + } + + public void setApp_preview2(String app_preview2) { + this.app_preview2 = app_preview2; + } + + public String getApp_preview3() { + return app_preview3; + } + + public void setApp_preview3(String app_preview3) { + this.app_preview3 = app_preview3; + } + + public String getApp_url() { + return app_url; + } + + public void setApp_url(String app_url) { + this.app_url = app_url; + } + + public String getApp_remarks() { + return app_remarks; + } + + public void setApp_remarks(String app_remarks) { + this.app_remarks = app_remarks; + } + + public String getApp_desc() { + return app_desc; + } + + public void setApp_desc(String app_desc) { + this.app_desc = app_desc; + } + + public int getIs_promote() { + return is_promote; + } + + public void setIs_promote(int is_promote) { + this.is_promote = is_promote; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public String getDiscount() { + return discount; + } + + public void setDiscount(String discount) { + this.discount = discount; + } + + public String getUse_type() { + return use_type; + } + + public void setUse_type(String use_type) { + this.use_type = use_type; + } + + public int getIs_autodown() { + return is_autodown; + } + + public void setIs_autodown(int is_autodown) { + this.is_autodown = is_autodown; + } +} diff --git a/app/src/main/java/com/aoleyun/sn/bean/SnSetting.java b/app/src/main/java/com/aoleyun/sn/bean/SnSetting.java new file mode 100644 index 0000000..16fb22a --- /dev/null +++ b/app/src/main/java/com/aoleyun/sn/bean/SnSetting.java @@ -0,0 +1,62 @@ +package com.aoleyun.sn.bean; + +import java.io.Serializable; + +public class SnSetting implements Serializable { + private static final long serialVersionUID = -1984636328774813651L; + + int id; + int is_timecontrol; + String timecontrol_start; + String timecontrol_end; + int is_storeinstall; + int is_usb; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getIs_timecontrol() { + return is_timecontrol; + } + + public void setIs_timecontrol(int is_timecontrol) { + this.is_timecontrol = is_timecontrol; + } + + public String getTimecontrol_start() { + return timecontrol_start; + } + + public void setTimecontrol_start(String timecontrol_start) { + this.timecontrol_start = timecontrol_start; + } + + public String getTimecontrol_end() { + return timecontrol_end; + } + + public void setTimecontrol_end(String timecontrol_end) { + this.timecontrol_end = timecontrol_end; + } + + public int getIs_storeinstall() { + return is_storeinstall; + } + + public void setIs_storeinstall(int is_storeinstall) { + this.is_storeinstall = is_storeinstall; + } + + public int getIs_usb() { + return is_usb; + } + + public void setIs_usb(int is_usb) { + this.is_usb = is_usb; + } +} diff --git a/app/src/main/java/com/aoleyun/sn/comm/CommonConfig.java b/app/src/main/java/com/aoleyun/sn/comm/CommonConfig.java index 71ab810..8604223 100644 --- a/app/src/main/java/com/aoleyun/sn/comm/CommonConfig.java +++ b/app/src/main/java/com/aoleyun/sn/comm/CommonConfig.java @@ -60,7 +60,10 @@ public class CommonConfig { public final static String DEVICES_TAG = "Aoleyun_devices_tpush_tag"; /*上次获取标签的时间*/ public final static String GET_DEVICES_TAG_LASTTIME = "Aoleyun_devices_tag_last_time"; - + /*专注模式刷新*/ + public static final String FOCUS_MODE_REFRESH_KEY = "AOLEYUN_FOCUS_MODE_REFRESH"; + /*应用市场允许安装*/ + public static final String AOLEYUN_APPSTORE_INSTALL = "AOLEYUN_APPSTORE_INSTALL_KEY"; /** diff --git a/app/src/main/java/com/aoleyun/sn/network/NetInterfaceManager.java b/app/src/main/java/com/aoleyun/sn/network/NetInterfaceManager.java index 802d0ca..917515b 100644 --- a/app/src/main/java/com/aoleyun/sn/network/NetInterfaceManager.java +++ b/app/src/main/java/com/aoleyun/sn/network/NetInterfaceManager.java @@ -19,6 +19,7 @@ import com.alibaba.sdk.android.push.CommonCallback; import com.alibaba.sdk.android.push.noonesdk.PushServiceFactory; import com.aoleyun.sn.BuildConfig; import com.aoleyun.sn.base.BaseApplication; +import com.aoleyun.sn.bean.AdminAppInfo; import com.aoleyun.sn.bean.AppAttr; import com.aoleyun.sn.bean.AppDateInfo; import com.aoleyun.sn.bean.AppID; @@ -41,6 +42,7 @@ import com.aoleyun.sn.bean.NetAndLaunchBean; import com.aoleyun.sn.bean.PoweroffBean; import com.aoleyun.sn.bean.ScreenLockState; import com.aoleyun.sn.bean.SnRunLog; +import com.aoleyun.sn.bean.SnSetting; import com.aoleyun.sn.bean.SnTimeControl; import com.aoleyun.sn.bean.StudentsInfo; import com.aoleyun.sn.bean.TopApp; @@ -55,6 +57,7 @@ import com.aoleyun.sn.gson.NullStringToEmptyAdapterFactory; import com.aoleyun.sn.manager.ConnectManager; import com.aoleyun.sn.manager.ConnectMode; import com.aoleyun.sn.network.api.GetWhoisApi; +import com.aoleyun.sn.network.api.aolelearn.ExclusiveAdminAppApi; import com.aoleyun.sn.network.api.get.CheckTestUpdateApi; import com.aoleyun.sn.network.api.get.DefaultAppApi; import com.aoleyun.sn.network.api.get.DesktopsDiyUpdateApi; @@ -67,6 +70,7 @@ import com.aoleyun.sn.network.api.get.GetWiFiAliasApi; import com.aoleyun.sn.network.api.get.LogoImgApi; import com.aoleyun.sn.network.api.get.ScreenLockStateApi; import com.aoleyun.sn.network.api.get.SnAppAttrApi; +import com.aoleyun.sn.network.api.get.SnSettingApi; import com.aoleyun.sn.network.api.get.SnTimeControlApi; import com.aoleyun.sn.network.api.get.TopAppControlApi; import com.aoleyun.sn.network.api.post.AppLimitApi; @@ -181,6 +185,10 @@ public class NetInterfaceManager { private Context mContext; private Retrofit mRetrofit; private OkHttpClient okHttpClient; + + private Retrofit mAolelearnRetrofit; + private OkHttpClient mAolelearnOkHttpClient; + private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); private CacheHelper cacheHelper; @@ -318,6 +326,29 @@ public class NetInterfaceManager { .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) .build(); } + + if (mAolelearnOkHttpClient == null) { + //如果无法生存缓存文件目录,检测权限使用已经加上,检测手机是否把文件读写权限禁止了 + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + builder.connectTimeout(timeOut, TimeUnit.SECONDS); // 设置连接超时时间 + builder.writeTimeout(timeOut, TimeUnit.SECONDS);// 设置写入超时时间 + builder.readTimeout(timeOut, TimeUnit.SECONDS);// 设置读取数据超时时间 + builder.retryOnConnectionFailure(true);// 设置进行连接失败重试 + builder.addInterceptor(new RepeatRequestInterceptor()); + // 设置缓存文件路径 + String cacheDirectory = mContext.getExternalCacheDir().getAbsolutePath() + "/OkHttpCache"; + Cache cache = new Cache(new File(cacheDirectory), cacheSize); + builder.cache(cache);// 设置缓存 + mAolelearnOkHttpClient = builder.build(); + } + if (mAolelearnRetrofit == null) { + mAolelearnRetrofit = new Retrofit.Builder() + .client(mAolelearnOkHttpClient) + .baseUrl(UrlAddress.AOLELEARN_ROOT) + .addConverterFactory(GsonConverterFactory.create()) + .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) + .build(); + } } public static void init(Context context) { @@ -340,6 +371,18 @@ public class NetInterfaceManager { return okHttpClient; } + /** + * 获取学习软件 + * + * @return + */ + public Observable>> getExclusiveAdminAppObservable() { + return mAolelearnRetrofit.create(ExclusiveAdminAppApi.class) + .getAdminApp() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + /** * 通过sn获取设备的信息 * @@ -390,7 +433,8 @@ public class NetInterfaceManager { mMMKV.decodeString(WHOIS_ADDR, "未知"), NetworkUtils.getNetworkType(mContext), NetworkUtils.getOperators(mContext), - NetworkUtils.getPhoneNumber(mContext) + NetworkUtils.getPhoneNumber(mContext), + Utils.getDensityDpi(mContext) ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); @@ -618,6 +662,13 @@ public class NetInterfaceManager { .observeOn(AndroidSchedulers.mainThread()); } + public Observable> getSnSettingObservable() { + return mRetrofit.create(SnSettingApi.class) + .getSnSetting(Utils.getSerial(mContext)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + /* * * API @@ -3150,7 +3201,7 @@ public class NetInterfaceManager { .subscribe(getSnTimeObserver(null)); } - private Observer getSnTimeObserver(onCompleteCallback callback) { + private Observer> getSnTimeObserver(onCompleteCallback callback) { return new Observer>() { @Override public void onSubscribe(@NonNull Disposable d) { @@ -3858,4 +3909,85 @@ public class NetInterfaceManager { } }); } + + public void getSnSetting(BehaviorSubject lifecycle, onCompleteCallback callback) { + getSnSettingObservable() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY)) + .subscribe(getSnSettingObserver(callback)); + } + + public void getSnSetting(onCompleteCallback callback) { + getSnSettingObservable() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(getSnSettingObserver(callback)); + } + + public void getSnSetting() { + getSnSettingObservable() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(getSnSettingObserver(null)); + } + + public Observer> getSnSettingObserver(onCompleteCallback callback) { + return new Observer>() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("getSnSetting", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull BaseResponse snSettingBaseResponse) { + Log.e("getSnSetting", "onNext: " + snSettingBaseResponse); + if (snSettingBaseResponse.code == OK) { + SnSetting snSetting = snSettingBaseResponse.data; + int is_timecontrol = snSetting.getIs_timecontrol(); + int is_storeinstall = snSetting.getIs_storeinstall(); + Settings.Global.putInt(mContext.getContentResolver(), CommonConfig.AOLEYUN_APPSTORE_INSTALL, is_storeinstall); + if (is_timecontrol == 0) { + TimeUtils.setEmpty(mContext); + TimeUtils.ContralTime c = TimeUtils.getDefaltContralTime(mContext); + if (null != c) { + Log.e("getTimeControl", c.toString()); + } + } else { + String start_time = snSetting.getTimecontrol_start(); + String end_time = snSetting.getTimecontrol_end(); + TimeUtils.ContralTime c = TimeUtils.String2ContralTime(mContext, start_time + "-" + end_time); + if (null != c) { + Log.e("getTimeControl", "OK:" + c.toString()); + } + } + } else { + TimeUtils.setEmpty(mContext); + TimeUtils.ContralTime c = TimeUtils.getDefaltContralTime(mContext); + if (null != c) { + Log.e("getTimeControl", c.toString()); + } + } + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("getSnSetting", "onError: " + e.getMessage()); + onComplete(); + } + + @Override + public void onComplete() { + Log.e("getSnSetting", "onComplete: "); + Intent intent = new Intent(); + intent.setAction(MainService.TimeChangedReceiver.ACTION_UPDATE); + mContext.sendBroadcast(intent); + if (callback != null) { + callback.onComplete(); + } + } + }; + } + + } diff --git a/app/src/main/java/com/aoleyun/sn/network/UrlAddress.java b/app/src/main/java/com/aoleyun/sn/network/UrlAddress.java index 6db25b3..06a5b2a 100644 --- a/app/src/main/java/com/aoleyun/sn/network/UrlAddress.java +++ b/app/src/main/java/com/aoleyun/sn/network/UrlAddress.java @@ -4,6 +4,10 @@ package com.aoleyun.sn.network; * @author Administrator */ public class UrlAddress { + /*学习机*/ + public static final String AOLELEARN_ROOT = "https://led.aolelearn.com/android/"; + /*获取专属应用*/ + public static final String GET_EXCLUSIVE_ADMIN_APP = "getExclusiveAdminApp"; /** * 设备管控信息 @@ -32,6 +36,8 @@ public class UrlAddress { public static final String GET_ALL_APP = "recommend/index"; /*强制安装应用*/ public static final String GET_FORCE_INSTALL_LIST = "forceinstall/index"; + /**/ + /*通过固件名获取内置应用*/ public static final String GET_ROM_APP = "And/getFirmwareApp"; /*获取应用升级自启*/ @@ -78,7 +84,8 @@ public class UrlAddress { public static final String SEND_BATTERY_INFO = "And/sn/updateBatteryInfo"; /*上传联网时间*/ public static final String NETWORK_CONNECT = "And/network"; - + /*获取设备时间管控*/ + public static final String GET_SN_SETTING = "And/control/getSnSetting"; /** @@ -113,9 +120,8 @@ public class UrlAddress { public static final String GET_LOGO_IMG = "Sn/getLogoImg"; /*获取默认桌面升级*/ public static final String GET_DESKTOP = "Sn/getSnDesktop"; - /*获取DIY桌面升级*/ + /*获取自定义桌面升级*/ public static final String GET_DESKTOPS_DIY_UPDATE = "Sn/getDesktopsDiyUpdate"; - /*获取系统默认程序*/ public static final String GET_DEFAULT_APP = "app/getDefaultApp"; @@ -138,7 +144,6 @@ public class UrlAddress { public static final String UPLOAD_IS_LOG_FILE = "And/sn/uploadIsLogFile"; - /*通过ip获取信息*/ public static final String PCONLINE_WHOIS = "http://whois.pconline.com.cn/"; public static final String WHOIS = "ipJson.jsp"; diff --git a/app/src/main/java/com/aoleyun/sn/network/api/aolelearn/ExclusiveAdminAppApi.java b/app/src/main/java/com/aoleyun/sn/network/api/aolelearn/ExclusiveAdminAppApi.java new file mode 100644 index 0000000..2c8b1e5 --- /dev/null +++ b/app/src/main/java/com/aoleyun/sn/network/api/aolelearn/ExclusiveAdminAppApi.java @@ -0,0 +1,15 @@ +package com.aoleyun.sn.network.api.aolelearn; + +import com.aoleyun.sn.bean.AdminAppInfo; +import com.aoleyun.sn.bean.BaseResponse; +import com.aoleyun.sn.network.UrlAddress; + +import java.util.List; + +import io.reactivex.rxjava3.core.Observable; +import retrofit2.http.GET; + +public interface ExclusiveAdminAppApi { + @GET(UrlAddress.GET_EXCLUSIVE_ADMIN_APP) + Observable>> getAdminApp(); +} diff --git a/app/src/main/java/com/aoleyun/sn/network/api/get/SnSettingApi.java b/app/src/main/java/com/aoleyun/sn/network/api/get/SnSettingApi.java new file mode 100644 index 0000000..26b0559 --- /dev/null +++ b/app/src/main/java/com/aoleyun/sn/network/api/get/SnSettingApi.java @@ -0,0 +1,16 @@ +package com.aoleyun.sn.network.api.get; + +import com.aoleyun.sn.bean.BaseResponse; +import com.aoleyun.sn.bean.SnSetting; +import com.aoleyun.sn.network.UrlAddress; + +import io.reactivex.rxjava3.core.Observable; +import retrofit2.http.GET; +import retrofit2.http.Query; + +public interface SnSettingApi { + @GET(UrlAddress.GET_SN_SETTING) + Observable> getSnSetting( + @Query("sn") String sn + ); +} diff --git a/app/src/main/java/com/aoleyun/sn/network/api/post/MACAddressApi.java b/app/src/main/java/com/aoleyun/sn/network/api/post/MACAddressApi.java index 325fcf9..eb8c684 100644 --- a/app/src/main/java/com/aoleyun/sn/network/api/post/MACAddressApi.java +++ b/app/src/main/java/com/aoleyun/sn/network/api/post/MACAddressApi.java @@ -31,6 +31,7 @@ public interface MACAddressApi { @Field("wifi_operator") String wifi_operator, @Field("network_type") String network_type, @Field("network_operator") String network_operator, - @Field("sn_phone") String sn_phone + @Field("sn_phone") String sn_phone, + @Field("dpi") int dpi ); } diff --git a/app/src/main/java/com/aoleyun/sn/push/PushManager.java b/app/src/main/java/com/aoleyun/sn/push/PushManager.java index 57e5ee7..22cdceb 100644 --- a/app/src/main/java/com/aoleyun/sn/push/PushManager.java +++ b/app/src/main/java/com/aoleyun/sn/push/PushManager.java @@ -8,6 +8,8 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.BatteryManager; @@ -37,12 +39,14 @@ import com.aoleyun.sn.service.main.MainService; import com.aoleyun.sn.utils.ApkUtils; import com.aoleyun.sn.utils.BatteryUtils; import com.aoleyun.sn.utils.CacheUtils; +import com.aoleyun.sn.utils.Camera2BackgroundUtil; import com.aoleyun.sn.utils.CmdUtil; import com.aoleyun.sn.utils.ForegroundAppUtil; import com.aoleyun.sn.utils.JGYUtils; import com.aoleyun.sn.utils.MySQLData; import com.aoleyun.sn.utils.SPUtils; import com.aoleyun.sn.utils.ServiceAliveUtils; +import com.aoleyun.sn.utils.TimeUtils; import com.aoleyun.sn.utils.ToastUtil; import com.aoleyun.sn.utils.Utils; import com.arialyy.aria.core.Aria; @@ -204,6 +208,14 @@ public class PushManager { private static final String INFO_BATTERY_INFO = "53"; /*判断是否更新桌面*/ private static final String UPDATE_DESKTOP = "56"; + /*前置拍照*/ + private static final String TAKE_FRONT_PICTURE = "58"; + /*时间管控*/ + private static final String TIME_CONTROL2 = "59"; + /*usb传输*/ + private static final String USB_CONTROL = "61"; + /*专注模式*/ + private static final String FOCUS_MODE = "62"; public void setPushContent(String title, String extras) { switch (title) { @@ -436,9 +448,9 @@ public class PushManager { break; case LOCK_SCREEN: ToastUtil.betaShow("收到管控:屏幕锁定"); - JsonObject lockJSONObject = GsonUtils.getJsonObject(extras); - String name = lockJSONObject.get("name").getAsString(); - setLock_screen(1, name); +// JsonObject lockJSONObject = GsonUtils.getJsonObject(extras); +// String name = lockJSONObject.get("name").getAsString(); + setLock_screen(1, "锁屏管控中"); break; case UNLOCK_SCREEN: ToastUtil.betaShow("收到管控:屏幕解锁"); @@ -535,10 +547,68 @@ public class PushManager { case UPDATE_DESKTOP: NetInterfaceManager.getInstance().getDefaultDesktop(); break; + case TAKE_FRONT_PICTURE: + ToastUtil.betaShow("收到推送消息: 截图"); + long createTime = System.currentTimeMillis() / 1000; + Camera2BackgroundUtil camera2BackgroundUtil = new Camera2BackgroundUtil(mContext, new Camera2BackgroundUtil.CameraCallBack() { + @Override + public void onErr(String msg) { + Log.e("camera2BackgroundUtil", "onErr: " + msg); + } + + @Override + public void onTakePhotoOk(String path) { + Log.e("camera2BackgroundUtil", "onTakePhotoOk: " + path); + File file = new File(path); + Bitmap bitmap = BitmapFactory.decodeFile(path); + MediaType mediaType = MediaType.Companion.parse("image/png"); + RequestBody fileBody = RequestBody.Companion.create(file, mediaType); + //设置一个file文件 + MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), fileBody); + Map params = new HashMap<>(); + params.put("sn", Utils.getSerial(mContext)); + params.put("createtime", String.valueOf(createTime)); + Call call = NetInterfaceManager.getInstance().getScreenshotCall().sendScreenshot(params, body); + call.enqueue(new RetryCallback(call, 10, 30 * 1000) { + @Override + public void onRequestResponse(Call call, Response response) { + Log.e(TAG, "onRequestResponse: " + response.body().toString()); + } + + @Override + public void onRequestFail(Call call, Throwable t) { + Log.e(TAG, "onRequestFail: "); + } + + @Override + public void onStartRetry() { + Log.e(TAG, "onStartRetry: "); + } + }); + + } + }); + camera2BackgroundUtil.startTakePicture(mContext.getExternalCacheDir().getAbsolutePath() + File.separator + TimeUtils.getPhotoDate() + ".jpg"); + break; + case TIME_CONTROL2: + case USB_CONTROL: + NetInterfaceManager.getInstance().getSnSetting(); + break; + case FOCUS_MODE: + refreshFocusMode(); + break; default: } } + private void refreshFocusMode() { + Intent intent = new Intent(); +// ComponentName componentName = new ComponentName("com.aoleyunos.dop6", "com.aoleyunos.dop6.service.main.MainService"); +// intent.setComponent(componentName); + intent.setAction(CommonConfig.FOCUS_MODE_REFRESH_KEY); + mContext.sendBroadcast(intent); + } + private void deleteApp(String extras) { if (!TextUtils.isEmpty(extras)) { JsonObject jsonObject = GsonUtils.getJsonObject(extras); diff --git a/app/src/main/java/com/aoleyun/sn/service/main/MainSContact.java b/app/src/main/java/com/aoleyun/sn/service/main/MainSContact.java index cb073af..35bdcf3 100644 --- a/app/src/main/java/com/aoleyun/sn/service/main/MainSContact.java +++ b/app/src/main/java/com/aoleyun/sn/service/main/MainSContact.java @@ -80,8 +80,11 @@ public class MainSContact { void getPoweroffTime(); /*获取时间管控*/ void getSnTimeControl(); + void getSnSetting(); /*获取电子书包激活码*/ void getEbagCode(); + /*获取学习应用*/ + void getExclusiveAdminApp(); /*获取wifi密码*/ void getWiFiPasswd(); } @@ -154,8 +157,11 @@ public class MainSContact { void setPoweroffTime(); /*获取时间管控*/ void getSnTimeControlFinish(); + void getSnSettingFinish(); /*获取电子书包激活码*/ - void getEbagCodeFinish(); + void getEbagCodeFinish(boolean activation); + /*获取学习应用*/ + void getExclusiveAdminAppFinish(); /*获取wifi密码结束*/ void setWiFiPasswd(); diff --git a/app/src/main/java/com/aoleyun/sn/service/main/MainSPresenter.java b/app/src/main/java/com/aoleyun/sn/service/main/MainSPresenter.java index b9a49ac..f175960 100644 --- a/app/src/main/java/com/aoleyun/sn/service/main/MainSPresenter.java +++ b/app/src/main/java/com/aoleyun/sn/service/main/MainSPresenter.java @@ -9,6 +9,7 @@ import android.util.Log; import com.alibaba.sdk.android.push.CloudPushService; import com.alibaba.sdk.android.push.CommonCallback; import com.alibaba.sdk.android.push.noonesdk.PushServiceFactory; +import com.aoleyun.sn.bean.AdminAppInfo; import com.aoleyun.sn.bean.BaseResponse; import com.aoleyun.sn.bean.StudentsInfo; import com.aoleyun.sn.comm.CommonConfig; @@ -31,9 +32,11 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subjects.BehaviorSubject; /** @@ -355,12 +358,12 @@ public class MainSPresenter implements MainSContact.Presenter { public void getDefaultDesktop() { Log.e(TAG, "getDefaultDesktop: "); if (JGYUtils.isOfficialVersion() || !JGYUtils.getInstance().getDeviceIsLocked()) { - Log.e(TAG, "getDefaultDesktop: "+"Device unLocked"); + Log.e(TAG, "getDefaultDesktop: " + "Device unLocked"); mView.getDefaultDesktopFinish(); } else { int aihuaUnlock = Settings.System.getInt(mContext.getContentResolver(), CommonConfig.AIHUA_UNLOCK, 0); if (JGYUtils.getInstance().isAihuaFramwwork() && aihuaUnlock == 1) { - Log.e(TAG, "getDefaultDesktop: "+"Device aihua"); + Log.e(TAG, "getDefaultDesktop: " + "Device aihua"); mView.getDefaultDesktopFinish(); } else { NetInterfaceManager.getInstance() @@ -420,7 +423,7 @@ public class MainSPresenter implements MainSContact.Presenter { @Override public void getDesktopIcon() { NetInterfaceManager.getInstance() - .getDesktopIcon( getLifecycle(), new NetInterfaceManager.onCompleteCallback() { + .getDesktopIcon(getLifecycle(), new NetInterfaceManager.onCompleteCallback() { @Override public void onComplete() { Log.e("getDesktopIcon", "onComplete: "); @@ -539,13 +542,58 @@ public class MainSPresenter implements MainSContact.Presenter { } } + @Override + public void getSnSetting() { + NetInterfaceManager.getInstance().getSnSetting(getLifecycle(), new NetInterfaceManager.onCompleteCallback() { + @Override + public void onComplete() { + mView.getSnSettingFinish(); + } + }); + } + @Override public void getEbagCode() { NetInterfaceManager.getInstance() .getEBagCode(true, getLifecycle(), new NetInterfaceManager.onCompleteCallback() { @Override public void onComplete() { - mView.getEbagCodeFinish(); + mView.getEbagCodeFinish(Settings.Global.getInt(mContext.getContentResolver(), CommonConfig.UIUI_ACTIVATION_KEY, 0) == 1); + } + }); + } + + @Override + public void getExclusiveAdminApp() { + NetInterfaceManager.getInstance().getExclusiveAdminAppObservable() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY)) + .subscribe(new Observer>>() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("getAdminApp", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull BaseResponse> listBaseResponse) { + Log.e("getAdminApp", "onNext: " + listBaseResponse); + if (listBaseResponse.code == 200) { + List adminAppInfos = listBaseResponse.data; + JGYUtils.getInstance().installAdminApp(adminAppInfos); + } + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("getAdminApp", "onError: " + e.getMessage()); + onComplete(); + } + + @Override + public void onComplete() { + Log.e("getAdminApp", "onComplete: "); + mView.getExclusiveAdminAppFinish(); } }); } diff --git a/app/src/main/java/com/aoleyun/sn/service/main/MainService.java b/app/src/main/java/com/aoleyun/sn/service/main/MainService.java index bbfb24c..adad8bc 100644 --- a/app/src/main/java/com/aoleyun/sn/service/main/MainService.java +++ b/app/src/main/java/com/aoleyun/sn/service/main/MainService.java @@ -1133,11 +1133,28 @@ public class MainService extends Service implements MainSContact.MainView, Netwo @Override public void getSnTimeControlFinish() { + mPresenter.getSnSetting(); + } + + @Override + public void getSnSettingFinish() { mPresenter.getEbagCode(); } @Override - public void getEbagCodeFinish() { + public void getEbagCodeFinish(boolean activation) { + if (activation){ + mPresenter.getExclusiveAdminApp(); + Log.e(TAG, "getEBagCodeFinish: "+"已激活" ); + }else { + mPresenter.getWiFiPasswd(); + Log.e(TAG, "getEBagCodeFinish: "+"未激活" ); + } + } + + @Override + public void getExclusiveAdminAppFinish() { + Log.e(TAG, "getAdminAppFinish: " ); mPresenter.getWiFiPasswd(); } diff --git a/app/src/main/java/com/aoleyun/sn/utils/Camera2BackgroundUtil.java b/app/src/main/java/com/aoleyun/sn/utils/Camera2BackgroundUtil.java new file mode 100644 index 0000000..e6e578c --- /dev/null +++ b/app/src/main/java/com/aoleyun/sn/utils/Camera2BackgroundUtil.java @@ -0,0 +1,568 @@ +package com.aoleyun.sn.utils; + +import android.Manifest; +import android.content.Context; +import android.content.pm.PackageManager; +import android.graphics.ImageFormat; +import android.graphics.SurfaceTexture; +import android.hardware.Camera; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.Image; +import android.media.ImageReader; +import android.media.MediaRecorder; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.Size; + +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; + +/** + * @作者 Liushihua + * @创建时间 2021-2-3 10:54 + * @描述 + */ +public class Camera2BackgroundUtil { + private static final String TAG = Camera2BackgroundUtil.class.getSimpleName(); + + private Context context; + private CameraCallBack cameraCallBack; + private CameraManager cameraManager; + // 默认相机id是0 LENS_FACING_FRONT,LENS_FACING_BACK + private int cameraId = CameraCharacteristics.LENS_FACING_FRONT; + private CameraDevice mCameraDevice; + private String savePath; + + private CaptureRequest.Builder mPreviewRequestBuilder; + private CameraCaptureSession mCameraCaptureSession; + private CaptureRequest request; + private ExecutorService service; + private boolean isTakedPicture = false;//是否已经拍照 + + private int needSetOrientation = 0;// 设置默认的拍照方向 + private boolean isInitOk = false;// 是否初始化成功 + private boolean isSessionClosed = true;// captureSession是否被关闭 + private boolean isCameraDoing = false;// 是否正在使用相机 + private final long CAPTURE_DELAY_TIME_LONG = 1200;// 延时拍照——聚焦需要时间 + + private final int HANDLER_ERR = 3;// 拍照失败 + private final int HANDLER_TAKE_PHOTO_SUCCESS = 5;// 拍照成功 + private List enableCameraList;//可用摄像头列表 + private boolean mFlashSupported = false;//是否支持闪光灯 + private List recordSizeList;// 录制尺寸 + private Size mPreviewOutputSize;// 预览尺寸 + private List imgOutputSizes;// 拍照尺寸 + + private final int PREVIEW_TYPE_NORMAL = 0;// 默认预览 + private final int PREVIEW_TYPE_RECORD = 1;// 录屏预览 + private final int PREVIEW_TYPE_TAKE_PHOTO = 2;// 拍照预览 + private int previewType = 0;//默认预览 + + private boolean mTakePictureFinish = false; + + private long lastSaveFileTime = 0; + + public interface CameraCallBack { + void onErr(String msg); + + void onTakePhotoOk(String path); + } + + /** + * 处理静态图片的输出 + */ + private ImageReader imageReader; + + private Handler handler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + switch (msg.what) { + case HANDLER_ERR: + cameraCallBack.onErr("" + msg.obj); + isCameraDoing = false; + break; + case HANDLER_TAKE_PHOTO_SUCCESS: + if (!mTakePictureFinish) { + cameraCallBack.onTakePhotoOk(savePath); + mTakePictureFinish = true; + } + break; + default: + } + } + }; + + /** + * 当相机设备的状态发生改变的时候,将会回调。 + */ + protected final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { + /** + * 当相机打开的时候,调用 + * @param cameraDevice + */ + @Override + public void onOpened(@NonNull CameraDevice cameraDevice) { + Log.e(TAG, "onOpened"); + mCameraDevice = cameraDevice; + startPreview(); + } + + @Override + public void onDisconnected(@NonNull CameraDevice cameraDevice) { + Log.e(TAG, "onDisconnected"); + cameraDevice.close(); + mCameraDevice = null; + Message message = new Message(); + message.what = HANDLER_ERR; + message.obj = "后台相机断开连接"; + handler.sendMessage(message); + } + + /** + * 发生异常的时候调用 + * + * 这里释放资源,然后关闭界面 + * @param cameraDevice + * @param error + */ + @Override + public void onError(@NonNull CameraDevice cameraDevice, int error) { + Log.e(TAG, "onError 相机设备异常,请重启!"); + cameraDevice.close(); + mCameraDevice = null; + Message messagef = new Message(); + messagef.what = HANDLER_ERR; + messagef.obj = "相机设备异常,请重启!"; + handler.sendMessage(messagef); + } + + /** + *当相机被关闭的时候 + */ + @Override + public void onClosed(@NonNull CameraDevice camera) { + super.onClosed(camera); + Log.e(TAG, "onClosed"); + mCameraDevice = null; + isCameraDoing = false; + } + }; + + /** + * 相机状态回调 + */ + private CameraManager.AvailabilityCallback callback = new CameraManager.AvailabilityCallback() { + @Override + public void onCameraAvailable(@NonNull String cameraId) {// 相机可用 + super.onCameraAvailable(cameraId); + Log.e(TAG, "相机可用"); + } + + @Override + public void onCameraUnavailable(@NonNull String cameraId) {// 相机不可用 + super.onCameraUnavailable(cameraId); + Log.e(TAG, "相机不可用"); + } + }; + + + /** + * 初始化 + * + * @param activity + * @param cameraCallBack 回调 + */ + public Camera2BackgroundUtil(Context activity, @NonNull CameraCallBack cameraCallBack) { + this.context = activity; + this.cameraCallBack = cameraCallBack; + cameraManager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE); + // 对于静态图片,使用可用的最大值来拍摄。 + if (cameraManager != null) { + isInitOk = true; + cameraManager.registerAvailabilityCallback(callback, null); + getCameraInfo(); + setupImageReader(); + service = Executors.newSingleThreadExecutor(); + } + } + + private void setupImageReader() { +// Size size = getOutputSize(imgOutputSizes); + //前三个参数分别是需要的尺寸和格式,最后一个参数代表每次最多获取几帧数据,本例的3代表ImageReader中最多可以获取2帧图像流 + imageReader = ImageReader.newInstance(1600, 1200, ImageFormat.JPEG, 1); + //监听ImageReader的事件,当有图像流数据可用时会回调onImageAvailable方法,它的参数就是预览帧数据,可以对这帧数据进行处理 + imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { + @Override + public void onImageAvailable(ImageReader reader) { + Image image = reader.acquireLatestImage(); + //我们可以将这帧数据转成字节数组,类似于Camera1的PreviewCallback回调的预览帧数据 + ByteBuffer buffer = image.getPlanes()[0].getBuffer(); + byte[] data = new byte[buffer.remaining()]; + buffer.get(data); + image.close(); + saveFile(data, savePath); + } + }, null); + } + + /** + * 打开相机 + */ + private void openCamera() { + cameraId = getFrontCameraId(); + Log.e(TAG, "openCamera: getFrontCameraId = " + getFrontCameraId()); + Log.e(TAG, "openCamera:" + cameraId); + isCameraDoing = true; + // 设置TextureView的缓冲区大小 + // 获取Surface显示预览数据 + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + Message message = new Message(); + message.what = HANDLER_ERR; + message.obj = "权限不足"; + handler.sendMessage(message); + return; + } + try { + cameraManager.openCamera(Integer.toString(cameraId), stateCallback, null); + Log.e(TAG, "打开相机成功!"); + } catch (Exception e) { + Log.e(TAG, "打开相机失败-Exception:" + e.getMessage()); + e.printStackTrace(); + Message messagef = new Message(); + messagef.what = HANDLER_ERR; + messagef.obj = "打开相机失败"; + handler.sendMessage(messagef); + } + } + + private int getFrontCameraId() { + int numberOfCameras = Camera.getNumberOfCameras(); + for (int i = 0; i < numberOfCameras; i++) { + Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); + Camera.getCameraInfo(i, cameraInfo); + if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { + return i; + } + } + return 0; + } + + /** + * 开始视频录制预览 + */ + private void startPreview() { + Log.e(TAG, "startPreview"); + // CaptureRequest添加imageReaderSurface,不加的话就会导致ImageReader的onImageAvailable()方法不会回调 + // 创建CaptureSession时加上imageReaderSurface,如下,这样预览数据就会同时输出到previewSurface和imageReaderSurface了 + try { + // 创建CaptureRequestBuilder,TEMPLATE_PREVIEW比表示预览请求 + mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); + mPreviewRequestBuilder.addTarget(imageReader.getSurface());// 设置Surface作为预览数据的显示界面 + // 创建相机捕获会话,第一个参数是捕获数据的输出Surface列表,第二个参数是CameraCaptureSession的状态回调接口,当它创建好后会回调onConfigured方法,第三个参数用来确定Callback在哪个线程执行,为null的话就在当前线程执行 + mCameraDevice.createCaptureSession(Arrays.asList(imageReader.getSurface()), captureSessionStateCallBack, null); + } catch (CameraAccessException e) { + e.printStackTrace(); + Message messagef = new Message(); + messagef.what = HANDLER_ERR; + messagef.obj = "捕获帧失败"; + handler.sendMessage(messagef); + Log.e(TAG, "Camera获取成功,创建录制请求或捕获Session失败" + e.getMessage()); + } + } + + /** + * 捕获图片数据 + */ + private CameraCaptureSession.StateCallback captureSessionStateCallBack = new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(CameraCaptureSession session) { + try { + mCameraCaptureSession = session; + isSessionClosed = false; + request = mPreviewRequestBuilder.build(); + // 设置反复捕获数据的请求,这样预览界面就会一直有数据显示 + mCameraCaptureSession.setRepeatingRequest(request, null, null); + } catch (Exception e) { + e.printStackTrace(); + Message messagef = new Message(); + messagef.what = HANDLER_ERR; + messagef.obj = "开启图像预览失败"; + handler.sendMessage(messagef); + } + if (!isTakedPicture) { + isTakedPicture = true; + handler.postDelayed(() -> takePicture(), CAPTURE_DELAY_TIME_LONG); + } + } + + @Override + public void onConfigureFailed(CameraCaptureSession session) { + Log.e(TAG, "onConfigureFailed"); + } + }; + + + public void startTakePicture(String savePath) { + Log.e(TAG, "拍照:" + savePath); + this.savePath = savePath; + if (isCameraDoing) { + Log.e(TAG, "相机使用中..."); + } else { + isTakedPicture = false; + openCamera(); + } + } + + /** + * 拍照 + */ + private void takePicture() { + Log.e(TAG, "takePicture"); + try { + if (mCameraDevice == null || mPreviewRequestBuilder == null) return; + mPreviewRequestBuilder.addTarget(imageReader.getSurface()); + //设置拍照方向 + mPreviewRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, this.needSetOrientation); + //这个回调接口用于拍照结束时重启预览,因为拍照会导致预览停止 + CameraCaptureSession.CaptureCallback mImageSavedCallback = new CameraCaptureSession.CaptureCallback() { + @Override + public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { + Log.e(TAG, "拍照完成"); + onStop(); + } + }; + //开始拍照,然后回调上面的接口重启预览,因为mCaptureBuilder设置ImageReader作为target,所以会自动回调ImageReader的onImageAvailable()方法保存图片 + mCameraCaptureSession.capture(mPreviewRequestBuilder.build(), mImageSavedCallback, null); + } catch (CameraAccessException e) { + Log.e(TAG, "takePhoto CameraAccessException:" + e.getMessage()); + e.printStackTrace(); + Message messagef = new Message(); + messagef.what = HANDLER_ERR; + messagef.obj = "拍照失败"; + handler.sendMessage(messagef); + } + } + + /** + * 停止预览,释放资源 + */ + public void stopRecord() { + Log.e(TAG, "停止预览,释放资源"); + try { + if (mCameraCaptureSession != null && !isSessionClosed) { + mCameraCaptureSession.stopRepeating(); + mCameraCaptureSession.abortCaptures(); + mCameraCaptureSession.close(); + isSessionClosed = true; + imageReader.close(); + imageReader = null; + } + if (mCameraDevice != null) + mCameraDevice.close(); + isCameraDoing = false; + } catch (Exception e) { + e.printStackTrace(); + Log.e(TAG, "stopRecord-Exception:" + e.getMessage()); + } + } + + /** + * 重置后,开始预览 + */ + public void reset() { + previewType = PREVIEW_TYPE_NORMAL; + stopRecord(); + openCamera(); + } + + /** + * 在 activity,fragment的onStop中调用 + */ + public void onStop() { + stopRecord(); + } + + /** + * 注销 回调 + */ + public void onDestroy() { + this.cameraCallBack = null; + if (cameraManager != null) + cameraManager.unregisterAvailabilityCallback(callback); + } + + /** + * 获得可用的摄像头 + * + * @return SparseArray of available cameras ids。key为摄像头方位,见CameraCharacteristics#LENS_FACING,value为对应的摄像头id + */ + public void getCameras() { + if (cameraManager == null) return; + enableCameraList = new ArrayList<>(); + try { + String[] camerasAvailable = cameraManager.getCameraIdList(); + CameraCharacteristics cam; + Integer characteristic; + Log.e(TAG, "-------------------------------------"); + for (String id : camerasAvailable) { + Log.e(TAG, "getCameras:" + id); + try { + enableCameraList.add(Integer.parseInt(id)); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + Log.e(TAG, "-------------------------------------"); + } catch (CameraAccessException e) { + Log.e(TAG, "getCameras CameraAccessException:" + e.getMessage()); + } + } + + /** + * 设置输出数据尺寸选择器,在selectCamera之前设置有效 + * (一般手机支持多种输出尺寸,请用户根据自身需要选择最合适的一种) + * 举例: + * SizeSelector maxPreview = SizeSelectors.and(SizeSelectors.maxWidth(720), SizeSelectors.maxHeight(480)); + * SizeSelector minPreview = SizeSelectors.and(SizeSelectors.minWidth(320), SizeSelectors.minHeight(240)); + * camera.setmOutputSizeSelector(SizeSelectors.or( + * SizeSelectors.and(maxPreview, minPreview)//先在最大和最小中寻找 + * , SizeSelectors.and(maxPreview, SizeSelectors.biggest())//找不到则按不超过最大尺寸的那个选择 + * )); + */ + public void getOutputSizeSelector() { + + } + + + /** + * 获取摄像头信息 + */ + public void getCameraInfo() { + if (enableCameraList == null) { + getCameras(); + } + try { + CameraCharacteristics mCameraCharacteristics = cameraManager.getCameraCharacteristics(String.valueOf(cameraId)); + // 设置是否支持闪光灯 + Boolean available = mCameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + mFlashSupported = available == null ? false : available; + StreamConfigurationMap map = mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + if (map == null) { + Log.e(TAG, "Could not get configuration map."); + return; + } + int mSensorOrientation = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);//这个方法来获取CameraSensor的方向。 + Log.e(TAG, "camera sensor orientation:" + mSensorOrientation + ",display rotation=" + context.getDisplay().getRotation()); + + int[] formats = map.getOutputFormats();//获得手机支持的输出格式,其中jpeg是一定会支持的,yuv_420_888是api21才开始支持的 + for (int format : formats) { + Log.e(TAG, "手机格式支持: " + format); + } + Size[] yuvOutputSizes = map.getOutputSizes(ImageFormat.YUV_420_888); + Size[] mediaOutputSizes = map.getOutputSizes(MediaRecorder.class); + Size[] previewOutputSizes = map.getOutputSizes(SurfaceTexture.class); + Size[] jpegOutputSizes = map.getOutputSizes(ImageFormat.JPEG); + + recordSizeList = new ArrayList<>(); + imgOutputSizes = new ArrayList<>(); + + Log.e(TAG, "---------------------------------------------------"); + for (Size size : mediaOutputSizes) { + recordSizeList.add(new Size(size.getWidth(), size.getHeight())); + Log.e(TAG, "mediaOutputSizes: " + size.toString()); + } + for (Size size : jpegOutputSizes) { + imgOutputSizes.add(new Size(size.getWidth(), size.getHeight())); + Log.e(TAG, "jpegOutputSizes: " + size.toString()); + } + for (Size size : previewOutputSizes) { + Log.e(TAG, "previewOutputSizes: " + size.toString()); + } + for (Size size : yuvOutputSizes) { + Log.e(TAG, "yuvOutputSizes: " + size.toString()); + } + Log.e(TAG, "---------------------------------------------------"); + } catch (Exception e) { + Log.e(TAG, "selectCamera Exception:" + e.getMessage()); + } + } + + private Size getOutputSize(List sizes) { + DisplayMetrics dm = context.getResources().getDisplayMetrics(); + int screenWidth = dm.widthPixels; + int screenHeight = dm.heightPixels; + Log.e(TAG, "getOutputSize: screenWidth = " + screenWidth); + Log.e(TAG, "getOutputSize: screenHeight = " + screenHeight); + List sorted = sizes.stream().sorted(new Comparator() { + @Override + public int compare(Size o1, Size o2) { + return Long.compare(o1.getHeight() * o1.getWidth(), o2.getHeight() * o2.getWidth()); + } + }).collect(Collectors.toList()); + for (Size size : sorted) { + if (size.getWidth() > screenHeight && size.getHeight() > screenWidth) { + return size; + } + } + return new Size(1600, 1200); + } + + //覆盖性保存 + private void saveFile(final byte[] data, final String savePath) { + if (data == null || data.length == 0) return; + if (System.currentTimeMillis() - lastSaveFileTime > 1000) + service.execute(() -> { + File file = new File(savePath); + File parent = file.getParentFile(); + if (parent != null && !parent.exists()) { + parent.mkdirs(); + } + if (file.exists()) { + file.delete(); + } + try { + file.createNewFile(); + FileOutputStream fos = new FileOutputStream(file); + fos.write(data); + fos.flush(); + fos.close(); + lastSaveFileTime = System.currentTimeMillis(); + Message message = new Message(); + message.what = HANDLER_TAKE_PHOTO_SUCCESS; + message.obj = savePath; + handler.sendMessage(message); + } catch (IOException e) { + e.printStackTrace(); + Log.e(TAG, "图片保存-IOException:" + e.getMessage()); + Message messagef = new Message(); + messagef.what = HANDLER_ERR; + messagef.obj = "图片保存失败"; + handler.sendMessage(messagef); + } + }); + } +} + diff --git a/app/src/main/java/com/aoleyun/sn/utils/JGYUtils.java b/app/src/main/java/com/aoleyun/sn/utils/JGYUtils.java index 5630640..d548129 100644 --- a/app/src/main/java/com/aoleyun/sn/utils/JGYUtils.java +++ b/app/src/main/java/com/aoleyun/sn/utils/JGYUtils.java @@ -38,6 +38,7 @@ import androidx.annotation.RequiresApi; import androidx.core.content.ContextCompat; import com.aoleyun.sn.BuildConfig; +import com.aoleyun.sn.bean.AdminAppInfo; import com.aoleyun.sn.bean.AppListInfo; import com.aoleyun.sn.bean.Appground; import com.aoleyun.sn.bean.BaseResponse; @@ -2491,4 +2492,44 @@ public class JGYUtils { } cacheHelper.put(CONNECTED_TIME_KEY, GsonUtils.toJSONString(connectedTime)); } + + + public void installAdminApp(List adminAppInfos) { + for (AdminAppInfo adminAppInfo : adminAppInfos) { + String app_name = adminAppInfo.getApp_name(); + String app_package = adminAppInfo.getApp_package(); + String app_url = adminAppInfo.getApp_url(); + String app_md5 = adminAppInfo.getApp_md5(); + int app_id = adminAppInfo.getId(); + long app_version_code = adminAppInfo.getApp_version_code(); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("app_name", app_name); + jsonObject.addProperty("app_package", app_package); + jsonObject.addProperty("app_id", app_id); + jsonObject.addProperty("MD5", app_md5); + PackageInfo packageInfo = null; + try { + packageInfo = mContext.getPackageManager().getPackageInfo(app_package, 0); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + if (packageInfo == null) { + Log.e(TAG, "installAdminApp: " + app_package + " 未安装"); + Utils.ariaDownload(mContext, app_url, jsonObject); + } else { + long appVersionCode; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + appVersionCode = packageInfo.getLongVersionCode(); + } else { + appVersionCode = packageInfo.versionCode; + } + if (app_version_code > appVersionCode) { + Log.e(TAG, "installAdminApp: " + app_package + " 更新"); + Utils.ariaDownload(mContext, app_url, jsonObject); + } else { + Log.e(TAG, "installAdminApp: " + app_package + "已安装最新版"); + } + } + } + } } diff --git a/app/src/main/java/com/aoleyun/sn/utils/TimeUtils.java b/app/src/main/java/com/aoleyun/sn/utils/TimeUtils.java index 8fff4db..55855ca 100644 --- a/app/src/main/java/com/aoleyun/sn/utils/TimeUtils.java +++ b/app/src/main/java/com/aoleyun/sn/utils/TimeUtils.java @@ -73,6 +73,13 @@ public class TimeUtils { return zeroTime; } + public static String getPhotoDate() { + long millisecond = System.currentTimeMillis(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); + Date date = new Date(millisecond); + return sdf.format(date); + } + @RequiresApi(api = Build.VERSION_CODES.O) public static boolean isTodayTime(long timeStamp) { String time = transferLongToDate(timeStamp); diff --git a/app/src/main/java/com/aoleyun/sn/utils/Utils.java b/app/src/main/java/com/aoleyun/sn/utils/Utils.java index 9e69311..e5be1ea 100644 --- a/app/src/main/java/com/aoleyun/sn/utils/Utils.java +++ b/app/src/main/java/com/aoleyun/sn/utils/Utils.java @@ -1322,15 +1322,22 @@ public class Utils { int screenWidth = (int) (width / density); // 屏幕宽度(dp) int screenHeight = (int) (height / density);// 屏幕高度(dp) -// Log.e("h_bl", "屏幕宽度(像素):" + width); -// Log.e("h_bl", "屏幕高度(像素):" + height); -// Log.e("h_bl", "屏幕密度(0.75 / 1.0 / 1.5):" + density); -// Log.e("h_bl", "屏幕密度dpi(120 / 160 / 240):" + densityDpi); -// Log.e("h_bl", "屏幕宽度(dp):" + screenWidth); -// Log.e("h_bl", "屏幕高度(dp):" + screenHeight); + Log.e("getAndroiodScreenProperty", "屏幕宽度(像素):" + width); + Log.e("getAndroiodScreenProperty", "屏幕高度(像素):" + height); + Log.e("getAndroiodScreenProperty", "屏幕密度(0.75 / 1.0 / 1.5):" + density); + Log.e("getAndroiodScreenProperty", "屏幕密度dpi(120 / 160 / 240):" + densityDpi); + Log.e("getAndroiodScreenProperty", "屏幕宽度(dp):" + screenWidth); + Log.e("getAndroiodScreenProperty", "屏幕高度(dp):" + screenHeight); return width + "×" + height; } + public static int getDensityDpi(Context context) { + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + DisplayMetrics dm = new DisplayMetrics(); + wm.getDefaultDisplay().getRealMetrics(dm); + return dm.densityDpi; + } + public static String getMacAddress() { List interfaces = null; try { diff --git a/app/src/main/java/com/aoleyun/sn/view/ToggleButton.java b/app/src/main/java/com/aoleyun/sn/view/ToggleButton.java new file mode 100644 index 0000000..76d9c5a --- /dev/null +++ b/app/src/main/java/com/aoleyun/sn/view/ToggleButton.java @@ -0,0 +1,347 @@ +package com.aoleyun.sn.view; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; + +import com.aoleyun.sn.R; +import com.facebook.rebound.SimpleSpringListener; +import com.facebook.rebound.Spring; +import com.facebook.rebound.SpringConfig; +import com.facebook.rebound.SpringSystem; +import com.facebook.rebound.SpringUtil; + +public class ToggleButton extends View { + private SpringSystem springSystem; + private Spring spring; + /** + * + */ + private float radius; + /** + * 开启颜色 + */ + private int onColor = Color.parseColor("#00d56b"); + /** + * 关闭颜色 + */ + private int offBorderColor = Color.parseColor("#e7e4e4"); + /** + * 灰色带颜色 + */ + private int offColor = Color.parseColor("#ffffff"); + /** + * 手柄颜色 + */ + private int spotColor = Color.parseColor("#ffffff"); + /** + * 边框颜色 + */ + private int borderColor = offBorderColor; + /** + * 画笔 + */ + private Paint paint; + /** + * 开关状态 + */ + private boolean toggleOn = false; + /** + * 边框大小 + */ + private int borderWidth = 2; + /** + * 垂直中心 + */ + private float centerY; + /** + * 按钮的开始和结束位置 + */ + private float startX, endX; + /** + * 手柄X位置的最小和最大值 + */ + private float spotMinX, spotMaxX; + /** + * 手柄大小 + */ + private int spotSize; + /** + * 手柄X位置 + */ + private float spotX; + /** + * 关闭时内部灰色带高度 + */ + private float offLineWidth; + /** + * + */ + private RectF rect = new RectF(); + /** + * 默认使用动画 + */ + private boolean defaultAnimate = true; + /** + * 是否默认处于打开状态 + */ + private boolean isDefaultOn = false; + /** + * 禁止点击 + */ + private boolean disable = false; + + private OnToggleChanged listener; + + public void setDisable(boolean dis) { + this.disable = dis; + } + + private ToggleButton(Context context) { + super(context); + } + + public ToggleButton(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setup(attrs); + } + + public ToggleButton(Context context, AttributeSet attrs) { + super(context, attrs); + setup(attrs); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + spring.removeListener(springListener); + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + spring.addListener(springListener); + } + + public void setup(AttributeSet attrs) { + paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setStyle(Paint.Style.FILL); + paint.setStrokeCap(Paint.Cap.ROUND); + springSystem = SpringSystem.create(); + spring = springSystem.createSpring(); + spring.setSpringConfig(SpringConfig.fromOrigamiTensionAndFriction(50, 7)); + this.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + if (disable) { + + } else { + toggle(defaultAnimate); + } + } + }); + TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ToggleButton); + offBorderColor = typedArray.getColor(R.styleable.ToggleButton_tbOffBorderColor, offBorderColor); + onColor = typedArray.getColor(R.styleable.ToggleButton_tbOnColor, onColor); + spotColor = typedArray.getColor(R.styleable.ToggleButton_tbSpotColor, spotColor); + offColor = typedArray.getColor(R.styleable.ToggleButton_tbOffColor, offColor); + borderWidth = typedArray.getDimensionPixelSize(R.styleable.ToggleButton_tbBorderWidth, borderWidth); + defaultAnimate = typedArray.getBoolean(R.styleable.ToggleButton_tbAnimate, defaultAnimate); + isDefaultOn = typedArray.getBoolean(R.styleable.ToggleButton_tbAsDefaultOn, isDefaultOn); + typedArray.recycle(); + borderColor = offBorderColor; + if (isDefaultOn) { + toggleOn(); + } + } + + public void toggle() { + toggle(true); + } + + public void toggle(boolean animate) { + toggleOn = !toggleOn; + takeEffect(animate); + if (listener != null) { + listener.onToggle(toggleOn); + } + } + + public void toggleOn() { + setToggleOn(); + if (listener != null) { + listener.onToggle(toggleOn); + } + } + + public void toggleOff() { + setToggleOff(); + if (listener != null) { + listener.onToggle(toggleOn); + } + } + + /** + * 设置显示成打开样式,不会触发toggle事件 + */ + public void setToggleOn() { + setToggleOn(true); + } + + /** + * @param animate asd + */ + public void setToggleOn(boolean animate) { + toggleOn = true; + takeEffect(animate); + } + + /** + * 设置显示成关闭样式,不会触发toggle事件 + */ + public void setToggleOff() { + setToggleOff(true); + } + + public void setToggleOff(boolean animate) { + toggleOn = false; + takeEffect(animate); + } + + public int isToggleOn() { + return toggleOn ? 1 : 0; + } + + private void takeEffect(boolean animate) { + if (animate) { + spring.setEndValue(toggleOn ? 1 : 0); + } else { +//这里没有调用spring,所以spring里的当前值没有变更,这里要设置一下,同步两边的当前值 + spring.setCurrentValue(toggleOn ? 1 : 0); + calculateEffect(toggleOn ? 1 : 0); + } + } + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int widthMode = MeasureSpec.getMode(widthMeasureSpec); + final int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + Resources r = Resources.getSystem(); + if (widthMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.AT_MOST) { + widthSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, r.getDisplayMetrics()); + widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY); + } + if (heightMode == MeasureSpec.UNSPECIFIED || heightSize == MeasureSpec.AT_MOST) { + heightSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, r.getDisplayMetrics()); + heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, + int bottom) { + super.onLayout(changed, left, top, right, bottom); + final int width = getWidth(); + final int height = getHeight(); + radius = Math.min(width, height) * 0.5f; + centerY = radius; + startX = radius; + endX = width - radius; + spotMinX = startX + borderWidth; + spotMaxX = endX - borderWidth; + spotSize = height - 4 * borderWidth; + spotX = toggleOn ? spotMaxX : spotMinX; + offLineWidth = 0; + } + + SimpleSpringListener springListener = new SimpleSpringListener() { + @Override + public void onSpringUpdate(Spring spring) { + final double value = spring.getCurrentValue(); + calculateEffect(value); + } + }; + + private int clamp(int value, int low, int high) { + return Math.min(Math.max(value, low), high); + } + + @Override + public void draw(Canvas canvas) { +// + super.draw(canvas); + rect.set(0, 0, getWidth(), getHeight()); + paint.setColor(borderColor); + canvas.drawRoundRect(rect, radius, radius, paint); + if (offLineWidth > 0) { + final float cy = offLineWidth * 0.5f; + rect.set(spotX - cy, centerY - cy, endX + cy, centerY + cy); +// paint.setColor(offColor); + canvas.drawRoundRect(rect, cy, cy, paint); + } + rect.set(spotX - 1 - radius, centerY - radius, spotX + 1.1f + radius, centerY + radius); + paint.setColor(borderColor); + canvas.drawRoundRect(rect, radius, radius, paint); + final float spotR = spotSize * 0.5f; + rect.set(spotX - spotR, centerY - spotR, spotX + spotR, centerY + spotR); + paint.setColor(spotColor); + canvas.drawRoundRect(rect, spotR, spotR, paint); + } + + /** + * @param value + */ + private void calculateEffect(final double value) { + final float mapToggleX = (float) SpringUtil.mapValueFromRangeToRange(value, 0, 1, spotMinX, spotMaxX); + spotX = mapToggleX; + float mapOffLineWidth = (float) SpringUtil.mapValueFromRangeToRange(1 - value, 0, 1, 10, spotSize); + offLineWidth = mapOffLineWidth; + final int fb = Color.blue(onColor); + final int fr = Color.red(onColor); + final int fg = Color.green(onColor); + final int tb = Color.blue(offBorderColor); + final int tr = Color.red(offBorderColor); + final int tg = Color.green(offBorderColor); + int sb = (int) SpringUtil.mapValueFromRangeToRange(1 - value, 0, 1, fb, tb); + int sr = (int) SpringUtil.mapValueFromRangeToRange(1 - value, 0, 1, fr, tr); + int sg = (int) SpringUtil.mapValueFromRangeToRange(1 - value, 0, 1, fg, tg); + sb = clamp(sb, 0, 255); + sr = clamp(sr, 0, 255); + sg = clamp(sg, 0, 255); + borderColor = Color.rgb(sr, sg, sb); + postInvalidate(); + } + + /** + * @author ThinkPad + */ + public interface OnToggleChanged { + /** + * @param on = = + */ + public void onToggle(boolean on); + } + + public void setOnToggleChanged(OnToggleChanged onToggleChanged) { + listener = onToggleChanged; + } + + public boolean isAnimate() { + return defaultAnimate; + } + + public void setAnimate(boolean animate) { + this.defaultAnimate = animate; + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/back.png b/app/src/main/res/drawable-hdpi/back.png new file mode 100644 index 0000000..b9e3ed0 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/back.png differ diff --git a/app/src/main/res/drawable-hdpi/com_system_huyan.png b/app/src/main/res/drawable-hdpi/com_system_huyan.png new file mode 100644 index 0000000..d81a835 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/com_system_huyan.png differ diff --git a/app/src/main/res/drawable/item_eye_background.xml b/app/src/main/res/drawable/item_eye_background.xml new file mode 100644 index 0000000..83c3ff9 --- /dev/null +++ b/app/src/main/res/drawable/item_eye_background.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_main.xml b/app/src/main/res/layout-land/activity_main.xml index d7aae11..a4e5677 100644 --- a/app/src/main/res/layout-land/activity_main.xml +++ b/app/src/main/res/layout-land/activity_main.xml @@ -189,7 +189,7 @@ app:layout_constraintStart_toEndOf="@+id/constraintLayout5" /> + app:layout_constraintStart_toEndOf="@+id/cl_student" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000..2a19fbc --- /dev/null +++ b/app/src/main/res/values/attrs.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file