From b481df8671dfe230d9063a4f9de43715885bdf77 Mon Sep 17 00:00:00 2001 From: tongtongstudio Date: Sat, 25 Dec 2021 17:04:52 +0800 Subject: [PATCH] =?UTF-8?q?version:1.1=20fix:=20add:=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=95=B0=E6=8D=AE=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 49 ++- app/src/main/AndroidManifest.xml | 79 +++-- app/src/main/assets/aria_config.xml | 161 +++++++++ .../com/uiui/os/activity/MainActivity.java | 85 ++++- .../com/uiui/os/activity/MainContact.java | 21 ++ .../com/uiui/os/activity/MainPresenter.java | 90 +++++ .../com/uiui/os/activity/NoticeActivity.java | 125 +++++++ .../uiui/os/adapter/AlarmClockAdapter.java | 61 ++++ .../uiui/os/adapter/NotificationAdapter.java | 53 +++ .../java/com/uiui/os/base/BaseActivity.java | 108 +++++- .../com/uiui/os/base/BaseApplication.java | 20 ++ .../java/com/uiui/os/base/BasePresenter.java | 10 + .../java/com/uiui/os/base/BaseService.java | 56 +++ .../main/java/com/uiui/os/base/BaseView.java | 4 + .../java/com/uiui/os/bean/AlarmClockData.java | 97 ++++++ .../java/com/uiui/os/bean/BaseResponse.java | 23 ++ .../com/uiui/os/fragment/AppListFragment.java | 55 ++- .../com/uiui/os/fragment/CustomFragment.java | 177 +++++++--- .../uiui/os/network/NetInterfaceManager.java | 178 ++++++++++ .../java/com/uiui/os/network/URLAddress.java | 13 + .../uiui/os/network/api/AlarmClockApi.java | 18 + .../os/network/api/AppUsageRecordApi.java | 21 ++ .../com/uiui/os/network/api/RunNewApp.java | 18 + .../os/network/api/SendScreenshotApi.java | 23 ++ .../com/uiui/os/receiver/BootReceiver.java | 88 +++++ .../com/uiui/os/service/AlarmService.java | 17 + .../java/com/uiui/os/service/MainService.java | 318 ++++++++++++++++++ .../os/utils/{ApkUtils.java => APKUtils.java} | 48 ++- .../java/com/uiui/os/utils/AlarmUtils.java | 287 ++++++++++++++++ .../main/java/com/uiui/os/utils/CmdUtil.java | 103 ++++++ .../com/uiui/os/utils/ForegroundAppUtil.java | 116 +++++++ .../main/java/com/uiui/os/utils/MD5Util.java | 112 ++++++ .../java/com/uiui/os/utils/TimeUtils.java | 93 +++++ .../java/com/uiui/os/utils/ToastUtil.java | 93 +++++ .../main/java/com/uiui/os/utils/Utils.java | 63 ++++ .../java/com/uiui/os/view/CustomContent.java | 1 - .../main/res/drawable-hdpi/actions_logo.png | Bin 26101 -> 0 bytes app/src/main/res/drawable-hdpi/charging.png | Bin 1977 -> 2219 bytes app/src/main/res/drawable-hdpi/voice.png | Bin 0 -> 3952 bytes .../main/res/drawable-hdpi/voice_white.png | Bin 0 -> 3202 bytes app/src/main/res/drawable-hdpi/wifi_icon.png | Bin 0 -> 5845 bytes .../main/res/drawable-xxxhdpi/sos_icon.png | Bin 0 -> 5337 bytes .../res/drawable/alarmclcok_background.xml | 20 ++ .../res/drawable/notice_voice_background.xml | 17 + app/src/main/res/drawable/ok_background.xml | 13 + .../main/res/drawable/voice_background.xml | 17 + .../main/res/layout-land/fragment_custom.xml | 161 ++++----- .../main/res/layout-port/fragment_custom.xml | 140 ++++---- app/src/main/res/layout/activity_notice.xml | 68 ++++ .../{actions_item.xml => item_actions.xml} | 2 +- app/src/main/res/layout/item_alarmclock.xml | 73 ++++ app/src/main/res/layout/item_notification.xml | 61 ++++ app/src/main/res/values/colors.xml | 6 +- app/src/main/res/values/styles.xml | 20 ++ settings.gradle | 2 +- 55 files changed, 3184 insertions(+), 300 deletions(-) create mode 100644 app/src/main/assets/aria_config.xml create mode 100644 app/src/main/java/com/uiui/os/activity/MainContact.java create mode 100644 app/src/main/java/com/uiui/os/activity/MainPresenter.java create mode 100644 app/src/main/java/com/uiui/os/activity/NoticeActivity.java create mode 100644 app/src/main/java/com/uiui/os/adapter/AlarmClockAdapter.java create mode 100644 app/src/main/java/com/uiui/os/adapter/NotificationAdapter.java create mode 100644 app/src/main/java/com/uiui/os/base/BasePresenter.java create mode 100644 app/src/main/java/com/uiui/os/base/BaseService.java create mode 100644 app/src/main/java/com/uiui/os/base/BaseView.java create mode 100644 app/src/main/java/com/uiui/os/bean/AlarmClockData.java create mode 100644 app/src/main/java/com/uiui/os/bean/BaseResponse.java create mode 100644 app/src/main/java/com/uiui/os/network/NetInterfaceManager.java create mode 100644 app/src/main/java/com/uiui/os/network/URLAddress.java create mode 100644 app/src/main/java/com/uiui/os/network/api/AlarmClockApi.java create mode 100644 app/src/main/java/com/uiui/os/network/api/AppUsageRecordApi.java create mode 100644 app/src/main/java/com/uiui/os/network/api/RunNewApp.java create mode 100644 app/src/main/java/com/uiui/os/network/api/SendScreenshotApi.java create mode 100644 app/src/main/java/com/uiui/os/receiver/BootReceiver.java create mode 100644 app/src/main/java/com/uiui/os/service/AlarmService.java create mode 100644 app/src/main/java/com/uiui/os/service/MainService.java rename app/src/main/java/com/uiui/os/utils/{ApkUtils.java => APKUtils.java} (78%) create mode 100644 app/src/main/java/com/uiui/os/utils/AlarmUtils.java create mode 100644 app/src/main/java/com/uiui/os/utils/CmdUtil.java create mode 100644 app/src/main/java/com/uiui/os/utils/ForegroundAppUtil.java create mode 100644 app/src/main/java/com/uiui/os/utils/MD5Util.java create mode 100644 app/src/main/java/com/uiui/os/utils/TimeUtils.java create mode 100644 app/src/main/java/com/uiui/os/utils/ToastUtil.java delete mode 100644 app/src/main/res/drawable-hdpi/actions_logo.png create mode 100644 app/src/main/res/drawable-hdpi/voice.png create mode 100644 app/src/main/res/drawable-hdpi/voice_white.png create mode 100644 app/src/main/res/drawable-hdpi/wifi_icon.png create mode 100644 app/src/main/res/drawable-xxxhdpi/sos_icon.png create mode 100644 app/src/main/res/drawable/alarmclcok_background.xml create mode 100644 app/src/main/res/drawable/notice_voice_background.xml create mode 100644 app/src/main/res/drawable/ok_background.xml create mode 100644 app/src/main/res/drawable/voice_background.xml create mode 100644 app/src/main/res/layout/activity_notice.xml rename app/src/main/res/layout/{actions_item.xml => item_actions.xml} (95%) create mode 100644 app/src/main/res/layout/item_alarmclock.xml create mode 100644 app/src/main/res/layout/item_notification.xml diff --git a/app/build.gradle b/app/build.gradle index 6051bec..f2862bb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,11 +1,11 @@ apply plugin: 'com.android.application' def appName() { - return "学习系统" + return "桌面" } def releaseTime() { - return new Date().format("yyyyMMddHHmmss", TimeZone.getDefault()) + return new Date().format("yyyyMMdd-HHmmss", TimeZone.getDefault()) } android { @@ -15,12 +15,27 @@ android { applicationId "com.uiui.os" minSdkVersion 24 targetSdkVersion 29 - versionCode 1 - versionName "1.0" + versionCode 2 + versionName "1.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } + lintOptions { + checkReleaseBuilds false + // Or, if you prefer, you can continue to check for errors in release builds, + // but continue the build even when errors are found: + abortOnError false + } + + aaptOptions.cruncherEnabled = false + aaptOptions.useNewCruncher = false + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + signingConfigs { zhanRui { storeFile file("src/doc/zhanxun.keystore") @@ -133,9 +148,24 @@ dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - - implementation 'com.squareup.okhttp3:okhttp:3.12.12' - implementation 'com.google.code.gson:gson:2.6.2' + //RxJava +// implementation 'io.reactivex.rxjava2:rxjava:2.2.12' +// implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' + // + implementation 'com.squareup.okhttp3:okhttp:4.9.1' + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'com.squareup.retrofit2:converter-gson:2.9.0' +// implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0' + implementation "com.squareup.retrofit2:adapter-rxjava3:2.9.0" + //Gson + implementation 'com.google.code.gson:gson:2.8.7' + implementation 'com.google.zxing:core:3.3.0' + //生命周期管理 + implementation 'com.trello.rxlifecycle4:rxlifecycle:4.0.2' + implementation 'com.trello.rxlifecycle4:rxlifecycle-android:4.0.2' + implementation 'com.trello.rxlifecycle4:rxlifecycle-components:4.0.2' + implementation 'com.trello.rxlifecycle4:rxlifecycle-components-preference:4.0.2' + implementation 'com.trello.rxlifecycle4:rxlifecycle-android-lifecycle:4.0.2' //bindView implementation 'com.jakewharton:butterknife:10.1.0' annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0' @@ -148,4 +178,9 @@ dependencies { //指示器 implementation 'com.github.hackware1993:MagicIndicator:1.7.0' // for androidx implementation 'com.king.view:circleprogressview:1.1.2' + //工具类 + implementation 'com.blankj:utilcodex:1.30.6' + //aria + implementation 'com.arialyy.aria:core:3.8.15' + annotationProcessor 'com.arialyy.aria:compiler:3.8.15' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 95b824c..f46e691 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,32 +7,20 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - + - + android:value="${AMAP_KEY}" /> + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/aria_config.xml b/app/src/main/assets/aria_config.xml new file mode 100644 index 0000000..a85bbf6 --- /dev/null +++ b/app/src/main/assets/aria_config.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/uiui/os/activity/MainActivity.java b/app/src/main/java/com/uiui/os/activity/MainActivity.java index e694912..7efbb59 100644 --- a/app/src/main/java/com/uiui/os/activity/MainActivity.java +++ b/app/src/main/java/com/uiui/os/activity/MainActivity.java @@ -7,14 +7,16 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Color; import android.os.IBinder; import android.os.RemoteException; import android.text.TextUtils; +import android.text.format.DateUtils; import android.util.Log; import android.view.KeyEvent; -import android.view.View; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; @@ -23,24 +25,19 @@ import androidx.fragment.app.FragmentTransaction; import androidx.viewpager.widget.ViewPager; import com.alarmclock.uiui.IAlarmAidlInterface; -import com.amap.api.location.AMapLocation; -import com.amap.api.location.AMapLocationClient; -import com.amap.api.location.AMapLocationListener; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; -import com.qweather.sdk.bean.base.Code; -import com.qweather.sdk.bean.base.Lang; -import com.qweather.sdk.bean.base.Unit; -import com.qweather.sdk.bean.weather.WeatherNowBean; -import com.qweather.sdk.view.QWeather; import com.uiui.os.R; import com.uiui.os.base.BaseActivity; import com.uiui.os.bean.AlarmItem; +import com.uiui.os.bean.BaseResponse; import com.uiui.os.fragment.AppListFragment; import com.uiui.os.fragment.BaseFragmentPagerAdapter; import com.uiui.os.fragment.CustomFragment; -import com.uiui.os.utils.AmapManager; -import com.uiui.os.utils.ApkUtils; +import com.uiui.os.network.NetInterfaceManager; +import com.uiui.os.utils.APKUtils; +import com.uiui.os.utils.TimeUtils; +import com.uiui.os.utils.Utils; import com.uiui.os.view.ScaleCircleNavigator; import net.lucode.hackware.magicindicator.MagicIndicator; @@ -52,6 +49,8 @@ import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; public class MainActivity extends BaseActivity { private String TAG = MainActivity.class.getSimpleName(); @@ -111,9 +110,9 @@ public class MainActivity extends BaseActivity { customFragment.setAlarmItem(null); return; } - Type type = new TypeToken() { + Type type = new TypeToken>() { }.getType(); - AlarmItem alarmItem = new Gson().fromJson(json, type); + List alarmItem = new Gson().fromJson(json, type); customFragment.setAlarmItem(alarmItem); } catch (RemoteException e) { e.printStackTrace(); @@ -142,7 +141,7 @@ public class MainActivity extends BaseActivity { public void initData() { registmNewAppReceiver(); fragments.add(customFragment); - ArrayList applicationInfoList = ApkUtils.queryFilterAppInfo(this); + ArrayList applicationInfoList = APKUtils.queryFilterAppInfo(this); int x = 0; for (int i = 0; i <= applicationInfoList.size(); i++) { if (i != 0 && i % APP_LIST_SIZE == 0) { @@ -176,7 +175,7 @@ public class MainActivity extends BaseActivity { private void addData() { List fragmentList = new ArrayList<>(); - ArrayList applicationInfoList = ApkUtils.queryFilterAppInfo(this); + ArrayList applicationInfoList = APKUtils.queryFilterAppInfo(this); int x = 0; for (int i = 0; i <= applicationInfoList.size(); i++) { if (i != 0 && i % APP_LIST_SIZE == 0) { @@ -245,7 +244,58 @@ public class MainActivity extends BaseActivity { @Override protected void onResume() { super.onResume(); + String packagename = TimeUtils.getInstance().getAppPackageName(); + Log.e("SendcloseApp", "packagename=" + packagename); + TimeUtils.getInstance().setEndTime(System.currentTimeMillis()); + if (packagename != null && packagename.length() > 0) { + ApplicationInfo app = APKUtils.getApplicationInfo(this, packagename); + PackageManager pm = getPackageManager(); + if (app != null) { + Log.e(TAG, "onResume: " + app.loadLabel(pm).toString()); + Log.e(TAG, "onResume: " + app.packageName); + NetInterfaceManager.getInstance().getAppUsageRecordControl() + .sendappUsageRecord(Utils.getSerial(), + app.loadLabel(pm).toString(), app.packageName, + TimeUtils.getInstance().getStartTime() / 1000, + TimeUtils.getInstance().getEndTime() / 1000) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + Log.e("onResume", "onSubscribe: "); + } + + @Override + public void onNext(BaseResponse baseResponse) { + Log.e("onResume", "onNext: " + baseResponse); + } + + @Override + public void onError(Throwable e) { + Log.e("onResume", "onError: " + e.getMessage()); + } + + @Override + public void onComplete() { + Log.e("onResume", "onComplete: "); + } + }); + TimeUtils.getInstance().setAppPackageName(""); + } else { + Log.e("fht", "app = null" + packagename); + } + } getAlarmData(); + + } + + @Override + protected void onRestart() { + super.onRestart(); + } + + @Override + protected void onStop() { + super.onStop(); } private void registmNewAppReceiver() { @@ -264,7 +314,10 @@ public class MainActivity extends BaseActivity { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Log.e(TAG, "onReceive: " + action); - addData(); + if (Intent.ACTION_PACKAGE_ADDED.equals(action) + || Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) { + addData(); + } } } } diff --git a/app/src/main/java/com/uiui/os/activity/MainContact.java b/app/src/main/java/com/uiui/os/activity/MainContact.java new file mode 100644 index 0000000..276657f --- /dev/null +++ b/app/src/main/java/com/uiui/os/activity/MainContact.java @@ -0,0 +1,21 @@ +package com.uiui.os.activity; + +import com.uiui.os.base.BasePresenter; +import com.uiui.os.base.BaseView; +import com.uiui.os.bean.AlarmClockData; + +import java.util.List; + +public class MainContact { + public interface Presenter extends BasePresenter { + //设置所有信息 + void getAlarmClock(); + + } + + public interface MainView extends BaseView { + //获取所有信息 + void setAlarmClock(List dataList); + + } +} diff --git a/app/src/main/java/com/uiui/os/activity/MainPresenter.java b/app/src/main/java/com/uiui/os/activity/MainPresenter.java new file mode 100644 index 0000000..193a2dd --- /dev/null +++ b/app/src/main/java/com/uiui/os/activity/MainPresenter.java @@ -0,0 +1,90 @@ +package com.uiui.os.activity; + +import android.content.Context; +import android.util.Log; + +import com.tencent.mmkv.MMKV; +import com.trello.rxlifecycle4.android.ActivityEvent; +import com.uiui.os.bean.AlarmClockData; +import com.uiui.os.bean.BaseResponse; +import com.uiui.os.network.NetInterfaceManager; +import com.uiui.os.utils.AlarmUtils; + +import java.util.List; + +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.subjects.BehaviorSubject; + + +/** + * MainActivity和MainService 的 Presenter + * + * @author jgy02 + */ +public class MainPresenter implements MainContact.Presenter { + private static final String TAG = MainPresenter.class.getSimpleName(); + private static final int OK = 200; + private MainContact.MainView mView; + private Context mContext; + + private BehaviorSubject lifecycle; + + public void setLifecycle(BehaviorSubject lifecycle) { + this.lifecycle = lifecycle; + } + + public BehaviorSubject getLifecycle() { + return lifecycle; + } + + public MainPresenter(Context context) { + this.mContext = context; + Log.e(TAG, "MainPresenter: " + context.getClass()); + } + + @Override + public void attachView(@androidx.annotation.NonNull MainContact.MainView view) { + this.mView = view; + } + + @Override + public void detachView() { + this.mView = null; + } + + + @Override + public void getAlarmClock() { + MMKV mmkv = MMKV.defaultMMKV(); + NetInterfaceManager.getInstance().getAlarmClockApiObservable() + .subscribe(new Observer>>() { + @Override + public void onSubscribe(Disposable d) { + Log.e("getAlarmClock", "onSubscribe: "); + } + + @Override + public void onNext(BaseResponse> listBaseResponse) { + Log.e("getAlarmClock", "onNext: "+listBaseResponse); + if (listBaseResponse.code == 200) { + List data = listBaseResponse.data; + AlarmUtils.getInstance().setAlarmClockData(data); + } else { + + } + } + + @Override + public void onError(Throwable e) { + Log.e("getAlarmClock", "onError: " + e.getMessage()); + onComplete(); + } + + @Override + public void onComplete() { + Log.e("getAlarmClock", "onComplete: "); + } + }); + } +} diff --git a/app/src/main/java/com/uiui/os/activity/NoticeActivity.java b/app/src/main/java/com/uiui/os/activity/NoticeActivity.java new file mode 100644 index 0000000..5dec510 --- /dev/null +++ b/app/src/main/java/com/uiui/os/activity/NoticeActivity.java @@ -0,0 +1,125 @@ +package com.uiui.os.activity; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.constraintlayout.widget.ConstraintLayout; + +import android.content.Intent; +import android.media.MediaPlayer; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +import com.blankj.utilcode.util.FileUtils; +import com.uiui.os.R; +import com.uiui.os.bean.AlarmClockData; +import com.uiui.os.utils.AlarmUtils; +import com.uiui.os.utils.Utils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import butterknife.BindView; +import butterknife.ButterKnife; + +public class NoticeActivity extends AppCompatActivity { + @BindView(R.id.tv_title) + TextView tv_title; + @BindView(R.id.bt_ok) + Button bt_ok; + @BindView(R.id.constraintLayout) + ConstraintLayout constraintLayout; + private AlarmClockData alarmClockData; + int code; + private MediaPlayer mediaPlayer; + private String TAG = NoticeActivity.class.getSimpleName(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_notice); + ButterKnife.bind(this); + Intent intent = getIntent(); + code = intent.getIntExtra("id", -1); + if (code == -1) { + finish(); + } else { + HashMap oldData = AlarmUtils.getInstance().getOldData(); + alarmClockData = oldData.get(code); + if (alarmClockData == null) { + finish(); + } + showData(alarmClockData); + } + + } + + private void showData(AlarmClockData alarmClockData) { + tv_title.setText(alarmClockData.getRemarks()); + bt_ok.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + HashMap oldData = AlarmUtils.getInstance().getOldData(); + List data = new ArrayList<>(); + for (AlarmClockData alarm : oldData.values()) { + if (alarm.getId() == code) { + alarm.setFinished(true); + } + data.add(alarm); + } + AlarmUtils.getInstance().setAlarmString(data); + finish(); + } + }); + String url = alarmClockData.getVoice(); + String md5 = alarmClockData.getVoice_md5(); + if (!TextUtils.isEmpty(url)) { + constraintLayout.setVisibility(View.VISIBLE); + String fileName = Utils.getFileNamefromURL(url); + File file = new File(Utils.getDownLoadPath(NoticeActivity.this) + fileName); + String fileMD5 = FileUtils.getFileMD5ToString(file); +// if (!md5.equals(fileMD5)) { +// // TODO: 2021/12/16 +// } else { + mediaPlayer = new MediaPlayer(); + try { + // 切歌之前先重置,释放掉之前的资源 + mediaPlayer.reset(); + FileInputStream fis = new FileInputStream(file); + mediaPlayer.setDataSource(fis.getFD()); + // 设置播放源 +// mediaPlayer.setDataSource(file.getAbsolutePath()); + // 开始播放前的准备工作,加载多媒体资源,获取相关信息 + mediaPlayer.prepare(); + // 开始播放 + mediaPlayer.start(); + } catch (IOException e) { + e.printStackTrace(); + Log.e(TAG, "showData: " + e.getMessage()); + } +// } + } else { + constraintLayout.setVisibility(View.GONE); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (mediaPlayer != null) { + if (mediaPlayer.isPlaying()) { + mediaPlayer.stop(); + } + mediaPlayer.release(); + mediaPlayer = null; + } + } +} diff --git a/app/src/main/java/com/uiui/os/adapter/AlarmClockAdapter.java b/app/src/main/java/com/uiui/os/adapter/AlarmClockAdapter.java new file mode 100644 index 0000000..4c1c3d0 --- /dev/null +++ b/app/src/main/java/com/uiui/os/adapter/AlarmClockAdapter.java @@ -0,0 +1,61 @@ +package com.uiui.os.adapter; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.uiui.os.R; +import com.uiui.os.bean.AlarmItem; + +import java.util.List; + +public class AlarmClockAdapter extends RecyclerView.Adapter { + private List alarmItemList; + + public void setAlarmItemList(List alarmItems) { + this.alarmItemList = alarmItems; + notifyDataSetChanged(); + } + + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new AlarmClockAdapter.ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_alarmclock, parent, false)); + + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + AlarmItem alarmItem = alarmItemList.get(position); + holder.tv_time.setText(alarmItem.mTime); + holder.tv_type.setText(alarmItem.mRepeatType); + if (alarmItem.mActive) { + holder.tv_status.setText("打开"); + } else { + holder.tv_status.setText("关闭"); + } + } + + @Override + public int getItemCount() { + return alarmItemList == null ? 0 : alarmItemList.size(); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + TextView tv_time; + TextView tv_status; + TextView tv_type; + + ViewHolder(@NonNull View itemView) { + super(itemView); + tv_time = itemView.findViewById(R.id.tv_time); + tv_status = itemView.findViewById(R.id.tv_status); + tv_type = itemView.findViewById(R.id.tv_type); + } + } +} diff --git a/app/src/main/java/com/uiui/os/adapter/NotificationAdapter.java b/app/src/main/java/com/uiui/os/adapter/NotificationAdapter.java new file mode 100644 index 0000000..705b97b --- /dev/null +++ b/app/src/main/java/com/uiui/os/adapter/NotificationAdapter.java @@ -0,0 +1,53 @@ +package com.uiui.os.adapter; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.uiui.os.R; +import com.uiui.os.bean.AlarmClockData; + +import java.util.List; + +public class NotificationAdapter extends RecyclerView.Adapter { + private List dataList; + + @NonNull + @Override + public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new Holder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_notification, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull Holder holder, int position) { + AlarmClockData alarmClockData = dataList.get(position); + holder.tv_title.setText("提醒事件:" + alarmClockData.getRemarks()); + holder.tv_time.setText("提醒时间:" + alarmClockData.getTime()); + + } + + @Override + public int getItemCount() { + return dataList == null ? 0 : dataList.size(); + } + + public void setDataList(List data) { + this.dataList = data; + notifyDataSetChanged(); + } + + class Holder extends RecyclerView.ViewHolder { + TextView tv_title; + TextView tv_time; + + public Holder(@NonNull View itemView) { + super(itemView); + tv_title = itemView.findViewById(R.id.tv_title); + tv_time = itemView.findViewById(R.id.tv_time); + } + } +} diff --git a/app/src/main/java/com/uiui/os/base/BaseActivity.java b/app/src/main/java/com/uiui/os/base/BaseActivity.java index 84cc88d..a17ab6a 100644 --- a/app/src/main/java/com/uiui/os/base/BaseActivity.java +++ b/app/src/main/java/com/uiui/os/base/BaseActivity.java @@ -2,39 +2,78 @@ package com.uiui.os.base; import android.os.Bundle; +import androidx.annotation.CallSuper; +import androidx.annotation.CheckResult; +import androidx.annotation.ContentView; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; +import com.trello.rxlifecycle4.LifecycleProvider; +import com.trello.rxlifecycle4.LifecycleTransformer; +import com.trello.rxlifecycle4.RxLifecycle; +import com.trello.rxlifecycle4.android.ActivityEvent; +import com.trello.rxlifecycle4.android.RxLifecycleAndroid; import com.uiui.os.R; import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX; -public abstract class BaseActivity extends AppCompatActivity { - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setBar(); - //状态栏改变放在setContentView前后有所不同 - setContentView(this.getLayoutId()); - initView(); - initData(); +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.subjects.BehaviorSubject; + + +public abstract class BaseActivity extends AppCompatActivity implements LifecycleProvider { + public final BehaviorSubject lifecycleSubject = BehaviorSubject.create(); + + public BaseActivity() { + super(); } - private void setBar() { + @ContentView + public BaseActivity(@LayoutRes int contentLayoutId) { + super(contentLayoutId); + } + + @Override + @NonNull + @CheckResult + public final Observable lifecycle() { + return lifecycleSubject.hide(); + } + + @Override + @NonNull + @CheckResult + public final LifecycleTransformer bindUntilEvent(@NonNull ActivityEvent event) { + return RxLifecycle.bindUntilEvent(lifecycleSubject, event); + } + + @Override + @NonNull + @CheckResult + public final LifecycleTransformer bindToLifecycle() { + return RxLifecycleAndroid.bindActivity(lifecycleSubject); + } + + @Override + @CallSuper + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + lifecycleSubject.onNext(ActivityEvent.CREATE); +// StatusBarUtil.init(this); UltimateBarX.statusBar(this) .transparent() .colorRes(R.color.colorPrimaryDark) -// .light(true) + .light(true) .apply(); UltimateBarX.navigationBar(this) .transparent() .colorRes(R.color.colorPrimaryDark) -// .light(true) + .light(true) .apply(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); + setContentView(this.getLayoutId()); + initView(); + initData(); } /** @@ -52,4 +91,39 @@ public abstract class BaseActivity extends AppCompatActivity { * 初始化数据 */ public abstract void initData(); + + @Override + @CallSuper + protected void onStart() { + super.onStart(); + lifecycleSubject.onNext(ActivityEvent.START); + } + + @Override + @CallSuper + protected void onResume() { + super.onResume(); + lifecycleSubject.onNext(ActivityEvent.RESUME); + } + + @Override + @CallSuper + protected void onPause() { + lifecycleSubject.onNext(ActivityEvent.PAUSE); + super.onPause(); + } + + @Override + @CallSuper + protected void onStop() { + lifecycleSubject.onNext(ActivityEvent.STOP); + super.onStop(); + } + + @Override + @CallSuper + protected void onDestroy() { + lifecycleSubject.onNext(ActivityEvent.DESTROY); + super.onDestroy(); + } } diff --git a/app/src/main/java/com/uiui/os/base/BaseApplication.java b/app/src/main/java/com/uiui/os/base/BaseApplication.java index 32e21c6..8cee691 100644 --- a/app/src/main/java/com/uiui/os/base/BaseApplication.java +++ b/app/src/main/java/com/uiui/os/base/BaseApplication.java @@ -3,12 +3,20 @@ package com.uiui.os.base; import android.annotation.SuppressLint; import android.app.Application; import android.content.Context; +import android.content.Intent; import android.os.Handler; import android.os.Looper; import android.util.Log; +import com.arialyy.aria.core.Aria; import com.qweather.sdk.view.HeConfig; +import com.tencent.mmkv.MMKV; +import com.uiui.os.BuildConfig; +import com.uiui.os.network.NetInterfaceManager; +import com.uiui.os.service.MainService; +import com.uiui.os.utils.AlarmUtils; import com.uiui.os.utils.AmapManager; +import com.uiui.os.utils.TimeUtils; public class BaseApplication extends Application { @@ -35,8 +43,20 @@ public class BaseApplication extends Application { public void onCreate() { super.onCreate(); context = this; + if (!BuildConfig.DEBUG) { + catchException(); + } + String rootDir = MMKV.initialize(this); + Log.e(TAG, "mmkv root: " + rootDir); + Aria.init(this); + Aria.get(this).getDownloadConfig().setMaxTaskNum(1); + Aria.get(this).getDownloadConfig().setConvertSpeed(true); + TimeUtils.init(this); + AlarmUtils.init(this); HeConfig.init("HE2111041506381545", "32b5ec69545e44119583a5e0ed4e87df"); AmapManager.init(this); + NetInterfaceManager.init(this); + startService(new Intent(this, MainService.class)); } diff --git a/app/src/main/java/com/uiui/os/base/BasePresenter.java b/app/src/main/java/com/uiui/os/base/BasePresenter.java new file mode 100644 index 0000000..3abc217 --- /dev/null +++ b/app/src/main/java/com/uiui/os/base/BasePresenter.java @@ -0,0 +1,10 @@ +package com.uiui.os.base; + +import androidx.annotation.NonNull; + +public interface BasePresenter { + + void attachView(@NonNull V view); + + void detachView(); +} diff --git a/app/src/main/java/com/uiui/os/base/BaseService.java b/app/src/main/java/com/uiui/os/base/BaseService.java new file mode 100644 index 0000000..3b4b424 --- /dev/null +++ b/app/src/main/java/com/uiui/os/base/BaseService.java @@ -0,0 +1,56 @@ +package com.uiui.os.base; + +import android.app.Service; +import android.content.Intent; + +import androidx.annotation.NonNull; + +import com.trello.rxlifecycle4.LifecycleProvider; +import com.trello.rxlifecycle4.LifecycleTransformer; +import com.trello.rxlifecycle4.RxLifecycle; +import com.trello.rxlifecycle4.android.ActivityEvent; +import com.trello.rxlifecycle4.android.RxLifecycleAndroid; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.subjects.BehaviorSubject; + + +public abstract class BaseService extends Service implements LifecycleProvider { + public final BehaviorSubject lifecycleSubject = BehaviorSubject.create(); + + public BaseService() { + super(); + } + + @Override + public final Observable lifecycle() { + return lifecycleSubject.hide(); + } + + @Override + public final LifecycleTransformer bindUntilEvent(@NonNull ActivityEvent event) { + return RxLifecycle.bindUntilEvent(lifecycleSubject, event); + } + + @Override + public final LifecycleTransformer bindToLifecycle() { + return RxLifecycleAndroid.bindActivity(lifecycleSubject); + } + + @Override + public void onCreate() { + super.onCreate(); + lifecycleSubject.onNext(ActivityEvent.CREATE); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + return super.onStartCommand(intent, flags, startId); + } + + @Override + public void onDestroy() { + super.onDestroy(); + lifecycleSubject.onNext(ActivityEvent.STOP); + } +} diff --git a/app/src/main/java/com/uiui/os/base/BaseView.java b/app/src/main/java/com/uiui/os/base/BaseView.java new file mode 100644 index 0000000..f11b175 --- /dev/null +++ b/app/src/main/java/com/uiui/os/base/BaseView.java @@ -0,0 +1,4 @@ +package com.uiui.os.base; + +public interface BaseView { +} diff --git a/app/src/main/java/com/uiui/os/bean/AlarmClockData.java b/app/src/main/java/com/uiui/os/bean/AlarmClockData.java new file mode 100644 index 0000000..daad1af --- /dev/null +++ b/app/src/main/java/com/uiui/os/bean/AlarmClockData.java @@ -0,0 +1,97 @@ +package com.uiui.os.bean; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.gson.Gson; +import com.google.gson.JsonParser; + +import java.io.Serializable; + +public class AlarmClockData implements Serializable { + private static final long serialVersionUID = -5856502480745183157L; + + int id; + int type; + String time; + String remarks; + String voice; + String voice_md5; + boolean finished = false; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public String getRemarks() { + return remarks; + } + + public void setRemarks(String remarks) { + this.remarks = remarks; + } + + public String getVoice() { + return voice; + } + + public void setVoice(String voice) { + this.voice = voice; + } + + public String getVoice_md5() { + return voice_md5; + } + + public void setVoice_md5(String voice_md5) { + this.voice_md5 = voice_md5; + } + + public boolean isFinished() { + return finished; + } + + public void setFinished(boolean finished) { + this.finished = finished; + } + + @NonNull + @Override + public String toString() { + return JsonParser.parseString(new Gson().toJson(this)).getAsJsonObject().toString(); + } + + + @Override + public boolean equals(@Nullable Object obj) { + if (obj == null) return false; + if (!(obj instanceof AlarmClockData)) return false; + if (id != ((AlarmClockData) obj).id) return false; + if (type != ((AlarmClockData) obj).type) return false; + if (!time.equals(((AlarmClockData) obj).time)) return false; + if (!remarks.equals(((AlarmClockData) obj).remarks)) return false; + if (!voice.equals(((AlarmClockData) obj).voice)) return false; + if (!voice_md5.equals(((AlarmClockData) obj).voice_md5)) return false; + return true; + } +} diff --git a/app/src/main/java/com/uiui/os/bean/BaseResponse.java b/app/src/main/java/com/uiui/os/bean/BaseResponse.java new file mode 100644 index 0000000..b86ee59 --- /dev/null +++ b/app/src/main/java/com/uiui/os/bean/BaseResponse.java @@ -0,0 +1,23 @@ +package com.uiui.os.bean; + +import androidx.annotation.NonNull; + +import com.google.gson.Gson; +import com.google.gson.JsonParser; + +import java.io.Serializable; + + +public class BaseResponse implements Serializable { + private static final long serialVersionUID = 5468533687801294972L; + + public int code; + public String msg; + public T data; + + @NonNull + @Override + public String toString() { + return JsonParser.parseString(new Gson().toJson(this)).getAsJsonObject().toString(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/uiui/os/fragment/AppListFragment.java b/app/src/main/java/com/uiui/os/fragment/AppListFragment.java index 8338a45..4d7cc13 100644 --- a/app/src/main/java/com/uiui/os/fragment/AppListFragment.java +++ b/app/src/main/java/com/uiui/os/fragment/AppListFragment.java @@ -1,12 +1,12 @@ package com.uiui.os.fragment; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.os.Bundle; import androidx.annotation.NonNull; -import androidx.core.app.NavUtils; import androidx.fragment.app.Fragment; import android.util.Log; @@ -16,17 +16,22 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; -import android.widget.Toast; +import com.google.gson.JsonObject; import com.uiui.os.R; -import com.uiui.os.utils.ApkUtils; +import com.uiui.os.bean.BaseResponse; +import com.uiui.os.network.NetInterfaceManager; +import com.uiui.os.utils.APKUtils; import com.uiui.os.utils.BitmapUtils; import com.uiui.os.utils.IconUtils; +import com.uiui.os.utils.TimeUtils; import com.uiui.os.view.MyGridLayout; import java.util.ArrayList; import java.util.Arrays; -import java.util.List; + +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; /** * A simple {@link Fragment} subclass. @@ -106,7 +111,7 @@ public class AppListFragment extends Fragment { @Override public View getView(int index) { PackageManager pm = rootView.getContext().getPackageManager(); - View view = getLayoutInflater().inflate(R.layout.actions_item, + View view = getLayoutInflater().inflate(R.layout.item_actions, null); ImageView iv = view.findViewById(R.id.iv); TextView tv = view.findViewById(R.id.tv); @@ -148,12 +153,50 @@ public class AppListFragment extends Fragment { public void onItemClick(View v, int index) { ApplicationInfo applicationInfo = applicationInfos.get(index); if (applicationInfo != null) { - ApkUtils.openPackage(v.getContext(), applicationInfo.packageName); + APKUtils.openPackage(v.getContext(), applicationInfo.packageName); + TimeUtils.getInstance().setAppPackageName(applicationInfo.packageName); + TimeUtils.getInstance().setStartTime(System.currentTimeMillis()); + SendRunningApp(getActivity()); } } }); } + private void SendRunningApp(Context context) { + String packageName = TimeUtils.getInstance().getAppPackageName(); + long time = TimeUtils.getInstance().getStartTime(); + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("app_package", packageName); + jsonObject.addProperty("version_name", APKUtils.getAPPVersionName(context, packageName)); + jsonObject.addProperty("start_time", time / 1000); + String jsonString = jsonObject.toString(); + Log.e(TAG, "SendRunningApp: " + jsonString); + NetInterfaceManager.getInstance() + .getRunningAppObservable(jsonString) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + Log.e("SendRunningApp", "onSubscribe: "); + } + + @Override + public void onNext(BaseResponse baseResponse) { + Log.e("SendRunningApp", "onSubscribe: " + baseResponse); + } + + @Override + public void onError(Throwable e) { + Log.e("SendRunningApp", "onError: " + e.getMessage()); + onComplete(); + } + + @Override + public void onComplete() { + Log.e("SendRunningApp", "onComplete: "); + } + }); + } + public void setAppList(ArrayList appList) { this.applicationInfos = appList; } diff --git a/app/src/main/java/com/uiui/os/fragment/CustomFragment.java b/app/src/main/java/com/uiui/os/fragment/CustomFragment.java index 065ebf4..bddc6c7 100644 --- a/app/src/main/java/com/uiui/os/fragment/CustomFragment.java +++ b/app/src/main/java/com/uiui/os/fragment/CustomFragment.java @@ -5,13 +5,20 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; import android.os.BatteryManager; import android.os.Bundle; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; -import android.os.SystemClock; +import android.provider.Settings; +import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -22,45 +29,73 @@ import android.widget.TextView; import com.amap.api.location.AMapLocation; import com.amap.api.location.AMapLocationClient; import com.amap.api.location.AMapLocationListener; +import com.blankj.utilcode.util.NetworkUtils; import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import com.king.view.circleprogressview.CircleProgressView; import com.qweather.sdk.bean.base.Code; import com.qweather.sdk.bean.base.Lang; import com.qweather.sdk.bean.base.Unit; import com.qweather.sdk.bean.weather.WeatherNowBean; import com.qweather.sdk.view.QWeather; +import com.tencent.mmkv.MMKV; import com.uiui.os.BuildConfig; import com.uiui.os.R; +import com.uiui.os.adapter.AlarmClockAdapter; +import com.uiui.os.adapter.NotificationAdapter; +import com.uiui.os.bean.AlarmClockData; import com.uiui.os.bean.AlarmItem; import com.uiui.os.utils.AmapManager; -import com.uiui.os.utils.ApkUtils; +import com.uiui.os.utils.APKUtils; import com.uiui.os.utils.AppUtil; import com.uiui.os.utils.Utils; import java.lang.reflect.Method; +import java.lang.reflect.Type; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; + /** * A simple {@link Fragment} subclass. * Use the {@link CustomFragment#newInstance} factory method to * create an instance of this fragment. */ -public class CustomFragment extends Fragment { +public class CustomFragment extends Fragment implements NetworkUtils.OnNetworkStatusChangedListener { + @BindView(R.id.tv_add) + TextView tv_add; + @BindView(R.id.tv_battery) + TextView tv_battery; + @BindView(R.id.tv_location) + TextView tv_location; + @BindView(R.id.iv_pic) + ImageView iv_pic; + @BindView(R.id.tv_temp) + TextView tv_temp; + @BindView(R.id.cpv) + CircleProgressView cpv; + @BindView(R.id.cl_alarm) + ConstraintLayout cl_alarm; + @BindView(R.id.iv_charging) + ImageView iv_charging; + @BindView(R.id.recyclerView) + RecyclerView recyclerView; + @BindView(R.id.rv_clock) + RecyclerView rv_clock; + @BindView(R.id.wifi_ssid) + TextView wifi_ssid; + @BindView(R.id.cl_wifi) + ConstraintLayout cl_wifi; + private String TAG = CustomFragment.class.getSimpleName(); - - private TextView tv_time,tv_add, tv_type, tv_status; - private ImageView iv_pic; - private TextView tv_temp; - private TextView tv_battery; - private TextView tv_location; - private CircleProgressView cpv; - private ConstraintLayout cl_alarm; - private ImageView iv_charging; private int[] mShaderColors = new int[]{0xFFfa3db5, 0xFFF8867E, 0xFFF79F6B, 0xFFF79F6B, 0xFFF79F6B, 0xFFF8867E, 0xFFfa3db5}; - + private View rootView; + private List alarmItemList; + private NotificationAdapter notificationAdapter; + private AlarmClockAdapter alarmClockAdapter; + private MMKV mmkv; // TODO: Rename parameter arguments, choose names that match // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER @@ -71,9 +106,6 @@ public class CustomFragment extends Fragment { private String mParam1; private String mParam2; - private View rootView; - private AlarmItem alarmItem; - public CustomFragment() { // Required empty public constructor } @@ -96,18 +128,20 @@ public class CustomFragment extends Fragment { return fragment; } - public void setAlarmItem(AlarmItem item) { - this.alarmItem = item; + public void setAlarmItem(List alarmItem) { + this.alarmItemList = alarmItem; setAlarm(); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + NetworkUtils.registerNetworkStatusChangedListener(this); if (getArguments() != null) { mParam1 = getArguments().getString(ARG_PARAM1); mParam2 = getArguments().getString(ARG_PARAM2); } + mmkv = MMKV.defaultMMKV(); registerBatteryReceiver(); getActivity().registerReceiver(mbatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); } @@ -128,6 +162,39 @@ public class CustomFragment extends Fragment { BatteryReceiver batteryReceiver; + @Override + public void onDisconnected() { + wifi_ssid.setText("WiFi未连接"); + } + + @Override + public void onConnected(NetworkUtils.NetworkType networkType) { + if (networkType == NetworkUtils.NetworkType.NETWORK_WIFI) { + wifi_ssid.setText(getConnectWifiSsid()); + } else { + wifi_ssid.setText("WiFi未连接"); + } + } + + private String getConnectWifiSsid() { + WifiManager wifiManager = (WifiManager) getActivity().getSystemService(Context.WIFI_SERVICE); + WifiInfo wifiInfo = wifiManager.getConnectionInfo(); + Log.d("wifiInfo", wifiInfo.toString()); + Log.d("SSID", wifiInfo.getSSID()); + return wifiInfo.getSSID(); + } + + /** + * 检查wifi是否处开连接状态 + * + * @return + */ + public boolean isWifiConnect() { + ConnectivityManager connManager = (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo mWifiInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + return mWifiInfo.isConnected(); + } + private class BatteryReceiver extends BroadcastReceiver { @Override @@ -139,7 +206,7 @@ public class CustomFragment extends Fragment { // 最大电量 int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0); int elec = (level * 100) / scale; - Log.e(TAG, "electricity:=" + elec + "%"); + Log.i(TAG, "electricity:=" + elec + "%"); tv_battery.setText(elec + "%"); } else if (Intent.ACTION_POWER_CONNECTED.equals(action) || Intent.ACTION_POWER_DISCONNECTED.equals(action) @@ -155,14 +222,14 @@ public class CustomFragment extends Fragment { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - Log.e(TAG, "onReceive: " + action); + Log.i(TAG, "onReceive: " + action); if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN); if (status == BatteryManager.BATTERY_STATUS_CHARGING) { if (rootView != null) { iv_charging.setVisibility(View.VISIBLE); } - }else { + } else { if (rootView != null) { iv_charging.setVisibility(View.GONE); } @@ -176,67 +243,63 @@ public class CustomFragment extends Fragment { Bundle savedInstanceState) { // Inflate the layout for this fragment rootView = inflater.inflate(R.layout.fragment_custom, container, false); + ButterKnife.bind(this, rootView); initView(); initData(); return rootView; } private void initView() { - tv_time = rootView.findViewById(R.id.tv_time); - tv_add = rootView.findViewById(R.id.tv_add); - tv_type = rootView.findViewById(R.id.tv_type); - tv_status = rootView.findViewById(R.id.tv_status); - - iv_pic = rootView.findViewById(R.id.iv_pic); - tv_temp = rootView.findViewById(R.id.tv_temp); - tv_location = rootView.findViewById(R.id.tv_location); - tv_battery = rootView.findViewById(R.id.tv_battery); Log.e(TAG, "initView: " + Utils.getBatteryLevel(getActivity())); tv_battery.setText(Utils.getBatteryLevel(getActivity()) + "%"); - cpv = rootView.findViewById(R.id.cpv); cpv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { killBackgroundApp(); } }); - cl_alarm = rootView.findViewById(R.id.cl_alarm); cl_alarm.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - ApkUtils.openPackage(getActivity(), "com.alarmclock.uiui"); + APKUtils.openPackage(getActivity(), "com.alarmclock.uiui"); } }); - iv_charging = rootView.findViewById(R.id.iv_charging); + notificationAdapter = new NotificationAdapter(); + recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); + recyclerView.setAdapter(notificationAdapter); + if (isWifiConnect()) { + wifi_ssid.setText(getConnectWifiSsid()); + } else { + wifi_ssid.setText("WiFi未连接"); + } + cl_wifi.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)); //直接进入手机中的wifi网络设置界面 + } + }); + alarmClockAdapter = new AlarmClockAdapter(); + rv_clock.setLayoutManager(new LinearLayoutManager(getActivity())); + rv_clock.setAdapter(alarmClockAdapter); setAlarm(); refreshMemory(); } private void setAlarm() { if (rootView == null) return; - if (alarmItem == null) { - tv_time.setText("暂无闹钟"); - tv_time.setVisibility(View.GONE); + if (alarmItemList == null) { tv_add.setVisibility(View.VISIBLE); - tv_type.setVisibility(View.GONE); - tv_status.setVisibility(View.GONE); + rv_clock.setVisibility(View.GONE); } else { - tv_time.setText(alarmItem.mTime); - tv_time.setVisibility(View.VISIBLE); tv_add.setVisibility(View.GONE); - tv_type.setText(alarmItem.mRepeatType); - tv_type.setVisibility(View.VISIBLE); - tv_status.setVisibility(View.VISIBLE); - if (alarmItem.mActive) { - tv_status.setText("打开"); - } else { - tv_status.setText("关闭"); - } + rv_clock.setVisibility(View.VISIBLE); + alarmClockAdapter.setAlarmItemList(alarmItemList); } } private void initData() { initAmap(); + getAlarmClock(); } private void initAmap() { @@ -258,6 +321,17 @@ public class CustomFragment extends Fragment { }); } + private void getAlarmClock() { + String jsonString = mmkv.decodeString("AlarmClock", ""); + if (!TextUtils.isEmpty(jsonString)) { + Type type = new TypeToken>() { + }.getType(); + Gson gson = new Gson(); + List data = gson.fromJson(jsonString, type); + notificationAdapter.setDataList(data); + } + } + private void getweather(double longitude, double latitude) { /** * 实况天气数据 @@ -276,7 +350,7 @@ public class CustomFragment extends Fragment { @Override public void onSuccess(WeatherNowBean weatherBean) { - Log.e(TAG, "getWeather onSuccess: " + new Gson().toJson(weatherBean)); +// Log.e(TAG, "getWeather onSuccess: " + new Gson().toJson(weatherBean)); //先判断返回的status是否正确,当status正确时获取数据,若status不正确,可查看status对应的Code值找到原因 if (Code.OK == weatherBean.getCode()) { WeatherNowBean.NowBaseBean now = weatherBean.getNow(); @@ -295,7 +369,7 @@ public class CustomFragment extends Fragment { private void killBackgroundApp() { - List pkgList = ApkUtils.queryFilterAppList(getActivity()); + List pkgList = APKUtils.queryFilterAppList(getActivity()); for (String pkg : pkgList) { if (pkg.equalsIgnoreCase(BuildConfig.APPLICATION_ID)) continue; killBackgroundProcesses(pkg); @@ -333,6 +407,7 @@ public class CustomFragment extends Fragment { @Override public void onDestroy() { super.onDestroy(); + NetworkUtils.unregisterNetworkStatusChangedListener(this); if (batteryReceiver != null) { getActivity().unregisterReceiver(batteryReceiver); } diff --git a/app/src/main/java/com/uiui/os/network/NetInterfaceManager.java b/app/src/main/java/com/uiui/os/network/NetInterfaceManager.java new file mode 100644 index 0000000..7372d54 --- /dev/null +++ b/app/src/main/java/com/uiui/os/network/NetInterfaceManager.java @@ -0,0 +1,178 @@ +package com.uiui.os.network; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.util.Log; + +import com.uiui.os.bean.AlarmClockData; +import com.uiui.os.bean.BaseResponse; +import com.uiui.os.network.api.AlarmClockApi; +import com.uiui.os.network.api.AppUsageRecordApi; +import com.uiui.os.network.api.RunNewApp; +import com.uiui.os.network.api.SendScreenshotApi; +import com.uiui.os.utils.MD5Util; +import com.uiui.os.utils.Utils; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.schedulers.Schedulers; +import okhttp3.Cache; +import okhttp3.Headers; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory; +import retrofit2.converter.gson.GsonConverterFactory; + +public class NetInterfaceManager { + @SuppressLint("StaticFieldLeak") + private static NetInterfaceManager INSTANCE; + private Context mContext; + private Retrofit mRetrofit; + private OkHttpClient okHttpClient; + private final ConcurrentHashMap requestIdsMap = new ConcurrentHashMap<>(); + //超时时间 + private static int timeOut = 30; + // 缓存文件最大限制大小20M + private static long cacheSize = 1024 * 1024 * 64; + + public static final String HTTP_KEY = "YTM3YTAxNTJmMmZmNzkyM2E2YzIwZjlhZTc0NzNmMGI="; + public static final String CUSTOM_REPEAT_REQ_PROTOCOL = "MY_CUSTOM_REPEAT_REQ_PROTOCOL"; + + private NetInterfaceManager(Context context) { + this.mContext = context; + if (okHttpClient == null) { + Interceptor interceptor = new Interceptor() { + @NotNull + @Override + public Response intercept(@NotNull Chain chain) throws IOException { + Request request = chain.request(); + //相同的请求 + String requestKey = MD5Util.getUpperMD5Str(request.method() + request.url().toString()); + long time = System.currentTimeMillis();//请求时间 + try { + if (requestIdsMap.size() > 0 && requestIdsMap.containsKey(requestKey)) { + Log.e("REPEAT-REQUEST", "重复请求:" + requestKey + " Method @" + request.method() + " --- " + " URL = " + request.url()); + chain.call().cancel(); + return new Response.Builder() + .protocol(Protocol.get(CUSTOM_REPEAT_REQ_PROTOCOL)) + .request(request) //multi thread + .build(); + } + requestIdsMap.put(requestKey, time); + Log.e("REPEAT-REQUEST", "注册请求:" + requestKey + " Method @" + request.method() + " --- " + " URL = " + request.url()); +// Request.Builder builder = request.newBuilder(); +// builder.addHeader("header", jsonObject.toString()); + return chain.proceed(request); + } catch (IOException e) { + throw e; + } finally { + if (requestIdsMap.containsKey(requestKey) && requestIdsMap.containsValue(time)) {//请求任务完成删除map中的数据 + requestIdsMap.remove(requestKey); + Log.e("REPEAT-REQUEST", "移除请求:" + requestKey + " Method @" + request.method() + " --- " + " URL = " + request.url()); + } + } + } + }; + + //如果无法生存缓存文件目录,检测权限使用已经加上,检测手机是否把文件读写权限禁止了 + 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(interceptor); + + // 设置缓存文件路径 + String cacheDirectory = mContext.getExternalCacheDir().getAbsolutePath() + "/OkHttpCache"; + Cache cache = new Cache(new File(cacheDirectory), cacheSize); + builder.cache(cache);// 设置缓存 + okHttpClient = builder.build(); + } + + if (mRetrofit == null) { + mRetrofit = new Retrofit.Builder() + .client(okHttpClient) + .baseUrl(URLAddress.ROOT_URL) + .addConverterFactory(GsonConverterFactory.create()) + .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) + .build(); + } + } + + /** + * 打印全局统一拦截添加的Http Headers + *

+ * 全局拦截的http 没法在配置中直接打印处理,因为先http 请求然后打印然后拦截添加的 + * + * @param request + */ + private static void logRequestHeaders(Request request) { + Log.e("OKhttp ", " 开始打印HTTP请求 Headers \n"); + Headers headers = request.headers(); + for (int i = 0, count = headers.size(); i < count; i++) { + String name = headers.name(i); + // Skip headers from the request body as they are explicitly logged above. + if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) { + Log.e("OKhttp: " + i + " ", name + ": " + headers.value(i)); + } + } + Log.e("OKhttp ", " 打印HTTP请求完成 Headers \n"); + } + + public static void init(Context context) { + if (INSTANCE == null) { + INSTANCE = new NetInterfaceManager(context); + } + } + + public static NetInterfaceManager getInstance() { + if (INSTANCE == null) { + throw new IllegalStateException("You must be init NetworkManager first"); + } + return INSTANCE; + } + + public OkHttpClient getOkHttpClient() { + return okHttpClient; + } + + /** + * 通过sn获取设备的信息 + * + * @return + */ + public Observable>> getAlarmClockApiObservable() { + return mRetrofit + .create(AlarmClockApi.class) + .getAlarmClockApiApi(Utils.getSerial()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + + public Observable getRunningAppObservable(String json) { + return mRetrofit.create(RunNewApp.class) + .sendRunningInfo(Utils.getSerial(), json) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + + public AppUsageRecordApi getAppUsageRecordControl() { + return mRetrofit.create(AppUsageRecordApi.class); + } + + public SendScreenshotApi getScreenshotApi() { + return mRetrofit.create(SendScreenshotApi.class); + } +} diff --git a/app/src/main/java/com/uiui/os/network/URLAddress.java b/app/src/main/java/com/uiui/os/network/URLAddress.java new file mode 100644 index 0000000..d8fa067 --- /dev/null +++ b/app/src/main/java/com/uiui/os/network/URLAddress.java @@ -0,0 +1,13 @@ +package com.uiui.os.network; + +public class URLAddress { + public static final String ROOT_URL = "https://led.aolelearn.cn/android/"; + //获取闹钟 + public static final String GET_ALARM_CLOCK = "getAlarmClock"; + //应用使用记录 + public static final String APP_USAGE_RECORD = "appUsageRecord"; + //正在运行的应用 + public static final String RUN_NEW_APP = "app/runNewApp"; + //上传截图 + public static final String SEND_SCREENSHOT = "sn/uploadScreenshot"; +} diff --git a/app/src/main/java/com/uiui/os/network/api/AlarmClockApi.java b/app/src/main/java/com/uiui/os/network/api/AlarmClockApi.java new file mode 100644 index 0000000..75bc53f --- /dev/null +++ b/app/src/main/java/com/uiui/os/network/api/AlarmClockApi.java @@ -0,0 +1,18 @@ +package com.uiui.os.network.api; + +import com.uiui.os.bean.AlarmClockData; +import com.uiui.os.bean.BaseResponse; +import com.uiui.os.network.URLAddress; + +import java.util.List; + +import io.reactivex.rxjava3.core.Observable; +import retrofit2.http.GET; +import retrofit2.http.Query; + +public interface AlarmClockApi { + @GET(URLAddress.GET_ALARM_CLOCK) + Observable>> getAlarmClockApiApi( + @Query("sn") String sn + ); +} diff --git a/app/src/main/java/com/uiui/os/network/api/AppUsageRecordApi.java b/app/src/main/java/com/uiui/os/network/api/AppUsageRecordApi.java new file mode 100644 index 0000000..d159756 --- /dev/null +++ b/app/src/main/java/com/uiui/os/network/api/AppUsageRecordApi.java @@ -0,0 +1,21 @@ +package com.uiui.os.network.api; + +import com.uiui.os.bean.BaseResponse; +import com.uiui.os.network.URLAddress; + +import io.reactivex.rxjava3.core.Observable; +import retrofit2.http.Field; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.POST; + +public interface AppUsageRecordApi { + @FormUrlEncoded + @POST(URLAddress.APP_USAGE_RECORD) + Observable sendappUsageRecord( + @Field("sn") String sn, + @Field("app_name") String app_name, + @Field("app_package") String app_package, + @Field("open_time") long open_time, + @Field("close_time") long close_time + ); +} diff --git a/app/src/main/java/com/uiui/os/network/api/RunNewApp.java b/app/src/main/java/com/uiui/os/network/api/RunNewApp.java new file mode 100644 index 0000000..9b2a14c --- /dev/null +++ b/app/src/main/java/com/uiui/os/network/api/RunNewApp.java @@ -0,0 +1,18 @@ +package com.uiui.os.network.api; + +import com.uiui.os.bean.BaseResponse; +import com.uiui.os.network.URLAddress; + +import io.reactivex.rxjava3.core.Observable; +import retrofit2.http.Field; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.POST; + +public interface RunNewApp { + @FormUrlEncoded + @POST(URLAddress.RUN_NEW_APP) + Observable sendRunningInfo( + @Field("sn") String sn, + @Field("app") String app + ); +} diff --git a/app/src/main/java/com/uiui/os/network/api/SendScreenshotApi.java b/app/src/main/java/com/uiui/os/network/api/SendScreenshotApi.java new file mode 100644 index 0000000..7a93163 --- /dev/null +++ b/app/src/main/java/com/uiui/os/network/api/SendScreenshotApi.java @@ -0,0 +1,23 @@ +package com.uiui.os.network.api; + + +import com.uiui.os.bean.BaseResponse; +import com.uiui.os.network.URLAddress; + +import java.util.Map; + +import io.reactivex.rxjava3.core.Observable; +import okhttp3.MultipartBody; +import retrofit2.http.Multipart; +import retrofit2.http.POST; +import retrofit2.http.Part; +import retrofit2.http.Query; + +public interface SendScreenshotApi { + @Multipart + @POST(URLAddress.SEND_SCREENSHOT) + Observable sendScreenshot( + @Query("sn") String sn, + @Part MultipartBody.Part file + ); +} diff --git a/app/src/main/java/com/uiui/os/receiver/BootReceiver.java b/app/src/main/java/com/uiui/os/receiver/BootReceiver.java new file mode 100644 index 0000000..a8412bd --- /dev/null +++ b/app/src/main/java/com/uiui/os/receiver/BootReceiver.java @@ -0,0 +1,88 @@ +package com.uiui.os.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.SystemClock; +import android.text.TextUtils; +import android.util.Log; + +import com.uiui.os.service.MainService; + +import java.util.concurrent.TimeUnit; + +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.ObservableOnSubscribe; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; + +public class BootReceiver extends BroadcastReceiver { + private static String TAG = BootReceiver.class.getSimpleName(); + public static final String BOOT_COMPLETED = "zuoyeos.action.BOOT_COMPLETED"; + + static { + getLockedState(); + } + + private static void getLockedState() { + Observable.create((ObservableOnSubscribe) emitter -> start = emitter::onNext) + .throttleLast(1, TimeUnit.HOURS) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + Log.e("getLockedState", "onSubscribe: "); + } + + @Override + public void onNext(Long aLong) { + + } + + @Override + public void onError(Throwable e) { + Log.e("getLockedState", "onError: " + e.getMessage()); + } + + @Override + public void onComplete() { + Log.e("getLockedState", "onComplete: "); + } + }); + } + + private interface Start { + void onstar(long time); + } + + private static Start start; + + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + Log.e(TAG, "action:" + action); + if (TextUtils.isEmpty(action)) { + return; + } + switch (action) { + default: + break; + case BOOT_COMPLETED: + MainService.mPresenter.getAlarmClock(); + case Intent.ACTION_BOOT_COMPLETED: + + break; + case Intent.ACTION_BATTERY_CHANGED: + case Intent.ACTION_BATTERY_LOW: + case Intent.ACTION_BATTERY_OKAY: + case Intent.ACTION_POWER_CONNECTED: + case Intent.ACTION_POWER_DISCONNECTED: + case Intent.ACTION_DATE_CHANGED: + case Intent.ACTION_TIME_TICK: + case Intent.ACTION_USER_PRESENT: + case Intent.ACTION_SCREEN_OFF: + case Intent.ACTION_SCREEN_ON: + break; + } + } +} diff --git a/app/src/main/java/com/uiui/os/service/AlarmService.java b/app/src/main/java/com/uiui/os/service/AlarmService.java new file mode 100644 index 0000000..7128b79 --- /dev/null +++ b/app/src/main/java/com/uiui/os/service/AlarmService.java @@ -0,0 +1,17 @@ +package com.uiui.os.service; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public class AlarmService extends Service { + public AlarmService() { + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + +} diff --git a/app/src/main/java/com/uiui/os/service/MainService.java b/app/src/main/java/com/uiui/os/service/MainService.java new file mode 100644 index 0000000..3917ae1 --- /dev/null +++ b/app/src/main/java/com/uiui/os/service/MainService.java @@ -0,0 +1,318 @@ +package com.uiui.os.service; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.IBinder; +import android.os.PowerManager; +import android.text.TextUtils; +import android.text.format.Formatter; +import android.util.Log; + +import com.arialyy.annotations.Download; +import com.arialyy.aria.core.Aria; +import com.arialyy.aria.core.task.DownloadTask; +import com.blankj.utilcode.util.ImageUtils; +import com.blankj.utilcode.util.NetworkUtils; +import com.uiui.os.activity.MainContact; +import com.uiui.os.activity.MainPresenter; +import com.uiui.os.activity.NoticeActivity; +import com.uiui.os.base.BaseService; +import com.uiui.os.bean.AlarmClockData; +import com.uiui.os.bean.BaseResponse; +import com.uiui.os.network.NetInterfaceManager; +import com.uiui.os.utils.CmdUtil; +import com.uiui.os.utils.ForegroundAppUtil; +import com.uiui.os.utils.TimeUtils; +import com.uiui.os.utils.ToastUtil; +import com.uiui.os.utils.Utils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.ObservableEmitter; +import io.reactivex.rxjava3.core.ObservableOnSubscribe; +import io.reactivex.rxjava3.core.ObservableSource; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.schedulers.Schedulers; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.RequestBody; + + +public class MainService extends BaseService implements MainContact.MainView, NetworkUtils.OnNetworkStatusChangedListener { + private static final String TAG = MainService.class.getSimpleName(); + public static MainPresenter mPresenter; + + public MainService() { + + } + + @Override + public void onDisconnected() { + + } + + @Override + public void onConnected(NetworkUtils.NetworkType networkType) { + + } + + @Override + public IBinder onBind(Intent intent) { + Log.e(TAG, "onBind: "); + return null; + } + + + @Override + public void onCreate() { + super.onCreate(); + Log.e(TAG, "onCreate: "); + Aria.init(this); + Aria.download(this).register(); + mPresenter = new MainPresenter(this); + mPresenter.attachView(this); + mPresenter.setLifecycle(lifecycleSubject); + NetworkUtils.registerNetworkStatusChangedListener(this); + mPresenter.getAlarmClock(); + registerAlarmReceiver(); + registerTimeReceiver(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.e(TAG, "onStartCommand: "); + return START_STICKY; + } + + @Override + public void onDestroy() { + super.onDestroy(); + mPresenter.detachView(); + NetworkUtils.unregisterNetworkStatusChangedListener(this); + if (alarmReceiver != null) { + unregisterReceiver(alarmReceiver); + } + if (mTimeChangedReceiver != null) { + unregisterReceiver(mTimeChangedReceiver); + } + } + + @Override + public void setAlarmClock(List dataList) { + + } + + public static final String ALARMWAKEUP = "ALARM_WAKEUP"; + + private void registerAlarmReceiver() { + if (alarmReceiver == null) { + alarmReceiver = new AlarmReceiver(); + } + IntentFilter filter = new IntentFilter(); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + filter.addAction(ALARMWAKEUP); + registerReceiver(alarmReceiver, filter); + } + + private AlarmReceiver alarmReceiver = new AlarmReceiver(); + + private class AlarmReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (TextUtils.isEmpty(action)) return; + Log.e(TAG, "onReceive: " + action); + String title = intent.getStringExtra("title"); + int code = intent.getIntExtra("id", -1); + Log.e(TAG, "onReceive: title = " + title); + if (ALARMWAKEUP.equals(action)) { + Intent noticeIntent = new Intent(); + noticeIntent.putExtra("id", code); + noticeIntent.setClass(MainService.this, NoticeActivity.class); + noticeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(noticeIntent); + } + } + } + + //监听时间和日期变化 + public void registerTimeReceiver() { + mTimeChangedReceiver = new TimeChangedReceiver(); + IntentFilter filter = new IntentFilter(); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + filter.addAction(Intent.ACTION_DATE_CHANGED); + filter.addAction(Intent.ACTION_TIME_CHANGED); + filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + filter.addAction(Intent.ACTION_TIME_TICK); + registerReceiver(mTimeChangedReceiver, filter); + } + + private TimeChangedReceiver mTimeChangedReceiver; + + private class TimeChangedReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_DATE_CHANGED.equals(intent.getAction())) { + Log.e(TAG, "TimeChangedReceiver:" + "data changed"); + } else if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) { + Log.e(TAG, "TimeChangedReceiver:" + "time changed"); + } else if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) { + Log.e(TAG, "TimeChangedReceiver:" + "timezone changed"); + } else if (Intent.ACTION_TIME_TICK.equals(intent.getAction())) { + Log.e(TAG, "TimeChangedReceiver:" + "time tick"); + isScreenshot(); + } + } + } + + private final static long ONE_HOURS_TIME = 60 * 60 * 1000; + private final static long TEN_MINUTES_TIME = 60 * 10 * 1000; + + + private void isScreenshot() { + //1、检测应用使用情况,如果设备长时间运行一个应用,超过1小时,启动截屏一次。 + //2、检测设备在非正常时间使用时,使用第三方应用,在运行10分钟后,启动截屏功能一次 + //屏幕未点亮时不用截图 + // TODO: 2021/12/20 计算当前app打开时间 + String topPackageName = ForegroundAppUtil.getForegroundPackageName(MainService.this); + Log.e(TAG, "isScreenshot: " + topPackageName); + String pkg = TimeUtils.getInstance().getAppPackageName(); + if (TextUtils.isEmpty(pkg)) { + return; + } + + PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + //true为打开,false为关闭 + boolean screenOn = powerManager.isInteractive(); + Log.e(TAG, "isScreenshot: screenOn = " + screenOn); + if (!screenOn) return; + + long startTime = TimeUtils.getInstance().getStartTime(); + if (TimeUtils.getInstance().isNormalTime()) {//正常时间段 + if (System.currentTimeMillis() - startTime >= ONE_HOURS_TIME) { + Log.e(TAG, "isScreenshot: " + "截图"); + doscreenshot(this); + } + } else {//非正常时间段 + if (System.currentTimeMillis() - startTime >= TEN_MINUTES_TIME) { + Log.e(TAG, "isScreenshot: " + "截图"); + doscreenshot(this); + } + } + } + + private static Observable getScreenshot(Context context, String filePath) { + Observable screenshotObservable = Observable.create(new ObservableOnSubscribe() { + @Override + public void subscribe(ObservableEmitter e) { + int code = CmdUtil.execute("screencap -p " + filePath).code; + e.onNext(code); + e.onComplete(); + } + }).subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + return screenshotObservable; + } + + private static Observable getSendFile(String path, MultipartBody.Part body) { + return NetInterfaceManager.getInstance() + .getScreenshotApi() + .sendScreenshot(Utils.getSerial(), body) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + + public static void doscreenshot(Context context) { + long time = System.currentTimeMillis(); + String path = context.getExternalFilesDir("db").getAbsolutePath(); + String filePath = path + File.separator + time + ".png"; + getScreenshot(context, filePath).concatMap(new Function>() { + @Override + public ObservableSource apply(Integer integer) throws Exception { + if (integer != 0) { + throw new FileNotFoundException(); + } + File file = new File(filePath); + if (!file.exists()) { + throw new FileNotFoundException(filePath); + } + Bitmap bitmap = BitmapFactory.decodeFile(filePath); + if (bitmap.getWidth() < bitmap.getHeight()) { + bitmap = ImageUtils.rotate(bitmap, -90, bitmap.getWidth(), bitmap.getHeight()); + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); + file.createNewFile(); + FileOutputStream fos = new FileOutputStream(file); + InputStream is = new ByteArrayInputStream(baos.toByteArray()); + int x; + byte[] b = new byte[1024 * 100]; + while ((x = is.read(b)) != -1) { + fos.write(b, 0, x); + } + fos.close(); + RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file); + MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), requestFile); + return getSendFile(filePath, body); + } + }).subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + Log.e("screenshot", "onSubscribe: "); + } + + @Override + public void onNext(BaseResponse baseResponse) { + Log.e("screenshot", "onNext: " + baseResponse); + } + + @Override + public void onError(Throwable e) { + Log.e("screenshot", "onError: " + e.getMessage()); + onComplete(); + } + + @Override + public void onComplete() { + Log.e("screenshot", "onComplete: "); + } + }); + } + + //在这里处理任务执行中的状态,如进度进度条的刷新 + @Download.onTaskRunning + protected void running(DownloadTask task) { + Log.e("aria", "正在下载:" + task.getPercent() + ":" + task.getExtendField()); + ToastUtil.show("正在下载:" + task.getPercent() + "%" + "\t" + Formatter.formatFileSize(MainService.this, task.getSpeed()) + "/s"); + } + + @Download.onTaskComplete + void taskComplete(DownloadTask task) { + //在这里处理任务完成的状态 + + } + + @Download.onTaskFail + void taskFail(DownloadTask task, Exception e) { + + } +} diff --git a/app/src/main/java/com/uiui/os/utils/ApkUtils.java b/app/src/main/java/com/uiui/os/utils/APKUtils.java similarity index 78% rename from app/src/main/java/com/uiui/os/utils/ApkUtils.java rename to app/src/main/java/com/uiui/os/utils/APKUtils.java index 596a525..4630e72 100644 --- a/app/src/main/java/com/uiui/os/utils/ApkUtils.java +++ b/app/src/main/java/com/uiui/os/utils/APKUtils.java @@ -1,9 +1,11 @@ package com.uiui.os.utils; +import android.app.Application; import android.content.ComponentName; 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.pm.ResolveInfo; import android.text.TextUtils; @@ -17,7 +19,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -public class ApkUtils { +public class APKUtils { private static String[] excludePackageName = {BuildConfig.APPLICATION_ID}; @@ -56,6 +58,7 @@ public class ApkUtils { /** * 获取第三方应用 + * * @param context * @return */ @@ -77,6 +80,29 @@ public class ApkUtils { return applicationInfos; } + public static PackageInfo getPackageInfo(Context context, String pkg) { + PackageManager packageManager = context.getPackageManager(); + PackageInfo packageInfo = null; + try { + packageInfo = packageManager.getPackageInfo(pkg, 0); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return packageInfo; + } + + public static ApplicationInfo getApplicationInfo(Context context, String pkg) { + PackageManager packageManager = context.getPackageManager(); + ApplicationInfo applicationInfo = null; + try { + applicationInfo = packageManager.getApplicationInfo(pkg, 0); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + + return applicationInfo; + } + public static void openApp(Context context, String packageName) { Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName); if (intent != null) { @@ -86,14 +112,14 @@ public class ApkUtils { } - public static Intent getAppOpenIntentByPackageName(Context context,String packageName){ + public static Intent getAppOpenIntentByPackageName(Context context, String packageName) { //Activity完整名 String mainAct = null; //根据包名寻找 PackageManager pkgMag = context.getPackageManager(); Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); - intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED|Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_NEW_TASK); List list = pkgMag.queryIntentActivities(intent, PackageManager.GET_ACTIVITIES); @@ -138,4 +164,20 @@ public class ApkUtils { return false; } + public static String getAPPVersionName(Context context, String packageName) { + String versionName = "0"; + + if (TextUtils.isEmpty(packageName)) { + return versionName; + } + PackageManager pm = context.getPackageManager(); + try { + PackageInfo packageInfo = pm.getPackageInfo(packageName, 0); + versionName = packageInfo.versionName; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return versionName; + } + } diff --git a/app/src/main/java/com/uiui/os/utils/AlarmUtils.java b/app/src/main/java/com/uiui/os/utils/AlarmUtils.java new file mode 100644 index 0000000..662a3a1 --- /dev/null +++ b/app/src/main/java/com/uiui/os/utils/AlarmUtils.java @@ -0,0 +1,287 @@ +package com.uiui.os.utils; + +import android.annotation.SuppressLint; +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Environment; +import android.text.TextUtils; +import android.util.Log; + +import androidx.core.content.ContextCompat; + +import com.arialyy.aria.core.Aria; +import com.blankj.utilcode.util.FileUtils; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.tencent.mmkv.MMKV; +import com.uiui.os.bean.AlarmClockData; +import com.uiui.os.service.MainService; + +import java.io.File; +import java.io.FileInputStream; +import java.lang.reflect.Type; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public class AlarmUtils { + @SuppressLint("StaticFieldLeak") + private static AlarmUtils sInstance; + private static String TAG = AlarmUtils.class.getSimpleName(); + private Context mContext; + private AlarmManager alarmManager; + private MMKV mmkv = MMKV.defaultMMKV(); + + + private AlarmUtils(Context context) { + this.mContext = context; + alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + } + + public static void init(Context context) { + if (sInstance == null) { + sInstance = new AlarmUtils(context); + } + } + + public static AlarmUtils getInstance() { + if (sInstance == null) { + throw new IllegalStateException("You must be init AmapManager first"); + } + return sInstance; + } + + private List alarmClockDataList; + private HashSet pendingIntents; + + public void setAlarmClockData(List data) { + this.alarmClockDataList = data; + if (pendingIntents == null) { + pendingIntents = getOldPendingIntents(); + } + for (PendingIntent pendingIntent : pendingIntents) { + alarmManager.cancel(pendingIntent); + } + List newData = mergeData(data); + for (AlarmClockData clockData : newData) { + setAlarm(clockData); + } + String jsonString = new Gson().toJson(newData); + Log.e(TAG, "setAlarmClockData: " + jsonString); + mmkv.encode("AlarmClock", jsonString); + } + + private List mergeData(List data) { + HashMap oldData = getOldData(); + List tempData = new ArrayList<>(); + for (AlarmClockData alarm : data) { + AlarmClockData oldAlarm = oldData.get(alarm.getId()); + if (oldAlarm == null) { + tempData.add(alarm); + } else { + if (oldAlarm.equals(alarm)) { + tempData.add(oldAlarm); + } else { + tempData.add(alarm); + } + } + } + return tempData; + } + + public void setAlarmString(List alarmClockData) { + String jsonString = new Gson().toJson(alarmClockData); + Log.e(TAG, "setAlarmString: " + jsonString); + mmkv.encode("AlarmClock", jsonString); + } + + public HashMap getOldData() { + String jsonString = mmkv.decodeString("AlarmClock", ""); + Log.e(TAG, "getOldPendingIntents: " + jsonString); + if (TextUtils.isEmpty(jsonString)) { + return new HashMap<>(); + } else { + Type type = new TypeToken>() { + }.getType(); + Gson gson = new Gson(); + List data = gson.fromJson(jsonString, type); + HashMap hashMap = new HashMap(); + for (AlarmClockData clockData : data) { + hashMap.put(clockData.getId(), clockData); + } + return hashMap; + } + } + + private HashSet getOldPendingIntents() { + HashSet pendingIntents = new HashSet<>(); + HashMap data = getOldData(); + for (AlarmClockData alarmClockData : data.values()) { + pendingIntents.add(getPendingIntent(alarmClockData)); + } + return pendingIntents; + } + + private PendingIntent getPendingIntent(AlarmClockData alarmClock) { + Intent intent = new Intent(MainService.ALARMWAKEUP); + intent.putExtra("title", alarmClock.getRemarks()); + intent.putExtra("id", alarmClock.getId()); + PendingIntent startPendingIntent = PendingIntent.getBroadcast(mContext, alarmClock.getId(), intent, PendingIntent.FLAG_CANCEL_CURRENT); + return startPendingIntent; + } + + private long getTimestamp(String timeString) { + if (TextUtils.isEmpty(timeString)) { + return 0; + } + if (timeString.length() == 8) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss"); + try { + Date date = simpleDateFormat.parse(timeString); + long timestamp = date.getTime() + getZeroTiemstamp(); + return timestamp; + } catch (ParseException e) { + return 0; + } + } else { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + try { + Date date = simpleDateFormat.parse(timeString); + long timestamp = date.getTime(); + return timestamp; + } catch (ParseException e) { + return 0; + } + } + } + + private long getZeroTiemstamp() { + long nowTime = System.currentTimeMillis(); + long dayMillisecond = 60 * 60 * 24 * 1000; + long zeroTime = ((nowTime) / dayMillisecond) * dayMillisecond; + Log.e(TAG, "getZeroTiemstamp: " + zeroTime); + return zeroTime; + } + + private static final int ONCE = 1; + private static final int LOOP = 2; + + public void setAlarm(AlarmClockData alarm) { + int id = alarm.getId(); + int type = alarm.getType(); + String timeString = alarm.getTime(); + long timestamp = getTimestamp(timeString); + String title = alarm.getRemarks(); + boolean finished = alarm.isFinished(); + String url = alarm.getVoice(); + String md5 = alarm.getVoice_md5(); + if (!TextUtils.isEmpty(url)) { + ariaDownload(url, md5); + } + if (type == ONCE) { + if (!finished) { + if (timestamp < System.currentTimeMillis()) { + Intent intent = new Intent(MainService.ALARMWAKEUP); + intent.putExtra("title", title); + intent.putExtra("id", id); + mContext.sendBroadcast(intent); + } else { + setOnceAlarm(MainService.ALARMWAKEUP, title, id, timestamp); + } + } + } else if (type == LOOP) { + setDayLoopAlarm(MainService.ALARMWAKEUP, title, id, timestamp); + } + } + + /** + * @param action + * @param requestCode + * @param timestamp 设置一次性闹钟 + */ + public void setOnceAlarm(String action, String extra, int requestCode, long timestamp) { + Intent intent = new Intent(action); + intent.putExtra("title", extra); + intent.putExtra("id", requestCode); + PendingIntent startPendingIntent = PendingIntent.getBroadcast(mContext, requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT); + pendingIntents.add(startPendingIntent); + alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timestamp, startPendingIntent); + Log.e(TAG, "setOnceAlarm: " + "id: " + requestCode + " title: " + extra + " timeString: " + timestamp); + } + + /** + * @param action + * @param requestCode + * @param timestamp 设置循环周期为一天的闹钟 + */ + public void setDayLoopAlarm(String action, String extra, int requestCode, long timestamp) { + setLoopAlarm(action, extra, requestCode, AlarmManager.INTERVAL_DAY, timestamp); + } + + /** + * @param action + * @param requestCode + * @param timestamp 设置循环周期为一小时的闹钟 + */ + public void setHourLoopAlarm(String action, String extra, int requestCode, long timestamp) { + setLoopAlarm(action, extra, requestCode, AlarmManager.INTERVAL_HOUR, timestamp); + } + + /** + * @param action + * @param requestCode + * @param intervalMillis + * @param timestamp 循环闹钟 + */ + public void setLoopAlarm(String action, String extra, int requestCode, long intervalMillis, long timestamp) { + Intent intent = new Intent(action); + intent.putExtra("title", extra); + intent.putExtra("id", requestCode); + PendingIntent startPendingIntent = PendingIntent.getBroadcast(mContext, requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT); + pendingIntents.add(startPendingIntent); + alarmManager.setWindow(AlarmManager.RTC_WAKEUP, timestamp, intervalMillis, startPendingIntent); + Log.e(TAG, "setLoopAlarm: " + "title: " + extra + " timeString: " + timestamp); + } + + + + public void ariaDownload(String url, String md5) { + String fileName = Utils.getFileNamefromURL(url); + File file = new File(Utils.getDownLoadPath(mContext) + fileName); + if (file.exists() && !file.isDirectory()) { + String fileMD5 = FileUtils.getFileMD5ToString(file); + Log.e("ariaDownload", "fileOnlineMD5=" + md5); + Log.e("ariaDownload", "fileMD5=" + fileMD5); + if (!md5.equals(fileMD5)) { + Aria.download(this) + .load(url) //读取下载地址 + .setFilePath(Utils.getDownLoadPath(mContext) + fileName) +// .ignoreFilePathOccupy() + .setExtendField(md5) + .create(); //启动下载} + } else { + Log.e("ariaDownload", "fileName = " + fileName + " exists"); + } + } else { + Aria.download(this) + .load(url) //读取下载地址 + .setFilePath(Utils.getDownLoadPath(mContext) + fileName) +// .ignoreFilePathOccupy() + .setExtendField(md5) + .create(); //启动下载} + } + } + + +} diff --git a/app/src/main/java/com/uiui/os/utils/CmdUtil.java b/app/src/main/java/com/uiui/os/utils/CmdUtil.java new file mode 100644 index 0000000..f717fb6 --- /dev/null +++ b/app/src/main/java/com/uiui/os/utils/CmdUtil.java @@ -0,0 +1,103 @@ +package com.uiui.os.utils; + +import android.text.TextUtils; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +public class CmdUtil { + private static final String TAG = "CmdUtil"; + + private static final String COMMAND_SH = "sh"; + private static final String COMMAND_EXIT = "exit\n"; + private static final String COMMAND_LINE_END = "\n"; + + + /** + * 运行命令 + * + * @param command 命令 + * @return 结果 + */ + public static Result execute(String command) { + Log.i(TAG, "execute() command = " + command); + Result result = new Result(); + + if (TextUtils.isEmpty(command)) { + Log.w(TAG, "WARNING: command should not be null or empty"); + return result; + } + + Process process = null; + DataOutputStream dos = null; + + try { + process = Runtime.getRuntime().exec(COMMAND_SH); + dos = new DataOutputStream(process.getOutputStream()); + dos.write(command.trim().getBytes()); + dos.writeBytes(COMMAND_LINE_END); + dos.flush(); + dos.writeBytes(COMMAND_EXIT); + dos.flush(); + result.code = process.waitFor(); + result.success = readBuffer(new BufferedReader(new InputStreamReader(process.getInputStream()))); + result.error = readBuffer(new BufferedReader(new InputStreamReader(process.getErrorStream()))); + Log.i(TAG, "result = " + result); + } catch (IOException ioe) { + ioe.printStackTrace(); + Log.e(TAG, ioe.getMessage()); + } catch (InterruptedException ie) { + ie.printStackTrace(); + Log.e(TAG, ie.getMessage()); + } finally { + try { + if (null != dos) { + dos.close(); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + Log.e(TAG, ioe.getMessage()); + } + if (null != process) { + process.destroy(); + } + } + return result; + } + + private static String readBuffer(BufferedReader bufferedReader) throws IOException { + StringBuilder sb = new StringBuilder(); + String s; + while ((s = bufferedReader.readLine()) != null) { + sb.append(s); + } + return sb.toString(); + } + + + /** + * Command执行结果 + */ + public static final class Result { + + public static final int SUCCESS = 0; + public static final int ERROR = -1; + + public int code = ERROR; + public String error; + public String success; + + @Override + public String toString() { + return "Result{" + + "code=" + code + + ", error='" + error + '\'' + + ", success='" + success + '\'' + + '}'; + } + } +} + diff --git a/app/src/main/java/com/uiui/os/utils/ForegroundAppUtil.java b/app/src/main/java/com/uiui/os/utils/ForegroundAppUtil.java new file mode 100644 index 0000000..9b28c8e --- /dev/null +++ b/app/src/main/java/com/uiui/os/utils/ForegroundAppUtil.java @@ -0,0 +1,116 @@ +package com.uiui.os.utils; + +import android.app.ActivityManager; +import android.app.usage.UsageStats; +import android.app.usage.UsageStatsManager; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.provider.Settings; +import android.text.TextUtils; + +import java.util.List; + +public class ForegroundAppUtil { + + private static final long END_TIME = System.currentTimeMillis(); + private static final long TIME_INTERVAL = 7 * 24 * 60 * 60 * 1000L; + private static final long START_TIME = END_TIME - TIME_INTERVAL; + + public static final String TOPAPP_KEY = "TOP_ALWAYS_SHOW_APP_NAME"; + private static String TAG = ForegroundAppUtil.class.getSimpleName(); + + public static String getForegroundPackageName(Context context) { + //系统应用可以直接获取 + ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + List runningTaskInfos = mActivityManager.getRunningTasks(1); + return runningTaskInfos.get(0).topActivity.getPackageName(); + } + + /** + * 获取栈顶的应用包名 + */ + public static String getForegroundActivityName(Context context) { + String currentClassName = ""; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + ActivityManager manager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); + currentClassName = manager.getRunningTasks(1).get(0).topActivity.getPackageName(); + } else { + UsageStats initStat = getForegroundUsageStats(context, START_TIME, END_TIME); + if (initStat != null) { + currentClassName = initStat.getPackageName(); + } + } + return currentClassName; + } + + /** + * 判断当前应用是否在前台 + */ + public static boolean isForegroundApp(Context context) { + return TextUtils.equals(getForegroundActivityName(context), context.getPackageName()); + } + + /** + * 获取时间段内, + */ + public static long getTotleForegroundTime(Context context) { + UsageStats usageStats = getCurrentUsageStats(context, START_TIME, END_TIME); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return usageStats != null ? usageStats.getTotalTimeInForeground() : 0; + } + return 0; + } + + /** + * 获取记录前台应用的UsageStats对象 + */ + private static UsageStats getForegroundUsageStats(Context context, long startTime, long endTime) { + UsageStats usageStatsResult = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + List usageStatses = getUsageStatsList(context, startTime, endTime); + if (usageStatses == null || usageStatses.isEmpty()) return null; + for (UsageStats usageStats : usageStatses) { + if (usageStatsResult == null || usageStatsResult.getLastTimeUsed() < usageStats.getLastTimeUsed()) { + usageStatsResult = usageStats; + } + } + } + return usageStatsResult; + } + + /** + * 获取记录当前应用的UsageStats对象 + */ + public static UsageStats getCurrentUsageStats(Context context, long startTime, long endTime) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + List usageStatses = getUsageStatsList(context, startTime, endTime); + if (usageStatses == null || usageStatses.isEmpty()) return null; + for (UsageStats usageStats : usageStatses) { + if (TextUtils.equals(usageStats.getPackageName(), context.getPackageName())) { + return usageStats; + } + } + } + return null; + } + + /** + * 通过UsageStatsManager获取List集合 + */ + public static List getUsageStatsList(Context context, long startTime, long endTime) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + UsageStatsManager manager = (UsageStatsManager) context.getApplicationContext().getSystemService(Context.USAGE_STATS_SERVICE); + //UsageStatsManager.INTERVAL_WEEKLY,UsageStatsManager的参数定义了5个,具体查阅源码 + List usageStatses = manager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, startTime, endTime); + if (usageStatses == null || usageStatses.size() == 0) {// 没有权限,获取不到数据 + Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.getApplicationContext().startActivity(intent); + return null; + } + return usageStatses; + } + return null; + } +} diff --git a/app/src/main/java/com/uiui/os/utils/MD5Util.java b/app/src/main/java/com/uiui/os/utils/MD5Util.java new file mode 100644 index 0000000..371ed8b --- /dev/null +++ b/app/src/main/java/com/uiui/os/utils/MD5Util.java @@ -0,0 +1,112 @@ +package com.uiui.os.utils; + +import android.annotation.SuppressLint; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class MD5Util { + + public static String packetMD5(String str) { + MessageDigest messageDigest = null; + try { + messageDigest = MessageDigest.getInstance("MD5"); + + messageDigest.reset(); + + messageDigest.update(str.getBytes("UTF-8")); + } catch (NoSuchAlgorithmException e) { + System.out.println("NoSuchAlgorithmException caught!"); + System.exit(-1); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + byte[] byteArray = messageDigest.digest(); + + StringBuffer md5StrBuff = new StringBuffer(); + + for (int i = 0; i < byteArray.length; i++) { + if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) + md5StrBuff.append("0").append( + Integer.toHexString(0xFF & byteArray[i])); + else + md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); + } + + return md5StrBuff.toString(); + } + + @SuppressLint("DefaultLocale") + public static String getUpperMD5Str(String str) { + MessageDigest messageDigest = null; + + try { + messageDigest = MessageDigest.getInstance("MD5"); + + messageDigest.reset(); + + messageDigest.update(str.getBytes("UTF-8")); + } catch (NoSuchAlgorithmException e) { + System.out.println("NoSuchAlgorithmException caught!"); + System.exit(-1); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + byte[] byteArray = messageDigest.digest(); + + StringBuffer md5StrBuff = new StringBuffer(); + + for (int i = 0; i < byteArray.length; i++) { + if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) + md5StrBuff.append("0").append( + Integer.toHexString(0xFF & byteArray[i])); + else + md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); + } + + return md5StrBuff.toString().toUpperCase(); + } + + + /** + * 获取16位的MD5 值得 + * + * @param str + * @return + */ + @SuppressLint("DefaultLocale") + public static String getUpperMD5Str16(String str) { + MessageDigest messageDigest = null; + + try { + messageDigest = MessageDigest.getInstance("MD5"); + + messageDigest.reset(); + + messageDigest.update(str.getBytes("UTF-8")); + } catch (NoSuchAlgorithmException e) { + System.out.println("NoSuchAlgorithmException caught!"); + System.exit(-1); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + byte[] byteArray = messageDigest.digest(); + + StringBuffer md5StrBuff = new StringBuffer(); + + for (int i = 0; i < byteArray.length; i++) { + if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) + md5StrBuff.append("0").append( + Integer.toHexString(0xFF & byteArray[i])); + else + md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); + } + + return md5StrBuff.toString().toUpperCase().substring(8, 24); + } + +} diff --git a/app/src/main/java/com/uiui/os/utils/TimeUtils.java b/app/src/main/java/com/uiui/os/utils/TimeUtils.java new file mode 100644 index 0000000..d9ed940 --- /dev/null +++ b/app/src/main/java/com/uiui/os/utils/TimeUtils.java @@ -0,0 +1,93 @@ +package com.uiui.os.utils; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.util.Log; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class TimeUtils { + @SuppressLint("StaticFieldLeak") + private static TimeUtils sInstance; + private Context mContext; + + private SimpleDateFormat ruleSDF = new SimpleDateFormat("HH:mm:ss"); + private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private String TAG = TimeUtils.class.getSimpleName(); + + private TimeUtils(Context context) { + this.mContext = context; + } + + public static void init(Context context) { + if (sInstance == null) { + sInstance = new TimeUtils(context); + } + } + + public static TimeUtils getInstance() { + if (sInstance == null) { + throw new IllegalStateException("You must be init TimeUtils first"); + } + return sInstance; + } + + private static String normalStartTime = "8:00:00"; + private static String unusualStartTime = "22:00:00"; + + private String appPackageName; + private long endTime = 0; + private long startTime = 0; + + public void setAppPackageName(String name) { + this.appPackageName = name; + } + + public String getAppPackageName() { + return appPackageName; + } + + public void setStartTime(long time) { + this.startTime = time; + } + + public long getStartTime() { + return startTime; + } + + public void setEndTime(long time) { + this.endTime = time; + } + + public long getEndTime() { + return endTime; + } + + private static final long DAY_TIME = 1000 * 60 * 60 * 24; + + public boolean isNormalTime() { + long nowTime = System.currentTimeMillis(); + String nowTimeString = ruleSDF.format(new Date(nowTime)); // 时间戳转换日期 + try { + Date startDate = ruleSDF.parse(normalStartTime); + Date endDate = ruleSDF.parse(unusualStartTime); + Date now = ruleSDF.parse(nowTimeString); + Log.e(TAG, "isScreenshot: startDate = " + startDate); + Log.e(TAG, "isScreenshot: endDate = " + endDate); + Log.e(TAG, "isScreenshot: now = " + now); + if (startDate.getTime() <= now.getTime() && now.getTime() <= endDate.getTime()) { + return true; + } else if (endDate.getTime() < now.getTime() && now.getTime() <= startDate.getTime() + DAY_TIME) { + return false; + } + } catch (ParseException e) { + e.printStackTrace(); + Log.e(TAG, "isScreenshot: " + e.getMessage()); + } + return false; + } + + +} diff --git a/app/src/main/java/com/uiui/os/utils/ToastUtil.java b/app/src/main/java/com/uiui/os/utils/ToastUtil.java new file mode 100644 index 0000000..6aa0849 --- /dev/null +++ b/app/src/main/java/com/uiui/os/utils/ToastUtil.java @@ -0,0 +1,93 @@ +package com.uiui.os.utils; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Color; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.widget.Toast; + +import com.blankj.utilcode.util.ToastUtils; +import com.uiui.os.BuildConfig; + + +/** + * Created by haoge on 2017/3/2. + */ + +public class ToastUtil { + private static final String TAG = ToastUtil.class.getSimpleName(); + @SuppressLint("StaticFieldLeak") + private static Context mContext; + private static Handler mainHandler = new Handler(Looper.getMainLooper()); + private static Toast debugToast; + private static Toast toast; + + + @SuppressLint("ShowToast") + public static void init(Context context) { + mContext = context; + toast = Toast.makeText(mContext, "", Toast.LENGTH_SHORT); + debugToast = Toast.makeText(mContext, "", Toast.LENGTH_SHORT); + + } + + private static long time1 = 0L; + private static long time2 = 0L; + + public static void show(final String msg) { + ToastUtils.make() +// .setBgColor(ColorUtils.getColor(R.color.toast_color)) + .setTextColor(Color.DKGRAY) +// .setGravity(Gravity.CENTER, 0, 0) + .setNotUseSystemToast() + .show(msg); + } + + public static void betaShow(final String msg) { + if ( BuildConfig.DEBUG) { + ToastUtils.make() +// .setBgColor(ColorUtils.getColor(R.color.toast_color)) + .setTextColor(Color.RED) +// .setGravity(Gravity.CENTER, 0, 0) + .setNotUseSystemToast() + .setDurationIsLong(true) + .show(msg); + } else { + Log.e(TAG, "debugShow: " + msg); + } + } + + private static Toast mToast = null; + + //android 8.0以后限制 + //https://www.jianshu.com/p/d9813ad03d59 + //https://www.jianshu.com/p/050ce052b873 + public static void showToast(Context context, String text, int duration) { + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P) { + Toast.makeText(context, text, duration).show(); + } else { + if (mToast == null) { + mToast = Toast.makeText(context, text, duration); + } else { + mToast.setText(text); + mToast.setDuration(duration); + } + mToast.show(); + } + } + + // public static void showInCenter(String msg) { +// mainHandler.post(() -> { +// if (toast != null) { +// toast.setGravity(Gravity.CENTER, 0, 0); +// toast.setText(msg); +// toast.show(); +// } +// }); +// } + + +} diff --git a/app/src/main/java/com/uiui/os/utils/Utils.java b/app/src/main/java/com/uiui/os/utils/Utils.java index 503c980..e9556fa 100644 --- a/app/src/main/java/com/uiui/os/utils/Utils.java +++ b/app/src/main/java/com/uiui/os/utils/Utils.java @@ -1,13 +1,66 @@ package com.uiui.os.utils; +import android.annotation.SuppressLint; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; import android.os.Build; +import android.os.Environment; +import android.util.Log; + +import androidx.core.content.ContextCompat; + +import com.uiui.os.BuildConfig; + +import java.io.File; +import java.io.FileInputStream; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.security.MessageDigest; public class Utils { + /** + * 获取设备序列号 + * + * @return + */ + @SuppressLint("MissingPermission") + public static String getSerial() { + String serial = "unknow"; + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {//9.0+ + serial = Build.getSerial(); + } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {//8.0+ + serial = Build.SERIAL; + } else {//8.0- + Class c = Class.forName("android.os.SystemProperties"); + Method get = c.getMethod("get", String.class); + serial = (String) get.invoke(c, "ro.serialno"); + } + } catch (Exception e) { + e.printStackTrace(); + Log.e("e", "读取设备序列号异常:" + e.toString()); + } + if (BuildConfig.DEBUG) { +// return "QNG2DKB00463"; + } + return serial; + } + + public static String getDeviceSN() { + String serial = null; + try { + Class c = Class.forName("android.os.SystemProperties"); + Method get = c.getMethod("get", String.class); + serial = (String) get.invoke(c, "persist.sys.hrSerial"); + } catch (Exception e) { + e.printStackTrace(); + } + return serial; + } + /** * 获取电量 * @@ -23,4 +76,14 @@ public class Utils { } } + public static String getDownLoadPath(Context context) { + String path = ContextCompat.getExternalFilesDirs(context, Environment.DIRECTORY_DOWNLOADS)[0].getAbsolutePath(); + return path + File.separator; + } + + public static String getFileNamefromURL(String url) { + int position = url.lastIndexOf("/"); + return url.substring(position + 1); + } + } diff --git a/app/src/main/java/com/uiui/os/view/CustomContent.java b/app/src/main/java/com/uiui/os/view/CustomContent.java index 90ed334..00eec53 100644 --- a/app/src/main/java/com/uiui/os/view/CustomContent.java +++ b/app/src/main/java/com/uiui/os/view/CustomContent.java @@ -66,6 +66,5 @@ public class CustomContent extends FrameLayout implements CustomContentCallbacks @Override public void onViewAdded(View child) { super.onViewAdded(child); - } } diff --git a/app/src/main/res/drawable-hdpi/actions_logo.png b/app/src/main/res/drawable-hdpi/actions_logo.png deleted file mode 100644 index dbbdff2bfd153ce70155dbf862625f725ef04eee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26101 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

-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 Z6_C!>k?eD6^>F1A08z-^qy|Dn#y{?H$TR=| delta 1340 zcmZ22xRYP8Gr-TCmrII^fq{Y7)59eQNCyD1J_j3+EJ^jL;x_yMTd#Mc32CF{I+wo7veO zB7q{u-#?r7ZIe&1x_q}f$+Q0P4rMdD8z48{ZwTlp#D$sIadYnLnNC?Z!!~5CY{n314`ZuOEDP6G5VN{gsEStGIU*{%Ify|Dcc?u729f=b9C?lbt zbooX3ekT2GJrOw?Z0A_KZ{6s3o24e*edU_Nj`rP~uXi*Qo5alUtEiWl()hm7@Uq&o z$Ju^Q&p16x5_VAAk?nHeR<3{ z7W0gjU%a@W-t)FKJD-I7L3z*U@Wo8KRCCYriEQX}o}A{lCfs5ElCw`87=8cy<_T_} zz{~0`)V{~+%31qIOs|CdFD;O*PY_sfynI&AiGFU0SrwaGjhY{C*P9b=7NuzTE~kVg z$9=Z`&3SM8CMCQ~u$sZNtZ-$i=p2TtVj=|%I^{Y1jG7<%o&;%5W|d~LVtc!tdkqWs zL3ZwGEvs%TD<&vaOH_*ciuZQO9{)-Q7*wYZH*7dx9=IR!sqf;2w z8s|;QUYh&!!OVcPE4i8vbSAIQ)QwpGc}7futMU!^GiOdP$G^U@UC<^m|EK!R;~sC` zh_AhMmxo`6v0P=IN7KbEHz?SP{=$DmdzvARd?z?lUoo1?( zy2yEitydNcx_)1>DdEaf?FfTUZ?qBVEqNjw4Q?_# z?ON6_g{5A+A=!pu>4$`nr9#Gwb2ph5t`~jAe3IK%OusUfJ176mwg5R@?PrZf7Jc#^ z+!9Tp&qOYH*!OcYNS!zre5&oO`J>=p3%fn;T9_Z6{QTJcvuV?cHmap;zPWY7+%;F< zaJEMXZX_vSIF75Z(tqj;~CH73NSVne*gr39>Ga%H zhXpsdT?$)VcqhNTWBb{RH{2p0mMvKu@_2{Q>~44N=+iBqE>uRiHG43YU%$FQ`iIVx z_L@_seY?+=_s^RyozomxvZ7Tub=CsiM~;gR_6YBOk?SjRV#?gh&h}qciTh^Q@qVnR zvNW7KIe5P7(a#$r>{~a@`{cU#+|}Ao|Cx9go=Dx9#&MkcFt9*m@O1TaS?83{1OTvq BKc@fy diff --git a/app/src/main/res/drawable-hdpi/voice.png b/app/src/main/res/drawable-hdpi/voice.png new file mode 100644 index 0000000000000000000000000000000000000000..0e8777f0ac756e7a09cc9c068ed163f7f7257fe0 GIT binary patch literal 3952 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⁣d7Sz4h(yoRms-}5zSOBtL^>HyTdTb_Dbj?WEjdAw1R zK3|FmrM2a!Nb=2!f@(6&s*q68n?dB^OZF{+s{9u`IZykmb)JR%(gc2ZqD{l z@zhR$rQ7Z!X+fx?gr9+c*--0z)Lvb0{9zn4B=*OVB#(5hTl}ld=U_Kgl{W$!Te2H2 zWuL!bQ+6xrfoWZlT+i|!_Fe#hX8iIS%ErJi%=R1 znkad?5;I3woAa}}k(0gDygBNhqowiQh5a@_J7HY+oLb`5>)r z8J;T#p-&UPBHe$#V)y8|$34%0lJ{o6nY84MqX>z$2|dyDk=$>zhtn`Pk9A}y%#(Pf zC5tetRtIoX+WZ)mTW(Lu>$`KQWw@DpIAf51n8%xK-v-$GZb(0GM`K>Ty5QL3e&GGo+iNCYd1@y`|sdw+g7u#US~N!LrPMs zv%CBi$ZYn6Tx=s5apc6)RLi%s&WY7xdt^=n5#+ zUpyH-&XT^!IjrM2{c!lO65w^;=0h|5a_Aei_Z+bD(4MLenyp&|JQz_9=eaiRJ3b-EK@Moh+ax&th0P=%Dhk>yaHMZ4k;4-B3H*t;oCl=Ec(k zZ}k+3#MJu9T@}6E`)D+)@gw(<;wQgtueSkCI=^o^Iy)enTm^uL=8txj2Ok@T3DDg&L1ve1u=n|5X-^uztqzijht8 z&FWpUK=;NPn;>^$H2_PKQuH3yy&)>DAE$EWfaDS!NmA3+uxQFH>=J$Iea&j|T#EooXh9wc|y|%GU;Af*hKl{lMU@Ee>~14qQ3A;-MA)R>)=y%wKxt#+p$)_a?g)2beiONpQZd&}v++{d@>#0UKmUC$k3FGTx z7S6ue`i5l9dzs6U<`*gSlAaaHyCj_6^6|BHF&)>|1}d0&kZa0~alH`0%OdS#na(=s zgcs4`@%b$!oufbs&?wgIWEG|WN40tJ-yqj@l{|GHt%4B@xtLvJB!e9J^+XGLrQ&6@ z-WQ=VtF&WSYvh#NT#LW(r%CmVb(?l(4Qz&QaO=uUNZDM@T+T2))Po{c7>_SbgSB;K zgC)`rM0=KA-&;WW!2_Y7@8=bGQs;iXjf}oxl>Tm9TL!6&?(4W7qWZ9Jo{;%ysga;e zi;^?Dv2EM;VSSv-X(rO()2@zV8Pam}DK78k_92^gL~K3zoo;Z{eR)^Nkk7zK@42qA z=!MU_NbSfk2WRgIE!*@Oc9vgR7+VPp%=${$=Kt|qn1j>dtziA*V?{B@v3v(0JrTkk UZh4jQ^k<`?o~dqywo~~30MgNujQ{`u literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/alarmclcok_background.xml b/app/src/main/res/drawable/alarmclcok_background.xml new file mode 100644 index 0000000..bf2ee59 --- /dev/null +++ b/app/src/main/res/drawable/alarmclcok_background.xml @@ -0,0 +1,20 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/notice_voice_background.xml b/app/src/main/res/drawable/notice_voice_background.xml new file mode 100644 index 0000000..0383881 --- /dev/null +++ b/app/src/main/res/drawable/notice_voice_background.xml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ok_background.xml b/app/src/main/res/drawable/ok_background.xml new file mode 100644 index 0000000..a246594 --- /dev/null +++ b/app/src/main/res/drawable/ok_background.xml @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/voice_background.xml b/app/src/main/res/drawable/voice_background.xml new file mode 100644 index 0000000..8243268 --- /dev/null +++ b/app/src/main/res/drawable/voice_background.xml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-land/fragment_custom.xml b/app/src/main/res/layout-land/fragment_custom.xml index f97bdf7..1628892 100644 --- a/app/src/main/res/layout-land/fragment_custom.xml +++ b/app/src/main/res/layout-land/fragment_custom.xml @@ -5,7 +5,6 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - @@ -120,38 +112,29 @@ android:layout_weight="2" android:background="@drawable/custom_background"> - - - + - - - + android:background="@drawable/custom_background" + tools:ignore="NestedWeights"> - - - - - + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/textView5" /> + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toBottomOf="@+id/iv_charging" /> + + + + - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout-port/fragment_custom.xml b/app/src/main/res/layout-port/fragment_custom.xml index 0f6e83c..584e3d0 100644 --- a/app/src/main/res/layout-port/fragment_custom.xml +++ b/app/src/main/res/layout-port/fragment_custom.xml @@ -89,22 +89,18 @@ android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" - android:text="我的便签" + android:text="爱心守护" android:textColor="@color/black" android:textSize="19sp" android:textStyle="bold" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - @@ -122,32 +118,28 @@ android:layout_weight="1" android:background="@drawable/custom_background"> - - + + - - - - - + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/textView5" /> @@ -245,33 +216,35 @@ + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toBottomOf="@+id/iv_charging" /> + + @@ -309,35 +294,40 @@ android:orientation="horizontal"> + + - - + diff --git a/app/src/main/res/layout/activity_notice.xml b/app/src/main/res/layout/activity_notice.xml new file mode 100644 index 0000000..4bd0e11 --- /dev/null +++ b/app/src/main/res/layout/activity_notice.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + +