├── app ├── .gitignore ├── src │ ├── main │ │ ├── assets │ │ │ ├── 0.bmp │ │ │ ├── 1.bmp │ │ │ ├── 2.bmp │ │ │ ├── 3.bmp │ │ │ ├── 4.bmp │ │ │ ├── 5.bmp │ │ │ ├── 6.bmp │ │ │ ├── 7.bmp │ │ │ ├── 8.bmp │ │ │ ├── 9.bmp │ │ │ ├── a.bmp │ │ │ ├── b.bmp │ │ │ ├── c.bmp │ │ │ ├── d.bmp │ │ │ ├── e.bmp │ │ │ ├── f.bmp │ │ │ ├── g.bmp │ │ │ ├── h.bmp │ │ │ ├── i.bmp │ │ │ ├── j.bmp │ │ │ ├── k.bmp │ │ │ ├── l.bmp │ │ │ ├── m.bmp │ │ │ ├── n.bmp │ │ │ ├── o.bmp │ │ │ ├── p.bmp │ │ │ ├── q.bmp │ │ │ ├── r.bmp │ │ │ ├── s.bmp │ │ │ ├── t.bmp │ │ │ ├── u.bmp │ │ │ ├── v.bmp │ │ │ ├── w.bmp │ │ │ ├── x.bmp │ │ │ ├── y.bmp │ │ │ └── z.bmp │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ ├── logo.png │ │ │ │ └── logo_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── logo.png │ │ │ │ └── logo_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── logo.png │ │ │ │ └── logo_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── logo.png │ │ │ │ └── logo_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── logo.png │ │ │ │ └── logo_round.png │ │ │ ├── drawable-nodpi │ │ │ │ ├── seekbar_thumb.png │ │ │ │ ├── side_nav_bar.webp │ │ │ │ ├── login_background.webp │ │ │ │ ├── notification_icon.png │ │ │ │ ├── today_widget_4_2.webp │ │ │ │ ├── splash_background.webp │ │ │ │ └── this_week_background.webp │ │ │ ├── values │ │ │ │ ├── attrs.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── styles.xml │ │ │ │ ├── strings.xml │ │ │ │ └── arrays.xml │ │ │ ├── xml │ │ │ │ ├── file_paths.xml │ │ │ │ ├── network_security_config.xml │ │ │ │ └── widget_provider_today_4_2.xml │ │ │ ├── layout │ │ │ │ ├── drawer_switch.xml │ │ │ │ ├── activity_custom.xml │ │ │ │ ├── fragment_check_score.xml │ │ │ │ ├── widget_layout_item.xml │ │ │ │ ├── fragment_check_score_show.xml │ │ │ │ ├── activity_splash.xml │ │ │ │ ├── activity_setting.xml │ │ │ │ ├── nav_header_main.xml │ │ │ │ ├── content_main.xml │ │ │ │ ├── widget_layout_today.xml │ │ │ │ ├── fragment_class_schedule.xml │ │ │ │ ├── app_bar_main.xml │ │ │ │ ├── activity_main.xml │ │ │ │ ├── preference_dialog_layout.xml │ │ │ │ ├── view_range.xml │ │ │ │ ├── activity_login.xml │ │ │ │ ├── activity_share.xml │ │ │ │ ├── item_score_rv.xml │ │ │ │ ├── fragment_this_week.xml │ │ │ │ ├── fragment_check_score_login.xml │ │ │ │ ├── widget_layout_item_draw.xml │ │ │ │ ├── item_class_rv.xml │ │ │ │ ├── fragment_today.xml │ │ │ │ └── activity_about.xml │ │ │ ├── menu │ │ │ │ ├── activity_custom_menu.xml │ │ │ │ ├── main.xml │ │ │ │ └── activity_main_drawer.xml │ │ │ └── drawable │ │ │ │ ├── baseline_done_24.xml │ │ │ │ ├── baseline_today_24.xml │ │ │ │ ├── baseline_brightness_4_24.xml │ │ │ │ ├── baseline_score_24.xml │ │ │ │ └── baseline_settings_applications_24.xml │ │ ├── java │ │ │ └── top │ │ │ │ └── itning │ │ │ │ └── yunshuclassschedule │ │ │ │ ├── util │ │ │ │ ├── Glide4Modle.kt │ │ │ │ ├── EventReceiver.kt │ │ │ │ ├── ImageHash.kt │ │ │ │ ├── Glide4Engine.kt │ │ │ │ ├── FileUtils.kt │ │ │ │ └── NetWorkUtils.kt │ │ │ │ ├── ui │ │ │ │ ├── widget │ │ │ │ │ ├── TodayRemoteViewsService.kt │ │ │ │ │ ├── TodayWidgetProvider.kt │ │ │ │ │ └── ToadyRemoteViewsFactory.kt │ │ │ │ ├── view │ │ │ │ │ └── RoundBackChange.kt │ │ │ │ ├── adapter │ │ │ │ │ ├── ScoreRecyclerViewAdapter.kt │ │ │ │ │ └── TodayRecyclerViewAdapter.kt │ │ │ │ ├── fragment │ │ │ │ │ ├── checkscore │ │ │ │ │ │ └── CheckScoreShowFragment.kt │ │ │ │ │ ├── CheckScoreFragment.kt │ │ │ │ │ ├── ClassScheduleFragment.kt │ │ │ │ │ ├── ThisWeekFragment.kt │ │ │ │ │ └── setting │ │ │ │ │ │ └── SettingsFragment.kt │ │ │ │ └── activity │ │ │ │ │ ├── AboutActivity.kt │ │ │ │ │ ├── SettingActivity.kt │ │ │ │ │ ├── SplashActivity.kt │ │ │ │ │ └── LoginActivity.kt │ │ │ │ ├── entity │ │ │ │ ├── EventEntity.kt │ │ │ │ ├── Hash.java │ │ │ │ ├── DataEntity.java │ │ │ │ ├── Score.java │ │ │ │ └── ClassSchedule.java │ │ │ │ ├── receiver │ │ │ │ ├── TimeTickReceiver.kt │ │ │ │ └── RemindReceiver.kt │ │ │ │ ├── common │ │ │ │ ├── BaseActivity.kt │ │ │ │ ├── App.kt │ │ │ │ └── ConstantPool.kt │ │ │ │ └── service │ │ │ │ ├── JobSchedulerService.kt │ │ │ │ ├── TodayWidgetService.kt │ │ │ │ └── CommonService.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── top │ │ │ └── itning │ │ │ └── yunshuclassschedule │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── top │ │ └── itning │ │ └── yunshuclassschedule │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── .gitattributes ├── extra ├── a.png ├── b.png ├── c.png ├── d.png ├── e.png ├── f.png ├── g.png └── coolapk.png ├── keystore.jks ├── .gitignore ├── gradle.properties ├── gradlew.bat ├── README.md └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.java linguist-language=kotlin -------------------------------------------------------------------------------- /extra/a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/extra/a.png -------------------------------------------------------------------------------- /extra/b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/extra/b.png -------------------------------------------------------------------------------- /extra/c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/extra/c.png -------------------------------------------------------------------------------- /extra/d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/extra/d.png -------------------------------------------------------------------------------- /extra/e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/extra/e.png -------------------------------------------------------------------------------- /extra/f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/extra/f.png -------------------------------------------------------------------------------- /extra/g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/extra/g.png -------------------------------------------------------------------------------- /keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/keystore.jks -------------------------------------------------------------------------------- /extra/coolapk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/extra/coolapk.png -------------------------------------------------------------------------------- /app/src/main/assets/0.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/0.bmp -------------------------------------------------------------------------------- /app/src/main/assets/1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/1.bmp -------------------------------------------------------------------------------- /app/src/main/assets/2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/2.bmp -------------------------------------------------------------------------------- /app/src/main/assets/3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/3.bmp -------------------------------------------------------------------------------- /app/src/main/assets/4.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/4.bmp -------------------------------------------------------------------------------- /app/src/main/assets/5.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/5.bmp -------------------------------------------------------------------------------- /app/src/main/assets/6.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/6.bmp -------------------------------------------------------------------------------- /app/src/main/assets/7.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/7.bmp -------------------------------------------------------------------------------- /app/src/main/assets/8.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/8.bmp -------------------------------------------------------------------------------- /app/src/main/assets/9.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/9.bmp -------------------------------------------------------------------------------- /app/src/main/assets/a.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/a.bmp -------------------------------------------------------------------------------- /app/src/main/assets/b.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/b.bmp -------------------------------------------------------------------------------- /app/src/main/assets/c.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/c.bmp -------------------------------------------------------------------------------- /app/src/main/assets/d.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/d.bmp -------------------------------------------------------------------------------- /app/src/main/assets/e.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/e.bmp -------------------------------------------------------------------------------- /app/src/main/assets/f.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/f.bmp -------------------------------------------------------------------------------- /app/src/main/assets/g.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/g.bmp -------------------------------------------------------------------------------- /app/src/main/assets/h.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/h.bmp -------------------------------------------------------------------------------- /app/src/main/assets/i.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/i.bmp -------------------------------------------------------------------------------- /app/src/main/assets/j.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/j.bmp -------------------------------------------------------------------------------- /app/src/main/assets/k.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/k.bmp -------------------------------------------------------------------------------- /app/src/main/assets/l.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/l.bmp -------------------------------------------------------------------------------- /app/src/main/assets/m.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/m.bmp -------------------------------------------------------------------------------- /app/src/main/assets/n.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/n.bmp -------------------------------------------------------------------------------- /app/src/main/assets/o.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/o.bmp -------------------------------------------------------------------------------- /app/src/main/assets/p.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/p.bmp -------------------------------------------------------------------------------- /app/src/main/assets/q.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/q.bmp -------------------------------------------------------------------------------- /app/src/main/assets/r.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/r.bmp -------------------------------------------------------------------------------- /app/src/main/assets/s.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/s.bmp -------------------------------------------------------------------------------- /app/src/main/assets/t.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/t.bmp -------------------------------------------------------------------------------- /app/src/main/assets/u.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/u.bmp -------------------------------------------------------------------------------- /app/src/main/assets/v.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/v.bmp -------------------------------------------------------------------------------- /app/src/main/assets/w.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/w.bmp -------------------------------------------------------------------------------- /app/src/main/assets/x.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/x.bmp -------------------------------------------------------------------------------- /app/src/main/assets/y.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/y.bmp -------------------------------------------------------------------------------- /app/src/main/assets/z.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/assets/z.bmp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/mipmap-hdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/mipmap-mdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/mipmap-xhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/mipmap-xxhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/mipmap-xxxhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/logo_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/mipmap-hdpi/logo_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/logo_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/mipmap-mdpi/logo_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/logo_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/mipmap-xhdpi/logo_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/logo_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/mipmap-xxhdpi/logo_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/logo_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/mipmap-xxxhdpi/logo_round.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/seekbar_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/drawable-nodpi/seekbar_thumb.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/side_nav_bar.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/drawable-nodpi/side_nav_bar.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/login_background.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/drawable-nodpi/login_background.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/drawable-nodpi/notification_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/today_widget_4_2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/drawable-nodpi/today_widget_4_2.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/splash_background.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/drawable-nodpi/splash_background.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/this_week_background.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itning/YunShuClassSchedule/dev/app/src/main/res/drawable-nodpi/this_week_background.webp -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /gradle 4 | /.idea 5 | /local.properties 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | /app/bugly 11 | /app/release 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/xml/file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/drawer_switch.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 176dp 6 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/util/Glide4Modle.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.util 2 | 3 | import com.bumptech.glide.annotation.GlideModule 4 | import com.bumptech.glide.module.AppGlideModule 5 | 6 | /** 7 | * @author itning 8 | */ 9 | @GlideModule 10 | class Glide4Modle : AppGlideModule() 11 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/util/EventReceiver.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.util 2 | 3 | /** 4 | * 事件接收 5 | * 6 | * @author itning 7 | */ 8 | interface EventReceiver { 9 | /** 10 | * 触发事件 11 | * 12 | * @return 已消费返回真 13 | */ 14 | fun eventTrigger(): Boolean 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/xml/widget_provider_today_4_2.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/menu/activity_custom_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_done_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/test/java/top/itning/yunshuclassschedule/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * @see [Testing documentation](http://d.android.com/tools/testing) 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, (2 + 2).toLong()) 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_custom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_check_score.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/widget_layout_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/ui/widget/TodayRemoteViewsService.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.ui.widget 2 | 3 | import android.content.Intent 4 | import android.util.Log 5 | import android.widget.RemoteViewsService 6 | 7 | class TodayRemoteViewsService : RemoteViewsService() { 8 | override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory { 9 | Log.d("TodayRemoteViewsService", "onGetViewFactory") 10 | return ToadyRemoteViewsFactory(this, intent) 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_check_score_show.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_today_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_brightness_4_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_score_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/nav_header_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/entity/EventEntity.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.entity 2 | 3 | import top.itning.yunshuclassschedule.common.ConstantPool 4 | 5 | /** 6 | * 事件实体 7 | * 8 | * @author itning 9 | */ 10 | class EventEntity { 11 | var id: ConstantPool.Int? = null 12 | var msg: String? = null 13 | var data: Any? = null 14 | 15 | constructor(id: ConstantPool.Int) { 16 | this.id = id 17 | } 18 | 19 | constructor(id: ConstantPool.Int, msg: String) { 20 | this.id = id 21 | this.msg = msg 22 | } 23 | 24 | constructor(id: ConstantPool.Int, msg: String, data: Any) { 25 | this.id = id 26 | this.msg = msg 27 | this.data = data 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | android.useAndroidX=true 15 | android.enableJetifier=true 16 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/receiver/TimeTickReceiver.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.receiver 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.util.Log 7 | 8 | import org.greenrobot.eventbus.EventBus 9 | 10 | import top.itning.yunshuclassschedule.common.ConstantPool 11 | import top.itning.yunshuclassschedule.entity.EventEntity 12 | 13 | /** 14 | * 时间改变广播,包括用户手动设置时间,时区 15 | * 16 | * @author itning 17 | */ 18 | class TimeTickReceiver : BroadcastReceiver() { 19 | 20 | override fun onReceive(context: Context, intent: Intent) { 21 | Log.d(TAG, "send time change event") 22 | EventBus.getDefault().post(EVENT_ENTITY) 23 | } 24 | 25 | companion object { 26 | private const val TAG = "TimeTickReceiver" 27 | private val EVENT_ENTITY = EventEntity(ConstantPool.Int.TIME_TICK_CHANGE) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/widget_layout_today.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | #ff212121 8 | #ff000000 9 | #ff80cbc4 10 | 11 | #f1ff4949 12 | #f0ff6e6e 13 | #f0c992ff 14 | #f0c88f6e 15 | #ef6e73ff 16 | #f0ffaf49 17 | #edffb7b7 18 | 19 | #b0bbfa 20 | #f53e3e3e 21 | 22 | #85C1D8 23 | 24 | -------------------------------------------------------------------------------- /app/src/androidTest/java/top/itning/yunshuclassschedule/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule 2 | 3 | import android.content.Intent 4 | import androidx.test.InstrumentationRegistry 5 | import androidx.test.runner.AndroidJUnit4 6 | 7 | import org.junit.Assert.assertEquals 8 | import org.junit.Test 9 | import org.junit.runner.RunWith 10 | import top.itning.yunshuclassschedule.service.CourseInfoService 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see [Testing documentation](http://d.android.com/tools/testing) 16 | */ 17 | @RunWith(AndroidJUnit4::class) 18 | class ExampleInstrumentedTest { 19 | @Test 20 | fun useAppContext() { 21 | // Context of the app under test. 22 | val appContext = InstrumentationRegistry.getTargetContext() 23 | appContext.startService(Intent(appContext, CourseInfoService::class.java)) 24 | 25 | assertEquals("top.itning.yunshuclassschedule", appContext.packageName) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/entity/Hash.java: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.entity; 2 | 3 | import org.greenrobot.greendao.annotation.Entity; 4 | import org.greenrobot.greendao.annotation.Generated; 5 | import org.greenrobot.greendao.annotation.Id; 6 | import org.greenrobot.greendao.annotation.NotNull; 7 | 8 | /** 9 | * 图片哈希 10 | * 11 | * @author itning 12 | */ 13 | @Entity 14 | public class Hash { 15 | @Id 16 | private String id; 17 | @NotNull 18 | private String name; 19 | 20 | @Generated(hash = 1070788195) 21 | public Hash(String id, @NotNull String name) { 22 | this.id = id; 23 | this.name = name; 24 | } 25 | 26 | @Generated(hash = 1112031932) 27 | public Hash() { 28 | } 29 | 30 | public String getId() { 31 | return id; 32 | } 33 | 34 | public void setId(String id) { 35 | this.id = id; 36 | } 37 | 38 | public String getName() { 39 | return name; 40 | } 41 | 42 | public void setName(String name) { 43 | this.name = name; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_class_schedule.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/entity/DataEntity.java: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.entity; 2 | 3 | import java.util.List; 4 | 5 | import top.itning.yunshuclassschedule.common.App; 6 | import top.itning.yunshuclassschedule.util.DateUtils; 7 | 8 | /** 9 | * 数据封装实体 10 | * 11 | * @author itning 12 | */ 13 | public class DataEntity { 14 | private final List classScheduleList; 15 | private final List timeList; 16 | 17 | public DataEntity(App app) { 18 | classScheduleList = app.getDaoSession().getClassScheduleDao().loadAll(); 19 | timeList = DateUtils.INSTANCE.getTimeList(); 20 | } 21 | 22 | public List getClassScheduleList() { 23 | return classScheduleList; 24 | } 25 | 26 | public List getTimeList() { 27 | return timeList; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return "DataEntity{" + 33 | "classScheduleList=" + classScheduleList + 34 | ", timeList=" + timeList + 35 | '}'; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/common/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.common 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | 6 | import androidx.annotation.Nullable 7 | import androidx.appcompat.app.AppCompatActivity 8 | import top.itning.yunshuclassschedule.entity.EventEntity 9 | import top.itning.yunshuclassschedule.service.CommonService 10 | import top.itning.yunshuclassschedule.service.RemindService 11 | import top.itning.yunshuclassschedule.service.TodayWidgetService 12 | 13 | /** 14 | * Base App Activity 15 | * 16 | * @author itning 17 | */ 18 | abstract class BaseActivity : AppCompatActivity() { 19 | 20 | override fun onCreate(@Nullable savedInstanceState: Bundle?) { 21 | super.onCreate(savedInstanceState) 22 | startService(Intent(this, CommonService::class.java)) 23 | startService(Intent(this, RemindService::class.java)) 24 | startService(Intent(this, TodayWidgetService::class.java)) 25 | } 26 | 27 | /** 28 | * 消息事件 29 | * 30 | * @param eventEntity what 31 | */ 32 | abstract fun onMessageEvent(eventEntity: EventEntity) 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/app_bar_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | 14 | 19 | 24 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 20 | 21 | 22 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/menu/activity_main_drawer.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 12 | 16 | 17 | 18 | 19 | 20 | 24 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/util/ImageHash.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.util 2 | 3 | import android.graphics.Bitmap 4 | import android.graphics.Matrix 5 | 6 | /** 7 | * 图片指纹工具类 8 | * 9 | * @author itning 10 | */ 11 | object ImageHash { 12 | /** 13 | * 计算指纹 14 | * 15 | * @param fromBitmap [Bitmap] 16 | * @return 指纹数据 17 | */ 18 | fun calculateFingerPrint(fromBitmap: Bitmap): String { 19 | val stringBuilder = StringBuilder() 20 | val width = fromBitmap.width 21 | val height = fromBitmap.height 22 | // 设置想要的大小 23 | val newWidth = 8 24 | val newHeight = 8 25 | // 计算缩放比例 26 | val scaleWidth = newWidth.toFloat() / width 27 | val scaleHeight = newHeight.toFloat() / height 28 | // 取得想要缩放的matrix参数 29 | val matrix = Matrix() 30 | matrix.postScale(scaleWidth, scaleHeight) 31 | // 得到新的图片 32 | val newbm = Bitmap.createBitmap(fromBitmap, 0, 0, width, height, matrix, true) 33 | for (i in 0 until newbm.width) { 34 | for (j in 0 until newbm.height) { 35 | val pixel = newbm.getPixel(i, j) 36 | stringBuilder.append(Math.abs(pixel)) 37 | } 38 | } 39 | newbm.recycle() 40 | fromBitmap.recycle() 41 | return stringBuilder.toString() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_settings_applications_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/preference_dialog_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 22 | 23 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_range.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 34 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/ui/view/RoundBackChange.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.ui.view 2 | 3 | import android.content.Context 4 | import android.graphics.Canvas 5 | import android.graphics.Color 6 | import android.graphics.Paint 7 | import android.util.AttributeSet 8 | import android.view.View 9 | import androidx.annotation.Nullable 10 | import top.itning.yunshuclassschedule.R 11 | 12 | /** 13 | * @author itning 14 | */ 15 | class RoundBackChange : View { 16 | private var color = -0x22000001 17 | private val mPaint = Paint() 18 | 19 | constructor(context: Context) : super(context, null) 20 | 21 | constructor(context: Context, @Nullable attrs: AttributeSet) : super(context, attrs) { 22 | //设置画笔宽度为10px 23 | val array = context.obtainStyledAttributes(attrs, R.styleable.RoundBackChange) 24 | color = array.getColor(R.styleable.RoundBackChange_self_color, color) 25 | array.recycle() 26 | //设置画笔颜色 27 | mPaint.color = color 28 | //设置画笔模式为填充 29 | mPaint.style = Paint.Style.FILL_AND_STROKE 30 | mPaint.strokeWidth = 10f 31 | mPaint.isAntiAlias = true 32 | } 33 | 34 | fun setBackColor(color: Int) { 35 | this.color = color 36 | mPaint.color = color 37 | } 38 | 39 | override fun onDraw(canvas: Canvas) { 40 | super.onDraw(canvas) 41 | canvas.drawColor(Color.TRANSPARENT) 42 | canvas.drawCircle((right - left - measuredWidth / 2).toFloat(), (top + measuredHeight / 2).toFloat(), (measuredWidth / 3).toFloat(), mPaint) 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/ui/widget/TodayWidgetProvider.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.ui.widget 2 | 3 | import android.appwidget.AppWidgetManager 4 | import android.appwidget.AppWidgetProvider 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.net.Uri 8 | import android.util.Log 9 | import android.widget.RemoteViews 10 | 11 | /** 12 | * 今天课程小部件 13 | * 14 | * @author itning 15 | */ 16 | class TodayWidgetProvider : AppWidgetProvider() { 17 | override fun onUpdate(context: Context?, appWidgetManager: AppWidgetManager?, appWidgetIds: IntArray?) { 18 | Log.d(TAG, "onUpdate") 19 | appWidgetIds?.forEach { 20 | val remoteViews = RemoteViews(context?.packageName, top.itning.yunshuclassschedule.R.layout.widget_layout_today) 21 | val intent = Intent(context, TodayRemoteViewsService::class.java) 22 | intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, it) 23 | intent.data = Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)) 24 | remoteViews.setRemoteAdapter(top.itning.yunshuclassschedule.R.id.lv, intent) 25 | remoteViews.setEmptyView(top.itning.yunshuclassschedule.R.id.lv, top.itning.yunshuclassschedule.R.id.empty_view) 26 | appWidgetManager?.updateAppWidget(it, remoteViews) 27 | } 28 | super.onUpdate(context, appWidgetManager, appWidgetIds) 29 | } 30 | 31 | override fun onDisabled(context: Context?) { 32 | Log.d(TAG, "onDisabled") 33 | super.onDisabled(context) 34 | } 35 | 36 | companion object { 37 | private const val TAG = "TodayWidgetProvider" 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/service/JobSchedulerService.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.service 2 | 3 | import android.app.job.JobParameters 4 | import android.app.job.JobService 5 | import android.content.Intent 6 | import android.os.Build 7 | import android.preference.PreferenceManager 8 | import android.util.Log 9 | import top.itning.yunshuclassschedule.ui.fragment.setting.SettingsFragment.Companion.FOREGROUND_SERVICE_STATUS 10 | 11 | /** 12 | * @author itning 13 | */ 14 | class JobSchedulerService : JobService() { 15 | 16 | override fun onStartJob(params: JobParameters): Boolean { 17 | Log.d(TAG, "onStartJob(): params = [$params]") 18 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 19 | if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(FOREGROUND_SERVICE_STATUS, true)) { 20 | startForegroundService(Intent(this, CommonService::class.java)) 21 | startForegroundService(Intent(this, RemindService::class.java)) 22 | startForegroundService(Intent(this, TodayWidgetService::class.java)) 23 | } 24 | } else { 25 | startService(Intent(this, CommonService::class.java)) 26 | startService(Intent(this, RemindService::class.java)) 27 | startService(Intent(this, TodayWidgetService::class.java)) 28 | } 29 | jobFinished(params, false) 30 | return true 31 | } 32 | 33 | override fun onStopJob(params: JobParameters): Boolean { 34 | Log.d(TAG, "onStopJob(): params = [$params]") 35 | return false 36 | } 37 | 38 | companion object { 39 | private const val TAG = "JobSchedulerService" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/ui/adapter/ScoreRecyclerViewAdapter.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.ui.adapter 2 | 3 | 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | 8 | import androidx.annotation.NonNull 9 | import androidx.appcompat.widget.AppCompatTextView 10 | import androidx.recyclerview.widget.RecyclerView 11 | import top.itning.yunshuclassschedule.R 12 | import top.itning.yunshuclassschedule.entity.Score 13 | 14 | /** 15 | * @author itning 16 | */ 17 | class ScoreRecyclerViewAdapter(@param:NonNull private val scoreList: List) : RecyclerView.Adapter() { 18 | 19 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 20 | return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_score_rv, parent, false)) 21 | } 22 | 23 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 24 | val score = scoreList[position] 25 | holder.tvId.text = score.id 26 | holder.tvName.text = score.name 27 | holder.tvSemester.text = score.semester 28 | holder.tvGrade.text = score.grade 29 | holder.tvCredit.text = score.credit 30 | } 31 | 32 | override fun getItemCount(): Int { 33 | return scoreList.size 34 | } 35 | 36 | class ViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) { 37 | val tvId: AppCompatTextView = itemView.findViewById(R.id.tv_id) 38 | 39 | val tvName: AppCompatTextView = itemView.findViewById(R.id.tv_name) 40 | 41 | val tvSemester: AppCompatTextView = itemView.findViewById(R.id.tv_semester) 42 | 43 | val tvGrade: AppCompatTextView = itemView.findViewById(R.id.tv_grade) 44 | 45 | val tvCredit: AppCompatTextView = itemView.findViewById(R.id.tv_credit) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 16 | 17 | 21 | 22 | 27 | 28 | 31 | 32 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 20 | 21 | 32 | 33 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/ui/fragment/checkscore/CheckScoreShowFragment.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.ui.fragment.checkscore 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.util.Log 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.view.inputmethod.InputMethodManager 10 | import androidx.fragment.app.Fragment 11 | import androidx.recyclerview.widget.LinearLayoutManager 12 | import kotlinx.android.synthetic.main.fragment_check_score_show.* 13 | import top.itning.yunshuclassschedule.R 14 | import top.itning.yunshuclassschedule.entity.Score 15 | import top.itning.yunshuclassschedule.ui.adapter.ScoreRecyclerViewAdapter 16 | 17 | /** 18 | * 展示 19 | * 20 | * @author itning 21 | */ 22 | class CheckScoreShowFragment : Fragment() { 23 | override fun onCreate(savedInstanceState: Bundle?) { 24 | super.onCreate(savedInstanceState) 25 | val inputMethodManager = requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager 26 | inputMethodManager.hideSoftInputFromWindow(requireActivity().window.decorView.windowToken, 0) 27 | } 28 | 29 | override fun onDestroy() { 30 | Log.d(TAG, "on Destroy") 31 | super.onDestroy() 32 | } 33 | 34 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 35 | return inflater.inflate(R.layout.fragment_check_score_show, container, false) 36 | } 37 | 38 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 39 | arguments?.getParcelableArrayList("scoreList")?.let { 40 | //RecyclerView初始化 41 | val layout = LinearLayoutManager(requireContext()) 42 | //列表再底部开始展示,反转后由上面开始展示 43 | layout.stackFromEnd = true 44 | //列表翻转 45 | layout.reverseLayout = true 46 | rv.layoutManager = layout 47 | rv.adapter = ScoreRecyclerViewAdapter(it) 48 | } 49 | } 50 | 51 | companion object { 52 | private const val TAG = "CheckScoreShowFragment" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_share.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 15 | 16 | 24 | 25 | 29 | 30 | 38 | 39 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/common/App.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.common 2 | 3 | import android.app.Application 4 | import android.content.ComponentCallbacks2 5 | import android.content.Context 6 | import android.content.SharedPreferences 7 | import android.content.res.Configuration 8 | import android.util.Log 9 | import com.tencent.bugly.crashreport.CrashReport 10 | import org.greenrobot.eventbus.EventBus 11 | import top.itning.yunshuclassschedule.AppActivityIndex 12 | import top.itning.yunshuclassschedule.entity.DaoMaster 13 | import top.itning.yunshuclassschedule.entity.DaoSession 14 | import top.itning.yunshuclassschedule.util.GlideApp 15 | 16 | /** 17 | * 应用基类 18 | * 19 | * @author itning 20 | */ 21 | class App : Application() { 22 | 23 | lateinit var daoSession: DaoSession 24 | 25 | override fun onCreate() { 26 | // 程序创建的时候执行 27 | //EventBus add Index 28 | EventBus.builder().addIndex(AppActivityIndex()).installDefaultEventBus() 29 | //bugly 30 | CrashReport.initCrashReport(applicationContext, "439037c8de", false) 31 | 32 | val helper = DaoMaster.DevOpenHelper(this, ConstantPool.Str.DB_NAME.get()) 33 | val db = helper.writableDb 34 | daoSession = DaoMaster(db).newSession() 35 | sharedPreferences = getSharedPreferences(ConstantPool.Str.SHARED_PREFERENCES_FILENAME.get(), Context.MODE_PRIVATE) 36 | super.onCreate() 37 | } 38 | 39 | override fun onTerminate() { 40 | // 程序终止的时候执行 41 | Log.d(TAG, "onTerminate") 42 | super.onTerminate() 43 | } 44 | 45 | override fun onLowMemory() { 46 | // 低内存的时候执行 47 | Log.d(TAG, "onLowMemory") 48 | GlideApp.get(this).clearMemory() 49 | super.onLowMemory() 50 | } 51 | 52 | override fun onTrimMemory(level: Int) { 53 | // 程序在内存清理的时候执行 54 | Log.d(TAG, "onTrimMemory:$level") 55 | if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { 56 | GlideApp.get(this).clearMemory() 57 | } 58 | GlideApp.get(this).trimMemory(level) 59 | super.onTrimMemory(level) 60 | } 61 | 62 | override fun onConfigurationChanged(newConfig: Configuration) { 63 | Log.d(TAG, "onConfigurationChanged") 64 | super.onConfigurationChanged(newConfig) 65 | } 66 | 67 | companion object { 68 | private const val TAG = "App" 69 | lateinit var sharedPreferences: SharedPreferences 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_score_rv.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 23 | 24 | 30 | 31 | 37 | 38 | 39 | 46 | 47 | 55 | 56 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/util/Glide4Engine.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.util 2 | 3 | import android.content.Context 4 | import android.graphics.drawable.Drawable 5 | import android.net.Uri 6 | import android.widget.ImageView 7 | 8 | import com.bumptech.glide.Priority 9 | import com.bumptech.glide.request.RequestOptions 10 | import com.zhihu.matisse.engine.ImageEngine 11 | 12 | /** 13 | * https://github.com/zhihu/Matisse/blob/master/sample/src/main/java/com/zhihu/matisse/sample/Glide4Engine.java 14 | * [ImageEngine] implementation using Glide. 15 | * 16 | * @author zhihu 17 | */ 18 | 19 | class Glide4Engine : ImageEngine { 20 | 21 | override fun loadThumbnail(context: Context, resize: Int, placeholder: Drawable, imageView: ImageView, uri: Uri) { 22 | GlideApp.with(context) 23 | .asBitmap() // some .jpeg files are actually gif 24 | .load(uri) 25 | .apply(RequestOptions() 26 | .override(resize, resize) 27 | .placeholder(placeholder) 28 | .centerCrop()) 29 | .into(imageView) 30 | } 31 | 32 | override fun loadGifThumbnail(context: Context, resize: Int, placeholder: Drawable, imageView: ImageView, 33 | uri: Uri) { 34 | GlideApp.with(context) 35 | .asBitmap() // some .jpeg files are actually gif 36 | .load(uri) 37 | .apply(RequestOptions() 38 | .override(resize, resize) 39 | .placeholder(placeholder) 40 | .centerCrop()) 41 | .into(imageView) 42 | } 43 | 44 | override fun loadImage(context: Context, resizeX: Int, resizeY: Int, imageView: ImageView, uri: Uri) { 45 | GlideApp.with(context) 46 | .load(uri) 47 | .apply(RequestOptions() 48 | .override(resizeX, resizeY) 49 | .priority(Priority.HIGH) 50 | .fitCenter()) 51 | .into(imageView) 52 | } 53 | 54 | override fun loadGifImage(context: Context, resizeX: Int, resizeY: Int, imageView: ImageView, uri: Uri) { 55 | GlideApp.with(context) 56 | .asGif() 57 | .load(uri) 58 | .apply(RequestOptions() 59 | .override(resizeX, resizeY) 60 | .priority(Priority.HIGH) 61 | .fitCenter()) 62 | .into(imageView) 63 | } 64 | 65 | override fun supportAnimatedGif(): Boolean { 66 | return true 67 | } 68 | } -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/receiver/RemindReceiver.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.receiver 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.util.Log 7 | 8 | import org.greenrobot.eventbus.EventBus 9 | 10 | import top.itning.yunshuclassschedule.common.ConstantPool 11 | import top.itning.yunshuclassschedule.entity.ClassSchedule 12 | import top.itning.yunshuclassschedule.entity.EventEntity 13 | 14 | /** 15 | * 提醒广播 16 | * 17 | * @author itning 18 | */ 19 | class RemindReceiver : BroadcastReceiver() { 20 | 21 | override fun onReceive(context: Context, intent: Intent) { 22 | Log.d(TAG, "on Receive") 23 | val type = intent.getStringExtra("type") 24 | val name = intent.getStringExtra("name") 25 | val location = intent.getStringExtra("location") 26 | val section = intent.getIntExtra("section", -1) 27 | val status = intent.getIntExtra("status", -1) 28 | val week = intent.getIntExtra("week", -1) 29 | if (section == -1 || status == -1 || week == -1) { 30 | Log.e(TAG, "section or status , week error ! section:$section status:$status week:$week") 31 | return 32 | } 33 | if (type == null || name == null || location == null) { 34 | Log.e(TAG, "null value ! type:$type name:$name location:$location") 35 | return 36 | } 37 | Log.d(TAG, "get data: type->$type name->$name location->$location section->$section status->$status week->$week") 38 | if (PHONE_MUTE == type) { 39 | if (status == 0) { 40 | Log.d(TAG, "PHONE_MUTE_OPEN") 41 | EventBus.getDefault().post(EventEntity(ConstantPool.Int.PHONE_MUTE_OPEN)) 42 | } else { 43 | Log.d(TAG, "PHONE_MUTE_CANCEL") 44 | EventBus.getDefault().post(EventEntity(ConstantPool.Int.PHONE_MUTE_CANCEL)) 45 | } 46 | } 47 | val classSchedule = ClassSchedule() 48 | classSchedule.section = section 49 | classSchedule.name = name 50 | classSchedule.location = location 51 | if (CLASS_REMINDER_UP == type) { 52 | Log.d(TAG, "CLASS_UP_REMIND") 53 | EventBus.getDefault().post(EventEntity(ConstantPool.Int.CLASS_UP_REMIND, "", classSchedule)) 54 | } 55 | if (CLASS_REMINDER_DOWN == type) { 56 | Log.d(TAG, "CLASS_DOWN_REMIND") 57 | EventBus.getDefault().post(EventEntity(ConstantPool.Int.CLASS_DOWN_REMIND, "", classSchedule)) 58 | } 59 | } 60 | 61 | companion object { 62 | private const val TAG = "RemindReceiver" 63 | private const val PHONE_MUTE = "phone_mute" 64 | private const val CLASS_REMINDER_UP = "class_reminder_up" 65 | private const val CLASS_REMINDER_DOWN = "class_reminder_down" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/entity/Score.java: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.entity; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | /** 7 | * 分数实体 8 | * 9 | * @author itning 10 | */ 11 | public class Score implements Parcelable { 12 | /** 13 | * 序号 14 | */ 15 | private String id; 16 | /** 17 | * 学期 18 | */ 19 | private String semester; 20 | /** 21 | * 科目名称 22 | */ 23 | private String name; 24 | /** 25 | * 成绩 26 | */ 27 | private String grade; 28 | /** 29 | * 学分 30 | */ 31 | private String credit; 32 | 33 | public String getId() { 34 | return id; 35 | } 36 | 37 | public void setId(String id) { 38 | this.id = id; 39 | } 40 | 41 | public String getSemester() { 42 | return semester; 43 | } 44 | 45 | public void setSemester(String semester) { 46 | this.semester = semester; 47 | } 48 | 49 | public String getName() { 50 | return name; 51 | } 52 | 53 | public void setName(String name) { 54 | this.name = name; 55 | } 56 | 57 | public String getGrade() { 58 | return grade; 59 | } 60 | 61 | public void setGrade(String grade) { 62 | this.grade = grade; 63 | } 64 | 65 | public String getCredit() { 66 | return credit; 67 | } 68 | 69 | public void setCredit(String credit) { 70 | this.credit = credit; 71 | } 72 | 73 | @Override 74 | public String toString() { 75 | return "Score{" + 76 | "id='" + id + '\'' + 77 | ", semester='" + semester + '\'' + 78 | ", name='" + name + '\'' + 79 | ", grade='" + grade + '\'' + 80 | ", credit='" + credit + '\'' + 81 | '}'; 82 | } 83 | 84 | @Override 85 | public int describeContents() { 86 | return 0; 87 | } 88 | 89 | @Override 90 | public void writeToParcel(Parcel dest, int flags) { 91 | dest.writeString(id); 92 | dest.writeString(semester); 93 | dest.writeString(name); 94 | dest.writeString(grade); 95 | dest.writeString(credit); 96 | } 97 | 98 | public static final Creator CREATOR = new Creator() { 99 | @Override 100 | public Score createFromParcel(Parcel in) { 101 | Score score = new Score(); 102 | score.id = in.readString(); 103 | score.semester = in.readString(); 104 | score.name = in.readString(); 105 | score.grade = in.readString(); 106 | score.credit = in.readString(); 107 | return score; 108 | } 109 | 110 | @Override 111 | public Score[] newArray(int size) { 112 | return new Score[size]; 113 | } 114 | }; 115 | } 116 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/util/FileUtils.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.util 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import android.util.Log 6 | import android.widget.Toast 7 | import androidx.annotation.CheckResult 8 | import androidx.annotation.NonNull 9 | import com.tencent.bugly.crashreport.CrashReport 10 | import java.io.File 11 | import java.io.FileInputStream 12 | import java.io.IOException 13 | import java.io.InputStream 14 | 15 | /** 16 | * 文件工具类 17 | * 18 | * @author itning 19 | */ 20 | object FileUtils { 21 | private const val TAG = "FileUtils" 22 | private const val MAX_IMAGE_FILE_SIZE = 20 23 | 24 | fun transferFile(@NonNull context: Context, @NonNull fromUri: Uri, @NonNull fileName: String) { 25 | if (!writeFile2Cache(context, fromUri)) { 26 | return 27 | } 28 | val file = File(context.cacheDir.toString() + File.separator + "cache") 29 | if (!file.canRead()) { 30 | Toast.makeText(context, "读取图片失败:", Toast.LENGTH_LONG).show() 31 | CrashReport.postCatchedException(Throwable("read image failure , file.canRead method return false")) 32 | return 33 | } 34 | // Get length of file in bytes 35 | val fileSizeInBytes = file.length() 36 | // Convert the bytes to Kilobytes (1 KB = 1024 Bytes) 37 | val fileSizeInKB = fileSizeInBytes / 1024 38 | // Convert the KB to MegaBytes (1 MB = 1024 KBytes) 39 | val fileSizeInMB = fileSizeInKB / 1024 40 | Log.d(TAG, "file size :" + fileSizeInKB + "KB") 41 | if (fileSizeInMB > MAX_IMAGE_FILE_SIZE) { 42 | Log.d(TAG, "this image too large :" + fileSizeInMB + "MB") 43 | Toast.makeText(context, "图片太大了", Toast.LENGTH_LONG).show() 44 | return 45 | } 46 | try { 47 | FileInputStream(file).channel.use { inChannel -> context.openFileOutput(fileName, Context.MODE_PRIVATE).channel.use { fileOutputStreamChannel -> fileOutputStreamChannel.transferFrom(inChannel, 0, inChannel.size()) } } 48 | } catch (e: IOException) { 49 | Log.e(TAG, " ", e) 50 | CrashReport.postCatchedException(e) 51 | Toast.makeText(context, "图片写入失败", Toast.LENGTH_LONG).show() 52 | } 53 | 54 | } 55 | 56 | @CheckResult 57 | private fun writeFile2Cache(@NonNull context: Context, @NonNull fromUri: Uri): Boolean { 58 | return try { 59 | context.contentResolver.openInputStream(fromUri)!!.toFile(context.cacheDir.toString() + File.separator + "cache") 60 | true 61 | } catch (e: Exception) { 62 | Log.e(TAG, " ", e) 63 | CrashReport.postCatchedException(e) 64 | Toast.makeText(context, "写入缓存失败:" + e.message, Toast.LENGTH_LONG).show() 65 | false 66 | } 67 | } 68 | 69 | private fun InputStream.toFile(path: String) { 70 | File(path).outputStream().use { this.copyTo(it) } 71 | } 72 | } 73 | 74 | 75 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | # 代码混淆压缩比,在0~7之间,默认为5,一般不做修改 23 | -optimizationpasses 5 24 | 25 | # 混合时不使用大小写混合,混合后的类名为小写 26 | -dontusemixedcaseclassnames 27 | 28 | # 指定不去忽略非公共库的类 29 | -dontskipnonpubliclibraryclasses 30 | 31 | # 这句话能够使我们的项目混淆后产生映射文件 32 | # 包含有类名->混淆后类名的映射关系 33 | -verbose 34 | 35 | # 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。 36 | -dontpreverify 37 | 38 | # 忽略警告,继续执行 39 | -ignorewarnings 40 | 41 | # 指定混淆是采用的算法,后面的参数是一个过滤器 42 | # 这个过滤器是谷歌推荐的算法,一般不做更改 43 | -optimizations !code/simplification/cast,!field/*,!class/merging/* 44 | 45 | -dontwarn javax.annotation.** 46 | -dontwarn javax.inject.** 47 | 48 | -keep class top.itning.yunshuclassschedule.entity.** { *; } 49 | 50 | # greenDAO 3 51 | -keepclassmembers class * extends org.greenrobot.greendao.AbstractDao { 52 | public static java.lang.String TABLENAME; 53 | } 54 | -keep class **$Properties 55 | -dontwarn org.greenrobot.greendao.rx.** 56 | 57 | #EventBus 58 | -keepattributes *Annotation* 59 | -keepclassmembers class ** { 60 | @org.greenrobot.eventbus.Subscribe ; 61 | } 62 | -keep enum org.greenrobot.eventbus.ThreadMode { *; } 63 | 64 | # Only required if you use AsyncExecutor 65 | -keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent { 66 | (java.lang.Throwable); 67 | } 68 | 69 | #Glide 70 | -keep public class * implements com.bumptech.glide.module.GlideModule 71 | -keep public class * extends com.bumptech.glide.AppGlideModule 72 | -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { 73 | **[] $VALUES; 74 | public *; 75 | } 76 | 77 | #Bugly 78 | -dontwarn com.tencent.bugly.** 79 | -keep public class com.tencent.bugly.**{*;} 80 | -keep class android.support.**{*;} 81 | 82 | # Retain service method parameters when optimizing. 83 | -keepclassmembers,allowshrinking,allowobfuscation interface * { 84 | @retrofit2.http.* ; 85 | } 86 | 87 | # Ignore annotation used for build tooling. 88 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 89 | 90 | # Ignore JSR 305 annotations for embedding nullability information. 91 | -dontwarn javax.annotation.** 92 | 93 | # Guarded by a NoClassDefFoundError try/catch and only used when on the classpath. 94 | -dontwarn kotlin.Unit 95 | 96 | # Matisse 97 | -dontwarn com.squareup.picasso.** -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/ui/activity/AboutActivity.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.ui.activity 2 | 3 | import android.content.Intent 4 | import android.content.pm.PackageManager 5 | import android.net.Uri 6 | import android.os.Bundle 7 | import android.util.Log 8 | import android.view.MenuItem 9 | import androidx.annotation.CheckResult 10 | import androidx.annotation.NonNull 11 | import androidx.appcompat.app.AppCompatActivity 12 | import kotlinx.android.synthetic.main.activity_about.* 13 | import org.greenrobot.eventbus.EventBus 14 | import org.greenrobot.eventbus.Subscribe 15 | import org.greenrobot.eventbus.ThreadMode 16 | import top.itning.yunshuclassschedule.R 17 | import top.itning.yunshuclassschedule.common.BaseActivity 18 | import top.itning.yunshuclassschedule.entity.EventEntity 19 | import top.itning.yunshuclassschedule.util.ThemeChangeUtil 20 | 21 | /** 22 | * 关于 23 | * 24 | * @author itning 25 | */ 26 | class AboutActivity : BaseActivity() { 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | ThemeChangeUtil.changeTheme(this) 29 | super.onCreate(savedInstanceState) 30 | setContentView(R.layout.activity_about) 31 | EventBus.getDefault().register(this) 32 | initView() 33 | } 34 | 35 | private fun initView() { 36 | Log.d(TAG, "init view") 37 | //设置返回箭头 38 | val supportActionBar = supportActionBar 39 | if (supportActionBar != null) { 40 | supportActionBar.setDisplayHomeAsUpEnabled(true) 41 | supportActionBar.title = "关于" 42 | } 43 | tv_version.text = getPackageVersionName(this) 44 | cv_href.setOnClickListener { onCvHrefClicked() } 45 | } 46 | 47 | /** 48 | * 获取当前应用版本 49 | * 50 | * @param appCompatActivity [AppCompatActivity] 51 | * @return 版本信息 52 | */ 53 | @CheckResult 54 | private fun getPackageVersionName(@NonNull appCompatActivity: AppCompatActivity): String { 55 | return try { 56 | appCompatActivity.packageManager.getPackageInfo(appCompatActivity.packageName, 0).versionName 57 | } catch (e: PackageManager.NameNotFoundException) { 58 | Log.w("SplashActivity", "Package name not found:", e) 59 | "" 60 | } 61 | 62 | } 63 | 64 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 65 | when (item.itemId) { 66 | android.R.id.home -> { 67 | finish() 68 | } 69 | } 70 | return super.onOptionsItemSelected(item) 71 | } 72 | 73 | override fun onDestroy() { 74 | EventBus.getDefault().unregister(this) 75 | super.onDestroy() 76 | } 77 | 78 | @Subscribe(threadMode = ThreadMode.MAIN) 79 | override fun onMessageEvent(eventEntity: EventEntity) { 80 | 81 | } 82 | 83 | private fun onCvHrefClicked() { 84 | val uri = Uri.parse("https://github.com/itning/YunShuClassSchedule") 85 | startActivity(Intent(Intent.ACTION_VIEW, uri)) 86 | } 87 | 88 | companion object { 89 | private const val TAG = "AboutActivity" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/entity/ClassSchedule.java: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.entity; 2 | 3 | import org.greenrobot.greendao.annotation.Entity; 4 | import org.greenrobot.greendao.annotation.Id; 5 | import org.greenrobot.greendao.annotation.Generated; 6 | 7 | /** 8 | * 课程实体 9 | * 10 | * @author itning 11 | */ 12 | @Entity 13 | public class ClassSchedule { 14 | /** 15 | * 课程编号 16 | */ 17 | @Id 18 | private String id; 19 | /** 20 | * 星期几的课程 21 | */ 22 | private int week; 23 | /** 24 | * 第几节课 25 | */ 26 | private int section; 27 | /** 28 | * 课程名 29 | */ 30 | private String name; 31 | /** 32 | * 地点 33 | */ 34 | private String location; 35 | /** 36 | * 教师 37 | */ 38 | private String teacher; 39 | /** 40 | * 周数 41 | */ 42 | private String numberOfWeek; 43 | 44 | @Generated(hash = 61551471) 45 | public ClassSchedule(String id, int week, int section, String name, 46 | String location, String teacher, String numberOfWeek) { 47 | this.id = id; 48 | this.week = week; 49 | this.section = section; 50 | this.name = name; 51 | this.location = location; 52 | this.teacher = teacher; 53 | this.numberOfWeek = numberOfWeek; 54 | } 55 | 56 | @Generated(hash = 1679435099) 57 | public ClassSchedule() { 58 | } 59 | 60 | public String getId() { 61 | return id; 62 | } 63 | 64 | public void setId(String id) { 65 | this.id = id; 66 | } 67 | 68 | public int getWeek() { 69 | return week; 70 | } 71 | 72 | public void setWeek(int week) { 73 | this.week = week; 74 | } 75 | 76 | public int getSection() { 77 | return section; 78 | } 79 | 80 | public void setSection(int section) { 81 | this.section = section; 82 | } 83 | 84 | public String getName() { 85 | return name; 86 | } 87 | 88 | public void setName(String name) { 89 | this.name = name; 90 | } 91 | 92 | public String getLocation() { 93 | return location; 94 | } 95 | 96 | public void setLocation(String location) { 97 | this.location = location; 98 | } 99 | 100 | public String getTeacher() { 101 | return teacher; 102 | } 103 | 104 | public void setTeacher(String teacher) { 105 | this.teacher = teacher; 106 | } 107 | 108 | public String getNumberOfWeek() { 109 | return numberOfWeek; 110 | } 111 | 112 | public void setNumberOfWeek(String numberOfWeek) { 113 | this.numberOfWeek = numberOfWeek; 114 | } 115 | 116 | @Override 117 | public String toString() { 118 | return "ClassSchedule{" + 119 | "id='" + id + '\'' + 120 | ", week=" + week + 121 | ", section=" + section + 122 | ", name='" + name + '\'' + 123 | ", location='" + location + '\'' + 124 | ", teacher='" + teacher + '\'' + 125 | ", numberOfWeek='" + numberOfWeek + '\'' + 126 | '}'; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/service/TodayWidgetService.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.service 2 | 3 | import android.app.Service 4 | import android.appwidget.AppWidgetManager 5 | import android.content.ComponentName 6 | import android.content.Intent 7 | import android.content.SharedPreferences 8 | import android.os.IBinder 9 | import android.util.Log 10 | import androidx.preference.PreferenceManager 11 | import org.greenrobot.eventbus.EventBus 12 | import org.greenrobot.eventbus.Subscribe 13 | import org.greenrobot.eventbus.ThreadMode 14 | import top.itning.yunshuclassschedule.R 15 | import top.itning.yunshuclassschedule.common.ConstantPool 16 | import top.itning.yunshuclassschedule.entity.EventEntity 17 | import top.itning.yunshuclassschedule.ui.fragment.setting.SettingsFragment 18 | import top.itning.yunshuclassschedule.ui.widget.TodayWidgetProvider 19 | import top.itning.yunshuclassschedule.util.ClassScheduleUtils 20 | import java.util.* 21 | 22 | /** 23 | * 小部件更新服务 24 | * 25 | * @author itning 26 | */ 27 | class TodayWidgetService : Service(), SharedPreferences.OnSharedPreferenceChangeListener { 28 | private lateinit var sharedPreferences: SharedPreferences 29 | 30 | override fun onBind(intent: Intent?): IBinder? { 31 | throw UnsupportedOperationException("Not yet implemented") 32 | } 33 | 34 | override fun onCreate() { 35 | Log.d(TAG, "on Create") 36 | EventBus.getDefault().register(this) 37 | sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) 38 | sharedPreferences.registerOnSharedPreferenceChangeListener(this) 39 | ClassScheduleUtils.startForegroundServer(this, TAG) 40 | super.onCreate() 41 | } 42 | 43 | override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { 44 | Log.d(TAG, "on Start Command") 45 | return START_REDELIVER_INTENT 46 | } 47 | 48 | override fun onDestroy() { 49 | Log.d(TAG, "on Destroy") 50 | EventBus.getDefault().unregister(this) 51 | sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) 52 | super.onDestroy() 53 | } 54 | 55 | @Subscribe(threadMode = ThreadMode.MAIN) 56 | fun onMessageEvent(eventEntity: EventEntity) { 57 | when (eventEntity.id) { 58 | ConstantPool.Int.TIME_TICK_CHANGE -> { 59 | val thisWidget = ComponentName(this, TodayWidgetProvider::class.java) 60 | val appWidgetManager = AppWidgetManager.getInstance(this) 61 | val appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget) 62 | Log.d(TAG, "appWidgetIds: ${Arrays.toString(appWidgetIds)}") 63 | appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.lv) 64 | } 65 | else -> { 66 | 67 | } 68 | } 69 | } 70 | 71 | override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { 72 | if (key == SettingsFragment.FOREGROUND_SERVICE_STATUS) { 73 | if (sharedPreferences.getBoolean(SettingsFragment.FOREGROUND_SERVICE_STATUS, true)) { 74 | ClassScheduleUtils.startForegroundServer(this, TAG) 75 | } else { 76 | stopForeground(true) 77 | } 78 | } 79 | } 80 | 81 | companion object { 82 | private const val TAG = "TodayWidgetService" 83 | } 84 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 云舒课表 2 | Android的开源课程表应用 3 | 4 | [![GitHub stars](https://img.shields.io/github/stars/itning/YunShuClassSchedule.svg?style=social&label=Stars)](https://github.com/itning/YunShuClassSchedule/stargazers) 5 | [![GitHub forks](https://img.shields.io/github/forks/itning/YunShuClassSchedule.svg?style=social&label=Fork)](https://github.com/itning/YunShuClassSchedule/network/members) 6 | [![GitHub watchers](https://img.shields.io/github/watchers/itning/YunShuClassSchedule.svg?style=social&label=Watch)](https://github.com/itning/YunShuClassSchedule/watchers) 7 | [![GitHub followers](https://img.shields.io/github/followers/itning.svg?style=social&label=Follow)](https://github.com/itning?tab=followers) 8 | 9 | [![GitHub issues](https://img.shields.io/github/issues/itning/YunShuClassSchedule.svg)](https://github.com/itning/YunShuClassSchedule/issues) 10 | [![GitHub license](https://img.shields.io/github/license/itning/YunShuClassSchedule.svg)](https://github.com/itning/YunShuClassSchedule/blob/master/LICENSE) 11 | [![GitHub last commit](https://img.shields.io/github/last-commit/itning/YunShuClassSchedule.svg)](https://github.com/itning/YunShuClassSchedule/commits) 12 | [![GitHub release](https://img.shields.io/github/release/itning/YunShuClassSchedule.svg)](https://github.com/itning/YunShuClassSchedule/releases) 13 | [![GitHub repo size in bytes](https://img.shields.io/github/repo-size/itning/YunShuClassSchedule.svg)](https://github.com/itning/YunShuClassSchedule) 14 | [![HitCount](https://hitcount.itning.top/?u=itning&r=YunShuClassSchedule)](https://github.com/itning/hit-count) 15 | [![language](https://img.shields.io/badge/language-Kotlin-green.svg)](https://github.com/itning/YunShuClassSchedule) 16 | 17 | ## 不再维护 18 | 19 | 该项目写的很烂,没有进行分层,业务逻辑基本上都在``activity``中,未来会将该项目重构为使用``jetpack``的项目。 20 | 敬请期待。 21 | 22 | ## 下载 23 | [![Coolapk](https://github.com/itning/YunShuClassSchedule/blob/master/extra/coolapk.png)](https://www.coolapk.com/apk/top.itning.yunshuclassschedule) 24 | ## 功能 25 | - 今日课表 26 | 27 | - 本周课表 28 | 29 | - 上下课课程提醒 30 | 31 | - 上下课手机自动静音 32 | 33 | - 更换主题,背景图片 34 | 35 | - 查成绩 36 | ## 应用截图 37 | ![](https://github.com/itning/YunShuClassSchedule/blob/master/extra/a.png) 38 | ![](https://github.com/itning/YunShuClassSchedule/blob/master/extra/b.png) 39 | ![](https://github.com/itning/YunShuClassSchedule/blob/master/extra/c.png) 40 | ![](https://github.com/itning/YunShuClassSchedule/blob/master/extra/d.png) 41 | ![](https://github.com/itning/YunShuClassSchedule/blob/master/extra/e.png) 42 | ![](https://github.com/itning/YunShuClassSchedule/blob/master/extra/f.png) 43 | ![](https://github.com/itning/YunShuClassSchedule/blob/master/extra/g.png) 44 | ## 项目依赖库 45 | - [gson](https://github.com/google/gson) 46 | - [butterknife](https://github.com/JakeWharton/butterknife) 47 | - [eventbus](https://github.com/greenrobot/EventBus) 48 | - [greendao](https://github.com/greenrobot/greenDAO) 49 | - [bugly](https://bugly.qq.com) 50 | - [colorpreference](https://github.com/kizitonwose/colorpreference) 51 | - [matisse](https://github.com/zhihu/Matisse) 52 | - [glide](https://github.com/bumptech/glide) 53 | - [statusbarutil](https://github.com/laobie/StatusBarUtil) 54 | - [materialdatetimepicker](https://github.com/wdullaer/MaterialDateTimePicker) 55 | - [jsoup](https://github.com/jhy/jsoup) 56 | - [customactivityoncrash](https://github.com/Ereza/CustomActivityOnCrash) 57 | - [RangeSeekBar](https://github.com/Jay-Goo/RangeSeekBar) 58 | - [taptargetview](https://github.com/KeepSafe/TapTargetView) 59 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/ui/fragment/CheckScoreFragment.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.ui.fragment 2 | 3 | import android.os.Bundle 4 | import android.util.Log 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import androidx.fragment.app.Fragment 9 | import androidx.fragment.app.FragmentManager 10 | import org.greenrobot.eventbus.EventBus 11 | import org.greenrobot.eventbus.Subscribe 12 | import org.greenrobot.eventbus.ThreadMode 13 | import top.itning.yunshuclassschedule.R 14 | import top.itning.yunshuclassschedule.common.ConstantPool 15 | import top.itning.yunshuclassschedule.entity.EventEntity 16 | import top.itning.yunshuclassschedule.entity.Score 17 | import top.itning.yunshuclassschedule.ui.fragment.checkscore.CheckScoreLoginFragment 18 | import top.itning.yunshuclassschedule.ui.fragment.checkscore.CheckScoreShowFragment 19 | import top.itning.yunshuclassschedule.util.EventReceiver 20 | import java.util.* 21 | 22 | /** 23 | * 查成绩 24 | * 25 | * @author itning 26 | */ 27 | class CheckScoreFragment : Fragment(), EventReceiver { 28 | private lateinit var mFragmentManager: FragmentManager 29 | 30 | override fun onCreate(savedInstanceState: Bundle?) { 31 | super.onCreate(savedInstanceState) 32 | EventBus.getDefault().register(this) 33 | } 34 | 35 | override fun onDestroy() { 36 | Log.d(TAG, "on Destroy") 37 | EventBus.getDefault().unregister(this) 38 | super.onDestroy() 39 | } 40 | 41 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 42 | val view = inflater.inflate(R.layout.fragment_check_score, container, false) 43 | mFragmentManager = childFragmentManager 44 | mFragmentManager.beginTransaction() 45 | .replace(R.id.frame_container, CheckScoreLoginFragment()) 46 | .commit() 47 | return view 48 | } 49 | 50 | @Subscribe(threadMode = ThreadMode.MAIN) 51 | fun onMessageEvent(eventEntity: EventEntity) { 52 | when (eventEntity.id) { 53 | ConstantPool.Int.SCORE_LOGIN_SUCCESS -> { 54 | val checkScoreShowFragment = CheckScoreShowFragment() 55 | val bundle = Bundle() 56 | 57 | val scoreList = eventEntity.data as ArrayList 58 | bundle.putParcelableArrayList("scoreList", scoreList) 59 | checkScoreShowFragment.arguments = bundle 60 | mFragmentManager.beginTransaction() 61 | .replace(R.id.frame_container, checkScoreShowFragment) 62 | .addToBackStack("checkScoreShowFragment") 63 | .commit() 64 | } 65 | ConstantPool.Int.RETURN_LOGIN_FRAGMENT -> { 66 | mFragmentManager.beginTransaction() 67 | .replace(R.id.frame_container, CheckScoreLoginFragment()) 68 | .commit() 69 | } 70 | else -> { 71 | } 72 | } 73 | } 74 | 75 | override fun eventTrigger(): Boolean { 76 | val backStackEntryCount = mFragmentManager.backStackEntryCount 77 | if (backStackEntryCount == 1) { 78 | mFragmentManager.popBackStackImmediate() 79 | return true 80 | } 81 | return false 82 | } 83 | 84 | companion object { 85 | private const val TAG = "CheckScoreFragment" 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_this_week.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 22 | 23 | 31 | 32 | 40 | 41 | 49 | 50 | 58 | 59 | 67 | 68 | 76 | 77 | 78 | 81 | 82 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_check_score_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 19 | 20 | 24 | 25 | 26 | 38 | 39 | 44 | 45 | 46 | 61 | 62 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/common/ConstantPool.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.common 2 | 3 | /** 4 | * 常量池 5 | * 6 | * @author itning 7 | */ 8 | class ConstantPool { 9 | 10 | enum class Str(private val value: String) { 11 | /** 12 | * 数据库名 13 | */ 14 | DB_NAME("class-schedule.db"), 15 | /** 16 | * sharedPreferences 文件名 17 | */ 18 | SHARED_PREFERENCES_FILENAME("class-schedule"), 19 | /** 20 | * 第一次进入app 21 | */ 22 | FIRST_IN_APP("first_in_app"), 23 | /** 24 | * 周课表文字大小 25 | */ 26 | WEEK_FONT_SIZE("class_font_size"), 27 | /** 28 | * 上一次的日期 29 | */ 30 | LAST_DATE("last-date"), 31 | /** 32 | * 是否显示授课教师信息 33 | */ 34 | TEACHER_INFO_STATUS("teacher_week_status"), 35 | /** 36 | * 下周一 日期 37 | */ 38 | NEXT_WEEK_OF_MONDAY("this_week_of_sunday"), 39 | /** 40 | * 每天课程节数 41 | */ 42 | CLASS_SECTION("class_section"), 43 | /** 44 | * 加入反馈群 45 | */ 46 | ADD_GROUP_DIALOG_STATE("add_group_dialog_state"), 47 | /** 48 | * 是否已经引导过 49 | */ 50 | NEW_USER_IS_STUDY("new_user_is_study"); 51 | 52 | fun get(): String { 53 | return value 54 | } 55 | } 56 | 57 | enum class Int(private val value: kotlin.Int) { 58 | /** 59 | * 延迟进入主活动时间 60 | */ 61 | DELAY_INTO_MAIN_ACTIVITY_TIME(1000), 62 | /** 63 | * 下载课程表信息错误 64 | */ 65 | HTTP_ERROR(111), 66 | /** 67 | * 进入主活动 68 | */ 69 | ENTER_HOME_ACTIVITY(112), 70 | /** 71 | * 退出间隔延迟 72 | */ 73 | EXIT_DELAY(2000), 74 | /** 75 | * 时间改变 76 | */ 77 | TIME_TICK_CHANGE(120), 78 | /** 79 | * 刷新本周课程fragment数据 80 | */ 81 | REFRESH_WEEK_FRAGMENT_DATA(125), 82 | /** 83 | * 应用颜色改变 84 | */ 85 | APP_COLOR_CHANGE(126), 86 | /** 87 | * 上课提醒 88 | */ 89 | CLASS_UP_REMIND(127), 90 | /** 91 | * 下课提醒 92 | */ 93 | CLASS_DOWN_REMIND(128), 94 | /** 95 | * 手机状态:取消静音 96 | */ 97 | PHONE_MUTE_CANCEL(129), 98 | /** 99 | * 手机状态:开启静音 100 | */ 101 | PHONE_MUTE_OPEN(130), 102 | /** 103 | * 课程信息数组更新 104 | */ 105 | COURSE_INFO_ARRAY_UPDATE(131), 106 | /** 107 | * 需要重新初始化课程Fragment 108 | */ 109 | REFRESH_CLASS_SCHEDULE_FRAGMENT(133), 110 | /** 111 | * 获取验证码和Cookie成功 112 | */ 113 | GET_COOKIE_AND_IMAGE_OK(134), 114 | /** 115 | * 获取验证码和Cookie失败 116 | */ 117 | GET_COOKIE_AND_IMAGE_FAILED(135), 118 | /** 119 | * 重新登陆 120 | */ 121 | RE_LOGIN_SCORE(136), 122 | /** 123 | * 登陆时消息 124 | */ 125 | SCORE_LOGIN_MSG(137), 126 | /** 127 | * 成绩查询成功 128 | */ 129 | SCORE_LOGIN_SUCCESS(138), 130 | /** 131 | * 回到登陆页面 132 | */ 133 | RETURN_LOGIN_FRAGMENT(139), 134 | /** 135 | * 课程周数改变(非持久) 136 | */ 137 | CLASS_WEEK_CHANGE(140); 138 | 139 | fun get(): kotlin.Int { 140 | return value 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 云舒课表 3 | 打开导航抽屉 4 | 关闭导航抽屉 5 | 设置字体大小 6 | 欢迎使用 7 | 导入数据 8 | 更换背景图片 9 | 显示授课教师 10 | 姓名 11 | 学号 12 | 记住姓名和学号 13 | 登陆 14 | 课程名 15 | 地点 16 | 教师 17 | 粘贴该课程 18 | 复制该课程 19 | 软件施工:ITNING 舒露 20 | 原型设计:舒露 21 | 软件测试:舒露 22 | Github:YunShuClassSchedule 23 | Yun Shu™ SoftWare Series 24 | © 2018 ITNING.TOP ALL RIGHTS RESERVED 25 | 开始使用 26 | 星期一 27 | 星期二 28 | 星期三 29 | 星期四 30 | 星期五 31 | 星期六 32 | 星期日 33 | 课程表 34 | 查成绩 35 | 其它 36 | 设置 37 | 夜间模式 38 | 完成 39 | 导入课程数据 40 | 选择文件导入 41 | 导出课程数据 42 | 导出到文件 43 | 分享课程数据 44 | (╥﹏╥)\n发生了错误 45 | 重新打开APP 46 | 关闭APP 47 | 详细错误信息 48 | 详细错误信息 49 | 关闭 50 | 复制错误信息 51 | 已复制 52 | 错误信息 53 | 下一周 54 | 上一周 55 | 设置课程周数: 56 | 设为单周课程 57 | 设为双周课程 58 | 全部选择 59 | 清空选择 60 | 区间 61 | 今天没有课 62 | 63 | -------------------------------------------------------------------------------- /app/src/main/res/layout/widget_layout_item_draw.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 24 | 25 | 32 | 33 | 41 | 42 | 48 | 49 | 55 | 56 | 60 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 83 | 84 | 90 | 91 | 100 | 101 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'org.greenrobot.greendao' 3 | apply plugin: 'kotlin-android' 4 | apply plugin: 'kotlin-kapt' 5 | apply plugin: 'kotlin-android-extensions' 6 | apply plugin: 'bugly' 7 | 8 | android { 9 | compileSdkVersion 28 10 | defaultConfig { 11 | applicationId "top.itning.yunshuclassschedule" 12 | minSdkVersion 21 13 | targetSdkVersion 28 14 | versionCode 64 15 | versionName "2.5.8" 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | javaCompileOptions { 18 | annotationProcessorOptions { 19 | //填写自己的package name+自定义类名 20 | arguments = [eventBusIndex: 'top.itning.yunshuclassschedule.AppActivityIndex'] 21 | } 22 | } 23 | resConfigs "zh", "en" 24 | } 25 | buildTypes { 26 | release { 27 | // 混淆 28 | minifyEnabled true 29 | // Zipalign优化 30 | zipAlignEnabled true 31 | // 移除无用的resource文件 32 | shrinkResources true 33 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 34 | } 35 | greendao { 36 | schemaVersion 3 37 | } 38 | } 39 | bugly { 40 | appId = '439037c8de' 41 | appKey = '037c8d58-2f72-4412-84bc-ba0b2340b7d3' 42 | } 43 | compileOptions { 44 | sourceCompatibility JavaVersion.VERSION_1_8 45 | targetCompatibility JavaVersion.VERSION_1_8 46 | } 47 | } 48 | 49 | dependencies { 50 | implementation fileTree(include: ['*.jar'], dir: 'libs') 51 | implementation 'androidx.appcompat:appcompat:1.1.0' 52 | implementation 'androidx.annotation:annotation:1.1.0' 53 | implementation 'com.google.android.material:material:1.1.0-alpha10' 54 | implementation 'androidx.gridlayout:gridlayout:1.0.0' 55 | implementation 'androidx.cardview:cardview:1.0.0' 56 | implementation 'androidx.preference:preference:1.1.0' 57 | implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2' 58 | /*https://github.com/google/gson*/ 59 | implementation 'com.google.code.gson:gson:2.8.5' 60 | /*https://github.com/greenrobot/EventBus*/ 61 | implementation 'org.greenrobot:eventbus:3.1.1' 62 | kapt 'org.greenrobot:eventbus-annotation-processor:3.1.1' 63 | /*https://github.com/greenrobot/greenDAO*/ 64 | implementation 'org.greenrobot:greendao:3.2.2' 65 | /*https://bugly.qq.com*/ 66 | implementation 'com.tencent.bugly:crashreport:3.0.0' 67 | /*https://github.com/kizitonwose/colorpreference*/ 68 | implementation 'com.github.kizitonwose.colorpreference:support:1.1.0' 69 | /*https://github.com/zhihu/Matisse*/ 70 | implementation 'com.zhihu.android:matisse:0.5.2-beta4' 71 | /*https://github.com/bumptech/glide*/ 72 | implementation 'com.github.bumptech.glide:glide:4.9.0' 73 | kapt 'com.github.bumptech.glide:compiler:4.9.0' 74 | /*https://github.com/laobie/StatusBarUtil*/ 75 | implementation 'com.jaeger.statusbarutil:library:1.5.1' 76 | /*https://github.com/wdullaer/MaterialDateTimePicker*/ 77 | implementation 'com.wdullaer:materialdatetimepicker:4.2.1' 78 | /*https://github.com/jhy/jsoup*/ 79 | implementation 'org.jsoup:jsoup:1.12.1' 80 | /*https://github.com/Ereza/CustomActivityOnCrash*/ 81 | implementation 'cat.ereza:customactivityoncrash:2.2.0' 82 | /*https://github.com/Hector1990/AutoWrapLineLayoutDemo*/ 83 | implementation 'cn.wolfspider:autowraplinelayout:1.0.1' 84 | /*https://github.com/Jay-Goo/RangeSeekBar*/ 85 | implementation 'com.github.Jay-Goo:RangeSeekBar:2.0.6' 86 | /*https://github.com/KeepSafe/TapTargetView*/ 87 | implementation 'com.getkeepsafe.taptargetview:taptargetview:1.12.0' 88 | 89 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 90 | 91 | testImplementation 'junit:junit:4.12' 92 | androidTestImplementation 'androidx.test:runner:1.2.0' 93 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_class_rv.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 24 | 25 | 32 | 33 | 41 | 42 | 48 | 49 | 55 | 56 | 60 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 83 | 84 | 90 | 91 | 100 | 101 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_today.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 19 | 20 | 21 | 28 | 29 | 41 | 42 | 54 | 55 | 68 | 69 | 81 | 82 | 83 | 84 | 85 | 86 | 92 | 93 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/ui/activity/SettingActivity.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.ui.activity 2 | 3 | 4 | import android.content.Intent 5 | import android.content.SharedPreferences 6 | import android.os.Bundle 7 | import android.util.Log 8 | import android.view.MenuItem 9 | import androidx.preference.PreferenceFragmentCompat 10 | import androidx.preference.PreferenceManager 11 | import androidx.preference.PreferenceScreen 12 | import org.greenrobot.eventbus.EventBus 13 | import org.greenrobot.eventbus.Subscribe 14 | import org.greenrobot.eventbus.ThreadMode 15 | import top.itning.yunshuclassschedule.R 16 | import top.itning.yunshuclassschedule.common.BaseActivity 17 | import top.itning.yunshuclassschedule.common.ConstantPool 18 | import top.itning.yunshuclassschedule.entity.EventEntity 19 | import top.itning.yunshuclassschedule.service.CourseInfoService 20 | import top.itning.yunshuclassschedule.ui.fragment.setting.SettingsFragment 21 | import top.itning.yunshuclassschedule.ui.fragment.setting.SettingsFragment.Companion.FOREGROUND_SERVICE_STATUS 22 | import top.itning.yunshuclassschedule.util.ThemeChangeUtil 23 | 24 | /** 25 | * 设置Activity 26 | * 27 | * @author itning 28 | */ 29 | class SettingActivity : BaseActivity(), PreferenceFragmentCompat.OnPreferenceStartScreenCallback, SharedPreferences.OnSharedPreferenceChangeListener { 30 | private lateinit var sharedPreferences: SharedPreferences 31 | 32 | override fun onCreate(savedInstanceState: Bundle?) { 33 | ThemeChangeUtil.changeTheme(this) 34 | super.onCreate(savedInstanceState) 35 | setContentView(R.layout.activity_setting) 36 | sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) 37 | sharedPreferences.registerOnSharedPreferenceChangeListener(this) 38 | EventBus.getDefault().register(this) 39 | initView() 40 | } 41 | 42 | /** 43 | * 初始化View 44 | */ 45 | private fun initView() { 46 | Log.d(TAG, "init view") 47 | //设置返回箭头 48 | val supportActionBar = supportActionBar 49 | if (supportActionBar != null) { 50 | supportActionBar.setDisplayHomeAsUpEnabled(true) 51 | supportActionBar.title = "设置" 52 | } 53 | val settingsFragment = SettingsFragment() 54 | supportFragmentManager.beginTransaction() 55 | .replace(R.id.frame_container, settingsFragment) 56 | .commit() 57 | } 58 | 59 | @Subscribe(threadMode = ThreadMode.MAIN) 60 | override fun onMessageEvent(eventEntity: EventEntity) { 61 | when (eventEntity.id) { 62 | ConstantPool.Int.APP_COLOR_CHANGE -> { 63 | ThemeChangeUtil.changeTheme(this) 64 | } 65 | else -> { 66 | } 67 | } 68 | } 69 | 70 | override fun onDestroy() { 71 | EventBus.getDefault().unregister(this) 72 | sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) 73 | super.onDestroy() 74 | } 75 | 76 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 77 | when (item.itemId) { 78 | android.R.id.home -> { 79 | finish() 80 | } 81 | } 82 | return super.onOptionsItemSelected(item) 83 | } 84 | 85 | override fun onPreferenceStartScreen(caller: PreferenceFragmentCompat, pref: PreferenceScreen): Boolean { 86 | val fragment = SettingsFragment() 87 | val args = Bundle() 88 | val key = pref.key 89 | args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, key) 90 | fragment.arguments = args 91 | supportFragmentManager 92 | .beginTransaction() 93 | .replace(R.id.frame_container, fragment) 94 | .addToBackStack(key) 95 | .commit() 96 | return true 97 | } 98 | 99 | override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { 100 | if (key == FOREGROUND_SERVICE_STATUS) { 101 | if (sharedPreferences.getBoolean(FOREGROUND_SERVICE_STATUS, true)) { 102 | startService(Intent(this, CourseInfoService::class.java)) 103 | } 104 | } 105 | } 106 | 107 | companion object { 108 | private const val TAG = "SettingActivity" 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/util/NetWorkUtils.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.util 2 | 3 | import android.content.Context 4 | import android.location.LocationManager 5 | import android.net.ConnectivityManager 6 | import android.telephony.TelephonyManager 7 | import androidx.annotation.NonNull 8 | 9 | 10 | /** 11 | * https://www.jianshu.com/p/10ed9ae02775 12 | * 13 | * @author alic 14 | * @author itning 15 | */ 16 | object NetWorkUtils { 17 | /** 18 | * 判断是否有网络连接 19 | * 20 | * @param context [Context] 21 | * @return 有返回true 22 | */ 23 | fun isNetworkConnected(@NonNull context: Context): Boolean { 24 | // 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理) 25 | val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 26 | // 获取NetworkInfo对象 27 | val networkInfo = manager.activeNetworkInfo 28 | //判断NetworkInfo对象是否为空 29 | return networkInfo != null && networkInfo.isAvailable 30 | } 31 | 32 | /** 33 | * 判断WIFI网络是否可用 34 | * 35 | * @param context [Context] 36 | * @return 是否可用 37 | */ 38 | fun isMobileConnected(@NonNull context: Context): Boolean { 39 | //获取手机所有连接管理对象(包括对wi-fi,net等连接的管理) 40 | val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 41 | //获取NetworkInfo对象 42 | val networkInfo = manager.activeNetworkInfo 43 | //判断NetworkInfo对象是否为空 并且类型是否为MOBILE 44 | return networkInfo != null && networkInfo.type == ConnectivityManager.TYPE_MOBILE && networkInfo.isAvailable 45 | } 46 | 47 | /** 48 | * 获取当前网络连接的类型信息 49 | * 原生 50 | * 51 | * @param context [Context] 52 | * @return [ConnectivityManager] 53 | */ 54 | fun getConnectedType(@NonNull context: Context): Int { 55 | //获取手机所有连接管理对象 56 | val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 57 | //获取NetworkInfo对象 58 | val networkInfo = manager.activeNetworkInfo 59 | return if (networkInfo != null && networkInfo.isAvailable) { 60 | //返回NetworkInfo的类型 61 | networkInfo.type 62 | } else -1 63 | } 64 | 65 | /** 66 | * 获取当前的网络状态 :没有网络-0:WIFI网络1:4G网络-4:3G网络-3:2G网络-2 67 | * 自定义 68 | * 69 | * @param context [Context] 70 | * @return 网络状态 71 | */ 72 | fun getAPNType(@NonNull context: Context): Int { 73 | //结果返回值 74 | var netType = 0 75 | //获取手机所有连接管理对象 76 | val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 77 | //获取NetworkInfo对象 78 | val networkInfo = manager.activeNetworkInfo ?: return netType 79 | //NetworkInfo对象为空 则代表没有网络 80 | //否则 NetworkInfo对象不为空 则获取该networkInfo的类型 81 | val nType = networkInfo.type 82 | if (nType == ConnectivityManager.TYPE_WIFI) { 83 | //WIFI 84 | netType = 1 85 | } else if (nType == ConnectivityManager.TYPE_MOBILE) { 86 | val nSubType = networkInfo.subtype 87 | val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager 88 | //3G 联通的3G为UMTS或HSDPA 电信的3G为EVDO 89 | netType = if (nSubType == TelephonyManager.NETWORK_TYPE_LTE && !telephonyManager.isNetworkRoaming) { 90 | 4 91 | } else if (nSubType == TelephonyManager.NETWORK_TYPE_UMTS 92 | || nSubType == TelephonyManager.NETWORK_TYPE_HSDPA 93 | || nSubType == TelephonyManager.NETWORK_TYPE_EVDO_0 && !telephonyManager.isNetworkRoaming) { 94 | 3 95 | //2G 移动和联通的2G为GPRS或EGDE,电信的2G为CDMA 96 | } else if (nSubType == TelephonyManager.NETWORK_TYPE_GPRS 97 | || nSubType == TelephonyManager.NETWORK_TYPE_EDGE 98 | || nSubType == TelephonyManager.NETWORK_TYPE_CDMA && !telephonyManager.isNetworkRoaming) { 99 | 2 100 | } else { 101 | 2 102 | } 103 | } 104 | return netType 105 | } 106 | 107 | /** 108 | * 判断GPS是否打开 109 | * ACCESS_FINE_LOCATION权限 110 | * 111 | * @param context [Context] 112 | * @return 是否打开 113 | */ 114 | fun isGPSEnabled(@NonNull context: Context): Boolean { 115 | //获取手机所有连接LOCATION_SERVICE对象 116 | val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager 117 | // 通过GPS卫星定位,定位级别可以精确到街(通过24颗卫星定位,在室外和空旷的地方定位准确、速度快) 118 | val gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) 119 | // 通过WLAN或移动网络(3G/2G)确定的位置(也称作AGPS,辅助GPS定位。主要用于在室内或遮盖物(建筑群或茂密的深林等)密集的地方定位) 120 | val network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) 121 | return gps || network 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/ui/activity/SplashActivity.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.ui.activity 2 | 3 | import android.app.NotificationManager 4 | import android.app.job.JobInfo 5 | import android.app.job.JobScheduler 6 | import android.content.ComponentName 7 | import android.content.Context 8 | import android.content.Intent 9 | import android.graphics.Point 10 | import android.os.Build 11 | import android.os.Bundle 12 | import android.os.Handler 13 | import android.preference.PreferenceManager 14 | import android.util.Log 15 | import kotlinx.android.synthetic.main.activity_splash.* 16 | import org.greenrobot.eventbus.EventBus 17 | import org.greenrobot.eventbus.Subscribe 18 | import org.greenrobot.eventbus.ThreadMode 19 | import top.itning.yunshuclassschedule.R 20 | import top.itning.yunshuclassschedule.common.App 21 | import top.itning.yunshuclassschedule.common.BaseActivity 22 | import top.itning.yunshuclassschedule.common.ConstantPool 23 | import top.itning.yunshuclassschedule.entity.EventEntity 24 | import top.itning.yunshuclassschedule.service.CourseInfoService 25 | import top.itning.yunshuclassschedule.service.JobSchedulerService 26 | import top.itning.yunshuclassschedule.util.GlideApp 27 | 28 | /** 29 | * 闪屏页 30 | * 31 | * @author itning 32 | */ 33 | class SplashActivity : BaseActivity() { 34 | override fun onCreate(savedInstanceState: Bundle?) { 35 | super.onCreate(savedInstanceState) 36 | if (!isTaskRoot) { 37 | finish() 38 | return 39 | } 40 | setContentView(R.layout.activity_splash) 41 | EventBus.getDefault().register(this) 42 | initBackGroundImage() 43 | 44 | startService(Intent(this, CourseInfoService::class.java)) 45 | 46 | initJobScheduler() 47 | startTime = System.currentTimeMillis() 48 | 49 | val notificationManager = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 50 | //勿扰权限判定 51 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !notificationManager.isNotificationPolicyAccessGranted) { 52 | PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("phone_mute_status", false).apply() 53 | } 54 | Handler().postDelayed({ this.enterMainActivity() }, ConstantPool.Int.DELAY_INTO_MAIN_ACTIVITY_TIME.get() - (System.currentTimeMillis() - startTime)) 55 | } 56 | 57 | /** 58 | * 初始化背景图片 59 | */ 60 | private fun initBackGroundImage() { 61 | val display = this.windowManager.defaultDisplay 62 | val size = Point() 63 | display.getSize(size) 64 | GlideApp 65 | .with(this) 66 | .load(R.drawable.splash_background) 67 | .override(size.x, size.y) 68 | .centerCrop() 69 | .into(iv_splash) 70 | } 71 | 72 | /** 73 | * init Job Scheduler 74 | */ 75 | private fun initJobScheduler() { 76 | Log.d(TAG, "init Job Scheduler") 77 | val jobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler 78 | jobScheduler.cancelAll() 79 | val jobInfo = JobInfo.Builder(1024, ComponentName(packageName, JobSchedulerService::class.java.name)) 80 | //10 minutes 81 | .setPeriodic((10 * 60 * 1000).toLong()) 82 | .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE) 83 | .build() 84 | val schedule = jobScheduler.schedule(jobInfo) 85 | if (schedule <= 0) { 86 | Log.e(TAG, "schedule error!") 87 | } 88 | } 89 | 90 | /** 91 | * 进入主Activity 92 | */ 93 | private fun enterMainActivity() { 94 | val intent: Intent = if (App.sharedPreferences.getBoolean(ConstantPool.Str.FIRST_IN_APP.get(), true)) { 95 | //第一次进入APP 96 | Intent(this, LoginActivity::class.java) 97 | } else { 98 | //非第一次,肯定已经登陆 99 | Intent(this, MainActivity::class.java) 100 | } 101 | startActivity(intent) 102 | finish() 103 | } 104 | 105 | @Subscribe(threadMode = ThreadMode.MAIN) 106 | override fun onMessageEvent(eventEntity: EventEntity) { 107 | when (eventEntity.id) { 108 | ConstantPool.Int.ENTER_HOME_ACTIVITY -> { 109 | Handler().postDelayed({ this.enterMainActivity() }, ConstantPool.Int.DELAY_INTO_MAIN_ACTIVITY_TIME.get() - (System.currentTimeMillis() - startTime)) 110 | } 111 | else -> { 112 | } 113 | } 114 | } 115 | 116 | override fun onBackPressed() { 117 | //do nothing 118 | } 119 | 120 | override fun onDestroy() { 121 | EventBus.getDefault().unregister(this) 122 | super.onDestroy() 123 | } 124 | 125 | companion object { 126 | private const val TAG = "SplashActivity" 127 | private var startTime: Long = 0 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 25 6 | 50 7 | 8 | 9 | 10 | 今天课程 11 | 本周课程 12 | 13 | 14 | today 15 | week 16 | 17 | 18 | 19 | 上课前1分钟 20 | 上课前3分钟 21 | 上课前5分钟 22 | 上课前10分钟 23 | 上课前30分钟 24 | 25 | 26 | 1 27 | 3 28 | 5 29 | 10 30 | 30 31 | 32 | 33 | 34 | 下课前1分钟 35 | 下课前3分钟 36 | 下课前5分钟 37 | 下课前10分钟 38 | 下课前30分钟 39 | 40 | 41 | 1 42 | 3 43 | 5 44 | 10 45 | 30 46 | 47 | 48 | 49 | 上课时 50 | 上课前1分钟 51 | 上课前3分钟 52 | 上课前5分钟 53 | 上课前10分钟 54 | 55 | 56 | 0 57 | 1 58 | 3 59 | 5 60 | 10 61 | 62 | 63 | 64 | 下课时 65 | 下课前1分钟 66 | 下课前3分钟 67 | 下课前5分钟 68 | 下课前10分钟 69 | 70 | 71 | 0 72 | 1 73 | 3 74 | 5 75 | 10 76 | 77 | 78 | 79 | #f44336 80 | #e91e63 81 | #9c27b0 82 | #673ab7 83 | #3f51b5 84 | #2196f3 85 | #03a9f4 86 | #00bcd4 87 | #009688 88 | #4caf50 89 | #8bc34a 90 | #cddc39 91 | #ffeb3b 92 | #ffc107 93 | #ff9800 94 | #ff5722 95 | #795548 96 | #9e9e9e 97 | #607d8b 98 | 99 | 100 | 101 | #d32f2f 102 | #c2185b 103 | #7b1fa2 104 | #512da8 105 | #303f9f 106 | #1976d2 107 | #0288d1 108 | #0097a7 109 | #00796b 110 | #388e3c 111 | #689f38 112 | #afb42b 113 | #fbc02d 114 | #ffa000 115 | #f57c00 116 | #e64a19 117 | #5d4037 118 | #616161 119 | #455a64 120 | 121 | 122 | 123 | #FF5252 124 | #FF4081 125 | #E040FB 126 | #7C4DFF 127 | #536DFE 128 | #448AFF 129 | #40C4FF 130 | #18FFFF 131 | #64FFDA 132 | #69F0AE 133 | #B2FF59 134 | #EEFF41 135 | #FFFF00 136 | #FFD740 137 | #FFAB40 138 | #FF6E40 139 | 140 | 141 | 142 | #de746c 143 | #d14b78 144 | #9e4fac 145 | #917ab9 146 | #6873b1 147 | #9ccdf4 148 | #b0bbfa 149 | #b0edf4 150 | #5ca19b 151 | #73b776 152 | #acc68e 153 | #c9d181 154 | #fff59f 155 | #fdde81 156 | #fac371 157 | #fa9271 158 | #806c65 159 | #cccccc 160 | #798387 161 | 162 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/ui/fragment/ClassScheduleFragment.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.ui.fragment 2 | 3 | import android.os.Bundle 4 | import android.os.Parcelable 5 | import android.util.Log 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.widget.Toast 10 | import androidx.annotation.Nullable 11 | import androidx.fragment.app.Fragment 12 | import androidx.fragment.app.FragmentStatePagerAdapter 13 | import androidx.preference.PreferenceManager 14 | import androidx.viewpager.widget.PagerAdapter 15 | import kotlinx.android.synthetic.main.fragment_class_schedule.* 16 | import org.greenrobot.eventbus.EventBus 17 | import org.greenrobot.eventbus.Subscribe 18 | import org.greenrobot.eventbus.ThreadMode 19 | import top.itning.yunshuclassschedule.R 20 | import top.itning.yunshuclassschedule.common.ConstantPool 21 | import top.itning.yunshuclassschedule.entity.EventEntity 22 | import top.itning.yunshuclassschedule.ui.fragment.setting.SettingsFragment.Companion.DEFAULT_SHOW_MAIN_FRAGMENT 23 | import top.itning.yunshuclassschedule.ui.fragment.setting.SettingsFragment.Companion.NOW_WEEK_NUM 24 | import top.itning.yunshuclassschedule.util.ThemeChangeUtil 25 | import java.util.* 26 | 27 | /** 28 | * 课程表 29 | * 30 | * @author itning 31 | */ 32 | class ClassScheduleFragment : Fragment() { 33 | 34 | /** 35 | * 标题集合 36 | */ 37 | private val titleList: MutableList 38 | /** 39 | * 片段集合 40 | */ 41 | private val fragmentList: MutableList 42 | 43 | init { 44 | titleList = ArrayList() 45 | titleList.add("今天") 46 | titleList.add("本周") 47 | fragmentList = ArrayList() 48 | fragmentList.add(TodayFragment()) 49 | fragmentList.add(ThisWeekFragment()) 50 | } 51 | 52 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 53 | Log.d(TAG, "on Create View") 54 | return inflater.inflate(R.layout.fragment_class_schedule, container, false) 55 | } 56 | 57 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 58 | ThemeChangeUtil.setTabLayoutColor(requireContext(), tl) 59 | initData() 60 | //设置默认展示页面 61 | if (TODAY != PreferenceManager.getDefaultSharedPreferences(requireContext()).getString(DEFAULT_SHOW_MAIN_FRAGMENT, TODAY)) { 62 | vp.currentItem = 1 63 | tl.getTabAt(1)?.select() 64 | } 65 | } 66 | 67 | private fun initData() { 68 | //预加载 69 | vp.offscreenPageLimit = fragmentList.size 70 | vp.adapter = object : FragmentStatePagerAdapter(childFragmentManager) { 71 | 72 | override fun getCount(): Int { 73 | return fragmentList.size 74 | } 75 | 76 | override fun getItem(position: Int): Fragment { 77 | return fragmentList[position] 78 | } 79 | 80 | @Nullable 81 | override fun getPageTitle(position: Int): CharSequence { 82 | return titleList[position] 83 | } 84 | 85 | override fun getItemPosition(`object`: Any): Int { 86 | Log.d(TAG, "getItemPosition: $`object`") 87 | return PagerAdapter.POSITION_NONE 88 | } 89 | 90 | override fun restoreState(state: Parcelable?, loader: ClassLoader?) { 91 | try { 92 | super.restoreState(state, loader) 93 | } catch (e: Exception) { 94 | // null caught 95 | } 96 | 97 | } 98 | } 99 | tl.setupWithViewPager(vp) 100 | } 101 | 102 | override fun onCreate(savedInstanceState: Bundle?) { 103 | Log.d(TAG, "on Create") 104 | super.onCreate(savedInstanceState) 105 | EventBus.getDefault().register(this) 106 | } 107 | 108 | override fun onDestroy() { 109 | Log.d(TAG, "on Destroy") 110 | EventBus.getDefault().unregister(this) 111 | super.onDestroy() 112 | } 113 | 114 | @Subscribe(threadMode = ThreadMode.MAIN) 115 | fun onMessageEvent(eventEntity: EventEntity) { 116 | when (eventEntity.id) { 117 | ConstantPool.Int.APP_COLOR_CHANGE -> { 118 | ThemeChangeUtil.setTabLayoutColor(requireContext(), tl) 119 | } 120 | ConstantPool.Int.REFRESH_CLASS_SCHEDULE_FRAGMENT -> { 121 | val adapter = vp.adapter 122 | if (adapter == null) { 123 | Toast.makeText(requireContext(), "未找到适配器,尝试重新打开APP解决此问题", Toast.LENGTH_LONG).show() 124 | return 125 | } 126 | adapter.notifyDataSetChanged() 127 | } 128 | ConstantPool.Int.CLASS_WEEK_CHANGE -> { 129 | if (PreferenceManager.getDefaultSharedPreferences(context).getString(NOW_WEEK_NUM, "1") == eventEntity.msg) { 130 | tl.getTabAt(1)?.text = "本周" 131 | } else { 132 | tl.getTabAt(1)?.text = "第${eventEntity.msg}周" 133 | } 134 | } 135 | else -> { 136 | } 137 | } 138 | } 139 | 140 | companion object { 141 | private const val TAG = "ClassScheduleFragment" 142 | private const val TODAY = "today" 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/ui/widget/ToadyRemoteViewsFactory.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.ui.widget 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.graphics.Bitmap 6 | import android.graphics.Canvas 7 | import android.util.Log 8 | import android.view.LayoutInflater 9 | import android.view.View 10 | import android.widget.* 11 | import androidx.core.content.ContextCompat 12 | import androidx.preference.PreferenceManager 13 | import top.itning.yunshuclassschedule.R 14 | import top.itning.yunshuclassschedule.common.App 15 | import top.itning.yunshuclassschedule.common.ConstantPool 16 | import top.itning.yunshuclassschedule.entity.ClassSchedule 17 | import top.itning.yunshuclassschedule.entity.ClassScheduleDao 18 | import top.itning.yunshuclassschedule.ui.fragment.setting.SettingsFragment 19 | import top.itning.yunshuclassschedule.ui.view.RoundBackChange 20 | import top.itning.yunshuclassschedule.util.ClassScheduleUtils 21 | import top.itning.yunshuclassschedule.util.DateUtils 22 | import java.util.* 23 | 24 | 25 | /** 26 | * 适配器工厂 27 | * 28 | * @author itning 29 | */ 30 | class ToadyRemoteViewsFactory(val context: Context, val intent: Intent?) : RemoteViewsService.RemoteViewsFactory { 31 | private var orderListBySection = mutableListOf() 32 | var width: Int = 0 33 | var height: Int = 0 34 | private val daoSession = (context.applicationContext as App).daoSession 35 | private val colorArray = IntArray(7) 36 | 37 | override fun onCreate() { 38 | val scale = context.resources.displayMetrics.density 39 | width = (325 * scale + 0.5f).toInt() 40 | height = (60 * scale + 0.5f).toInt() 41 | colorArray[0] = ContextCompat.getColor(context, R.color.class_color_1) 42 | colorArray[1] = ContextCompat.getColor(context, R.color.class_color_2) 43 | colorArray[2] = ContextCompat.getColor(context, R.color.class_color_3) 44 | colorArray[3] = ContextCompat.getColor(context, R.color.class_color_4) 45 | colorArray[4] = ContextCompat.getColor(context, R.color.class_color_5) 46 | colorArray[5] = ContextCompat.getColor(context, R.color.class_color_6) 47 | colorArray[6] = ContextCompat.getColor(context, R.color.class_color_7) 48 | } 49 | 50 | override fun onDataSetChanged() { 51 | Log.d(TAG, "onDataSetChanged") 52 | val nowWeekNum = (PreferenceManager.getDefaultSharedPreferences(context).getString(SettingsFragment.NOW_WEEK_NUM, "1")!!.toInt() - 1).toString() 53 | val section = App.sharedPreferences.getInt(ConstantPool.Str.CLASS_SECTION.get(), 5) 54 | orderListBySection = ClassScheduleUtils 55 | .orderListBySection(daoSession 56 | .classScheduleDao 57 | .queryBuilder() 58 | .where(ClassScheduleDao.Properties.Week.eq(DateUtils.week)) 59 | .list() 60 | .filter { ClassScheduleUtils.isThisWeekOfClassSchedule(it, nowWeekNum) } 61 | .filter { it.section <= section } 62 | .toMutableList()) 63 | 64 | } 65 | 66 | override fun onDestroy() { 67 | Log.d(TAG, "onDestroy") 68 | orderListBySection.clear() 69 | } 70 | 71 | override fun getCount(): Int { 72 | return orderListBySection.size 73 | } 74 | 75 | override fun getViewAt(position: Int): RemoteViews? { 76 | val classSchedule = orderListBySection[position] 77 | val relativeLayout = LayoutInflater.from(context).inflate(R.layout.widget_layout_item_draw, RelativeLayout(context)) 78 | val name = relativeLayout.findViewById(R.id.tv_name) 79 | val location = relativeLayout.findViewById(R.id.tv_location) 80 | val time = relativeLayout.findViewById(R.id.tv_time) 81 | val round = relativeLayout.findViewById(R.id.round) 82 | round.setBackColor(colorArray[Random().nextInt(colorArray.size)]) 83 | name.text = classSchedule.name 84 | location.text = classSchedule.location 85 | time.text = DateUtils.timeList[classSchedule.section - 1] 86 | if (position == 0 && ClassScheduleUtils.haveClassAfterTime(orderListBySection)) { 87 | val viewBottom = relativeLayout.findViewById(R.id.view_bottom) 88 | val viewTop = relativeLayout.findViewById(R.id.view_top) 89 | val viewLeft = relativeLayout.findViewById(R.id.view_left) 90 | val viewProgress = relativeLayout.findViewById(R.id.view_progress) 91 | val flNo = relativeLayout.findViewById(R.id.fl_no) 92 | flNo.visibility = View.INVISIBLE 93 | viewBottom.visibility = View.VISIBLE 94 | viewTop.visibility = View.VISIBLE 95 | viewLeft.visibility = View.VISIBLE 96 | viewProgress.visibility = View.VISIBLE 97 | val layoutParams = viewProgress.layoutParams 98 | layoutParams.width = DateUtils.getNowProgress(width, orderListBySection) 99 | viewProgress.layoutParams = layoutParams 100 | } 101 | 102 | val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) 103 | relativeLayout.measure(width, height) 104 | relativeLayout.layout(0, 0, 0, 0) 105 | relativeLayout.draw(Canvas(bitmap)) 106 | 107 | val remoteViews = RemoteViews(context.packageName, R.layout.widget_layout_item) 108 | remoteViews.setBitmap(R.id.iv, "setImageBitmap", bitmap) 109 | return remoteViews 110 | } 111 | 112 | override fun getLoadingView(): RemoteViews? { 113 | return null 114 | } 115 | 116 | override fun getViewTypeCount(): Int { 117 | return 1 118 | } 119 | 120 | override fun getItemId(position: Int): Long { 121 | return position.toLong() 122 | } 123 | 124 | override fun hasStableIds(): Boolean { 125 | return true 126 | } 127 | 128 | companion object { 129 | private const val TAG = "ToadyRemoteViewsFactory" 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/ui/adapter/TodayRecyclerViewAdapter.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.ui.adapter 2 | 3 | import android.content.Context 4 | import android.graphics.Point 5 | import android.util.Log 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.view.WindowManager 10 | import android.widget.FrameLayout 11 | import android.widget.TextView 12 | import android.widget.Toast 13 | import androidx.annotation.NonNull 14 | import androidx.core.content.ContextCompat 15 | import androidx.recyclerview.widget.RecyclerView 16 | import com.tencent.bugly.crashreport.CrashReport 17 | import top.itning.yunshuclassschedule.R 18 | import top.itning.yunshuclassschedule.entity.ClassSchedule 19 | import top.itning.yunshuclassschedule.ui.view.RoundBackChange 20 | import top.itning.yunshuclassschedule.util.ClassScheduleUtils 21 | import top.itning.yunshuclassschedule.util.DateUtils 22 | import top.itning.yunshuclassschedule.util.ThemeChangeUtil 23 | import java.util.* 24 | 25 | /** 26 | * 今天课程列表适配器 27 | * 28 | * @author itning 29 | */ 30 | class TodayRecyclerViewAdapter( 31 | /** 32 | * 列表数据集合 33 | */ 34 | @param:NonNull private val scheduleList: List, 35 | /** 36 | * [Context] 37 | */ 38 | @param:NonNull private val context: Context) : RecyclerView.Adapter() { 39 | /** 40 | * 颜色数组 41 | */ 42 | private val colorArray = IntArray(7) 43 | /** 44 | * 随机好的颜色集合 45 | */ 46 | private val showColorList: ArrayList 47 | var viewProgress: View? = null 48 | private set 49 | 50 | init { 51 | Log.d(TAG, "new Today Recycler View Adapter") 52 | //数组赋值 53 | colorArray[0] = ContextCompat.getColor(context, R.color.class_color_1) 54 | colorArray[1] = ContextCompat.getColor(context, R.color.class_color_2) 55 | colorArray[2] = ContextCompat.getColor(context, R.color.class_color_3) 56 | colorArray[3] = ContextCompat.getColor(context, R.color.class_color_4) 57 | colorArray[4] = ContextCompat.getColor(context, R.color.class_color_5) 58 | colorArray[5] = ContextCompat.getColor(context, R.color.class_color_6) 59 | colorArray[6] = ContextCompat.getColor(context, R.color.class_color_7) 60 | //随机颜色集合构建 61 | val random = Random() 62 | showColorList = ArrayList(colorArray.size) 63 | do { 64 | val number = random.nextInt(colorArray.size) 65 | if (!showColorList.contains(number)) { 66 | showColorList.add(number) 67 | } 68 | } while (showColorList.size != colorArray.size) 69 | } 70 | 71 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 72 | Log.d(TAG, "onCreateViewHolder") 73 | return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_class_rv, parent, false)) 74 | } 75 | 76 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 77 | Log.d(TAG, "onBindViewHolder pos->$position") 78 | if (position >= scheduleList.size) { 79 | scheduleList.forEach { Log.e(TAG, it.toString()) } 80 | CrashReport.postCatchedException(Throwable("onBindViewHolder scheduleList.size ${scheduleList.size}")) 81 | Toast.makeText(context, "内部数据错误,请联系开发者", Toast.LENGTH_LONG).show() 82 | } 83 | val classSchedule = scheduleList[position] 84 | holder.tvName.text = classSchedule.name 85 | holder.tvLocation.text = classSchedule.location 86 | holder.tvTime.text = DateUtils.timeList[classSchedule.section - 1] 87 | if (position >= showColorList.size) { 88 | val number = Random().nextInt(colorArray.size) 89 | holder.round.setBackColor(colorArray[number]) 90 | } else { 91 | holder.round.setBackColor(colorArray[showColorList[position]]) 92 | } 93 | //显示设置可见性 94 | holder.flNo.visibility = View.VISIBLE 95 | holder.viewBottom.visibility = View.INVISIBLE 96 | holder.viewTop.visibility = View.INVISIBLE 97 | holder.viewLeft.visibility = View.INVISIBLE 98 | holder.viewProgress.visibility = View.INVISIBLE 99 | ThemeChangeUtil.setProgressBackgroundResource(context, holder.viewProgress) 100 | ThemeChangeUtil.setBackgroundResources(context, holder.viewBottom, holder.viewTop, holder.viewLeft, holder.viewCenter) 101 | if (position == 0 && ClassScheduleUtils.haveClassAfterTime(scheduleList)) { 102 | //是当前正在或要上的课程 103 | holder.flNo.visibility = View.INVISIBLE 104 | holder.viewBottom.visibility = View.VISIBLE 105 | holder.viewTop.visibility = View.VISIBLE 106 | holder.viewLeft.visibility = View.VISIBLE 107 | holder.viewProgress.visibility = View.VISIBLE 108 | val display = (Objects.requireNonNull(context.getSystemService(Context.WINDOW_SERVICE)) as WindowManager).defaultDisplay 109 | val size = Point() 110 | display.getSize(size) 111 | val layoutParams = holder.viewProgress.layoutParams 112 | layoutParams.width = DateUtils.getNowProgress(size.x, scheduleList) 113 | holder.viewProgress.layoutParams = layoutParams 114 | viewProgress = holder.viewProgress 115 | } 116 | } 117 | 118 | override fun getItemCount(): Int { 119 | return scheduleList.size 120 | } 121 | 122 | class ViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) { 123 | val viewLeft: View = itemView.findViewById(R.id.view_left) 124 | 125 | val viewTop: View = itemView.findViewById(R.id.view_top) 126 | 127 | val viewBottom: View = itemView.findViewById(R.id.view_bottom) 128 | 129 | val viewCenter: View = itemView.findViewById(R.id.view_center) 130 | 131 | val viewProgress: View = itemView.findViewById(R.id.view_progress) 132 | 133 | val round: RoundBackChange = itemView.findViewById(R.id.round) 134 | 135 | val flNo: FrameLayout = itemView.findViewById(R.id.fl_no) 136 | 137 | val tvName: TextView = itemView.findViewById(R.id.tv_name) 138 | 139 | val tvLocation: TextView = itemView.findViewById(R.id.tv_location) 140 | 141 | val tvTime: TextView = itemView.findViewById(R.id.tv_time) 142 | } 143 | 144 | companion object { 145 | private const val TAG = "TodayRecyclerAdapter" 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 26 | 29 | 32 | 35 | 36 | 37 | 47 | 51 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 75 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 107 | 111 | 115 | 120 | 125 | 129 | 130 | 131 | 132 | 135 | 136 | 137 | 138 | 139 | 140 | 145 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/ui/fragment/ThisWeekFragment.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.ui.fragment 2 | 3 | import android.graphics.Point 4 | import android.graphics.drawable.Drawable 5 | import android.os.Bundle 6 | import android.util.Log 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import android.widget.Toast 11 | import androidx.fragment.app.Fragment 12 | import androidx.preference.PreferenceManager 13 | import com.bumptech.glide.load.engine.DiskCacheStrategy 14 | import com.bumptech.glide.request.target.CustomViewTarget 15 | import com.bumptech.glide.request.transition.Transition 16 | import kotlinx.android.synthetic.main.fragment_this_week.* 17 | import org.greenrobot.eventbus.EventBus 18 | import org.greenrobot.eventbus.Subscribe 19 | import org.greenrobot.eventbus.ThreadMode 20 | import top.itning.yunshuclassschedule.R 21 | import top.itning.yunshuclassschedule.common.App 22 | import top.itning.yunshuclassschedule.common.ConstantPool 23 | import top.itning.yunshuclassschedule.entity.EventEntity 24 | import top.itning.yunshuclassschedule.ui.adapter.ClassScheduleItemLongClickListener 25 | import top.itning.yunshuclassschedule.ui.fragment.setting.SettingsFragment 26 | import top.itning.yunshuclassschedule.util.ClassScheduleUtils 27 | import top.itning.yunshuclassschedule.util.ClassScheduleUtils.COPY_LIST 28 | import top.itning.yunshuclassschedule.util.GlideApp 29 | 30 | 31 | /** 32 | * 本周 33 | * 34 | * @author itning 35 | */ 36 | class ThisWeekFragment : Fragment() { 37 | private lateinit var mView: View 38 | private lateinit var clickListener: ClassScheduleItemLongClickListener 39 | 40 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 41 | Log.d(TAG, "on Create View") 42 | mView = inflater.inflate(R.layout.fragment_this_week, container, false) 43 | return mView 44 | } 45 | 46 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 47 | setViewBackground() 48 | val nowWeekNum = (PreferenceManager.getDefaultSharedPreferences(context).getString(SettingsFragment.NOW_WEEK_NUM, "1")!!.toInt() - 1).toString() 49 | val daoSession = (requireActivity().application as App).daoSession 50 | val classScheduleList = daoSession.classScheduleDao.loadAll() 51 | .filter { ClassScheduleUtils.isThisWeekOfClassSchedule(it, nowWeekNum) } 52 | .toMutableList() 53 | clickListener = ClassScheduleItemLongClickListener(requireActivity(), classScheduleList, COPY_LIST) 54 | ClassScheduleUtils.loadingView(classScheduleList, schedule_gridlayout, gl_header, clickListener, requireActivity()) 55 | } 56 | 57 | override fun onCreate(savedInstanceState: Bundle?) { 58 | super.onCreate(savedInstanceState) 59 | EventBus.getDefault().register(this) 60 | } 61 | 62 | override fun onDestroy() { 63 | Log.d(TAG, "on Destroy") 64 | EventBus.getDefault().unregister(this) 65 | super.onDestroy() 66 | } 67 | 68 | @Subscribe(threadMode = ThreadMode.MAIN) 69 | fun onMessageEvent(eventEntity: EventEntity) { 70 | when (eventEntity.id) { 71 | ConstantPool.Int.REFRESH_WEEK_FRAGMENT_DATA -> { 72 | val nowWeekNum = (PreferenceManager.getDefaultSharedPreferences(context).getString(SettingsFragment.NOW_WEEK_NUM, "1")!!.toInt() - 1).toString() 73 | val daoSession = (requireActivity().application as App).daoSession 74 | val classScheduleList = daoSession.classScheduleDao.loadAll() 75 | .filter { ClassScheduleUtils.isThisWeekOfClassSchedule(it, nowWeekNum) } 76 | .toMutableList() 77 | clickListener = ClassScheduleItemLongClickListener(requireActivity(), classScheduleList, COPY_LIST) 78 | ClassScheduleUtils.loadingView(classScheduleList, schedule_gridlayout, gl_header, clickListener, requireActivity()) 79 | } 80 | ConstantPool.Int.APP_COLOR_CHANGE -> { 81 | clickListener.updateBtnBackgroundTintList() 82 | } 83 | ConstantPool.Int.CLASS_WEEK_CHANGE -> { 84 | val nowWeekNum = (eventEntity.msg!!.toInt() - 1).toString() 85 | val daoSession = (requireActivity().application as App).daoSession 86 | val classScheduleList = daoSession.classScheduleDao.loadAll() 87 | .filter { ClassScheduleUtils.isThisWeekOfClassSchedule(it, nowWeekNum) } 88 | .toMutableList() 89 | clickListener = ClassScheduleItemLongClickListener(requireActivity(), classScheduleList, COPY_LIST) 90 | ClassScheduleUtils.loadingView(classScheduleList, schedule_gridlayout, gl_header, clickListener, requireActivity()) 91 | } 92 | else -> { 93 | } 94 | } 95 | } 96 | 97 | /** 98 | * 设置视图背景 99 | */ 100 | private fun setViewBackground() { 101 | val file = requireContext().getFileStreamPath("background_img") 102 | if (file.exists() && file.isFile && file.length() != 0L) { 103 | val display = requireActivity().windowManager.defaultDisplay 104 | val size = Point() 105 | display.getSize(size) 106 | Log.d(TAG, "screen width:" + size.x + " height:" + size.y) 107 | GlideApp 108 | .with(this) 109 | .load(file) 110 | .override(size.x, size.y) 111 | .diskCacheStrategy(DiskCacheStrategy.NONE) 112 | .skipMemoryCache(true) 113 | .into(object : CustomViewTarget(mView) { 114 | 115 | override fun onLoadFailed(errorDrawable: Drawable?) { 116 | Log.d(TAG, "on Load Failed : $errorDrawable") 117 | Toast.makeText(requireContext(), "图片加载失败", Toast.LENGTH_LONG).show() 118 | } 119 | 120 | override fun onResourceReady(resource: Drawable, transition: Transition?) { 121 | Log.d(TAG, "on Resource Ready : $resource") 122 | view.background = resource 123 | } 124 | 125 | override fun onResourceCleared(placeholder: Drawable?) { 126 | Log.d(TAG, "on Resource Cleared : $placeholder") 127 | view.background = placeholder 128 | } 129 | }) 130 | } else { 131 | Log.d(TAG, "file is not exists , now use default background") 132 | mView.setBackgroundResource(R.drawable.this_week_background) 133 | } 134 | } 135 | 136 | companion object { 137 | private const val TAG = "ThisWeekFragment" 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 23 | 24 | 36 | 37 | 51 | 52 | 56 | 57 | 63 | 64 | 71 | 72 | 79 | 80 | 81 | 82 | 97 | 98 | 102 | 103 | 109 | 110 | 111 | 112 | 124 | 125 | 139 | 140 | 153 | 154 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/service/CommonService.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.service 2 | 3 | import android.annotation.TargetApi 4 | import android.app.NotificationChannel 5 | import android.app.NotificationManager 6 | import android.app.Service 7 | import android.content.Context 8 | import android.content.Intent 9 | import android.content.IntentFilter 10 | import android.content.SharedPreferences 11 | import android.os.Build 12 | import android.os.IBinder 13 | import android.util.Log 14 | import android.widget.Toast 15 | import androidx.annotation.NonNull 16 | import androidx.preference.PreferenceManager 17 | import org.greenrobot.eventbus.EventBus 18 | import org.greenrobot.eventbus.Subscribe 19 | import org.greenrobot.eventbus.ThreadMode 20 | import top.itning.yunshuclassschedule.common.App 21 | import top.itning.yunshuclassschedule.common.ConstantPool 22 | import top.itning.yunshuclassschedule.entity.EventEntity 23 | import top.itning.yunshuclassschedule.receiver.TimeTickReceiver 24 | import top.itning.yunshuclassschedule.ui.fragment.setting.SettingsFragment.Companion.FOREGROUND_SERVICE_STATUS 25 | import top.itning.yunshuclassschedule.ui.fragment.setting.SettingsFragment.Companion.NOW_WEEK_NUM 26 | import top.itning.yunshuclassschedule.util.ClassScheduleUtils 27 | import top.itning.yunshuclassschedule.util.DateUtils.getNextMondayOfTimeInMillis 28 | 29 | /** 30 | * 公共服务 31 | * 32 | * @author itning 33 | */ 34 | class CommonService : Service(), SharedPreferences.OnSharedPreferenceChangeListener { 35 | private lateinit var timeTickReceiver: TimeTickReceiver 36 | private lateinit var sharedPreferences: SharedPreferences 37 | 38 | override fun onCreate() { 39 | Log.d(TAG, "on Create") 40 | EventBus.getDefault().register(this) 41 | sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) 42 | sharedPreferences.registerOnSharedPreferenceChangeListener(this) 43 | initNotificationChannel() 44 | ClassScheduleUtils.startForegroundServer(this, TAG) 45 | val filter = IntentFilter() 46 | filter.addAction(Intent.ACTION_TIME_TICK) 47 | //设置了系统时区 48 | filter.addAction(Intent.ACTION_TIMEZONE_CHANGED) 49 | //设置了系统时间 50 | filter.addAction(Intent.ACTION_TIME_CHANGED) 51 | timeTickReceiver = TimeTickReceiver() 52 | registerReceiver(timeTickReceiver, filter) 53 | } 54 | 55 | /** 56 | * 初始化通知渠道 57 | * 58 | * @since android 8.0 59 | */ 60 | private fun initNotificationChannel() { 61 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 62 | Log.d(TAG, "Build.VERSION.SDK_INT :" + Build.VERSION.SDK_INT + " now create Notification Channel") 63 | 64 | var channelId = "class_reminder" 65 | var channelName = "课程提醒" 66 | var importance = NotificationManager.IMPORTANCE_HIGH 67 | createNotificationChannel(channelId, channelName, importance, true) 68 | 69 | channelId = "foreground_service" 70 | channelName = "前台服务" 71 | importance = NotificationManager.IMPORTANCE_NONE 72 | createNotificationChannel(channelId, channelName, importance, false) 73 | } 74 | } 75 | 76 | override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { 77 | Log.d(TAG, "on Start Command") 78 | return START_REDELIVER_INTENT 79 | } 80 | 81 | override fun onDestroy() { 82 | EventBus.getDefault().unregister(this) 83 | unregisterReceiver(timeTickReceiver) 84 | sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) 85 | } 86 | 87 | override fun onBind(intent: Intent): IBinder? { 88 | throw UnsupportedOperationException("Not yet implemented") 89 | } 90 | 91 | @Subscribe(threadMode = ThreadMode.MAIN) 92 | fun onMessageEvent(eventEntity: EventEntity) { 93 | when (eventEntity.id) { 94 | ConstantPool.Int.HTTP_ERROR -> { 95 | Toast.makeText(this, eventEntity.msg, Toast.LENGTH_LONG).show() 96 | } 97 | ConstantPool.Int.TIME_TICK_CHANGE -> { 98 | val currentTimeMillis = System.currentTimeMillis() 99 | if (isNewWeek(currentTimeMillis)) { 100 | val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) 101 | var i = defaultSharedPreferences.getString(NOW_WEEK_NUM, "1")!!.toInt() 102 | var lastTimeMillis = App.sharedPreferences.getLong(ConstantPool.Str.NEXT_WEEK_OF_MONDAY.get(), currentTimeMillis) 103 | while (true) { 104 | if (currentTimeMillis > lastTimeMillis) { 105 | Log.d(TAG, "currentTimeMillis > nextMondayOfTimeInMillis is true") 106 | val nextMondayOfTimeInMillis = getNextMondayOfTimeInMillis(lastTimeMillis) 107 | lastTimeMillis = nextMondayOfTimeInMillis 108 | i++ 109 | continue 110 | } 111 | break 112 | } 113 | if (i > 50) { 114 | i = 1 115 | } 116 | Log.d(TAG, "Set week num $i") 117 | if (defaultSharedPreferences.edit().putString(NOW_WEEK_NUM, i.toString()).commit()) { 118 | App.sharedPreferences.edit().putLong(ConstantPool.Str.NEXT_WEEK_OF_MONDAY.get(), lastTimeMillis).apply() 119 | } 120 | } 121 | } 122 | else -> { 123 | 124 | } 125 | } 126 | } 127 | 128 | /** 129 | * 检查是不是新的一周 130 | */ 131 | private fun isNewWeek(currentTimeMillis: Long): Boolean { 132 | val lastTimeMillis = App.sharedPreferences.getLong(ConstantPool.Str.NEXT_WEEK_OF_MONDAY.get(), currentTimeMillis) 133 | val isNewWeek = currentTimeMillis > lastTimeMillis 134 | Log.d(TAG, "Is new week ? $isNewWeek") 135 | return isNewWeek 136 | } 137 | 138 | /** 139 | * 创建通知渠道 140 | * 141 | * @param channelId 渠道ID 142 | * @param channelName 渠道名 143 | * @param importance 重要程度 [NotificationManager] 144 | */ 145 | @TargetApi(Build.VERSION_CODES.O) 146 | private fun createNotificationChannel(@NonNull channelId: String, @NonNull channelName: String, importance: Int, showBadge: Boolean) { 147 | Log.d(TAG, "created Notification Channel id:$channelId name:$channelName importance:$importance") 148 | val channel = NotificationChannel(channelId, channelName, importance) 149 | channel.setShowBadge(showBadge) 150 | val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 151 | notificationManager.createNotificationChannel(channel) 152 | } 153 | 154 | override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { 155 | if (key == FOREGROUND_SERVICE_STATUS) { 156 | if (sharedPreferences.getBoolean(FOREGROUND_SERVICE_STATUS, true)) { 157 | ClassScheduleUtils.startForegroundServer(this, TAG) 158 | } else { 159 | stopForeground(true) 160 | } 161 | } 162 | } 163 | 164 | companion object { 165 | private const val TAG = "CommonService" 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/ui/fragment/setting/SettingsFragment.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.ui.fragment.setting 2 | 3 | import android.app.NotificationManager 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.SharedPreferences 7 | import android.os.Build 8 | import android.os.Bundle 9 | import android.provider.Settings 10 | import android.util.Log 11 | import android.widget.Toast 12 | import androidx.appcompat.app.AlertDialog 13 | import androidx.preference.* 14 | import org.greenrobot.eventbus.EventBus 15 | import top.itning.yunshuclassschedule.R 16 | import top.itning.yunshuclassschedule.common.App 17 | import top.itning.yunshuclassschedule.common.ConstantPool 18 | import top.itning.yunshuclassschedule.entity.EventEntity 19 | import top.itning.yunshuclassschedule.util.DateUtils.getNextMondayOfTimeInMillis 20 | import top.itning.yunshuclassschedule.util.ThemeChangeUtil 21 | 22 | /** 23 | * 设置Fragment 24 | * 25 | * @author itning 26 | */ 27 | class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener { 28 | 29 | private lateinit var prefs: SharedPreferences 30 | private lateinit var defaultShowMainFragmentListPreference: ListPreference 31 | private lateinit var classReminderUpTime: ListPreference 32 | private lateinit var classReminderDownTime: ListPreference 33 | private lateinit var phoneMuteBeforeTime: ListPreference 34 | private lateinit var phoneMuteAfterTime: ListPreference 35 | private lateinit var nowWeekNumEditTextPreference: EditTextPreference 36 | 37 | override fun onCreate(savedInstanceState: Bundle?) { 38 | super.onCreate(savedInstanceState) 39 | prefs = PreferenceManager.getDefaultSharedPreferences(context) 40 | prefs.registerOnSharedPreferenceChangeListener(this) 41 | val bundle = arguments 42 | if (bundle == null) { 43 | defaultShowMainFragmentListPreference = findPreference(DEFAULT_SHOW_MAIN_FRAGMENT)!! 44 | defaultShowMainFragmentListPreference.summary = defaultShowMainFragmentListPreference.entry 45 | val foregroundServiceStatus: Preference = findPreference(FOREGROUND_SERVICE_STATUS)!! 46 | foregroundServiceStatus.setOnPreferenceChangeListener { _, newValue -> 47 | if (!(newValue as Boolean)) { 48 | AlertDialog.Builder(requireContext()).setTitle("注意") 49 | .setMessage("关闭后台常驻会导致提醒服务,手机自动静音服务不准确。建议您不要关闭!") 50 | .setCancelable(true) 51 | .setPositiveButton("我知道了", null) 52 | .show() 53 | } 54 | true 55 | } 56 | nowWeekNumEditTextPreference = findPreference(NOW_WEEK_NUM)!! 57 | nowWeekNumEditTextPreference.setOnPreferenceChangeListener { _, newValue -> 58 | if (newValue.toString() == "" || (Integer.valueOf(newValue.toString())) > 50) { 59 | Toast.makeText(requireContext(), "设置的值必须大于1且小于50", Toast.LENGTH_LONG).show() 60 | false 61 | } else { 62 | true 63 | } 64 | } 65 | nowWeekNumEditTextPreference.summary = "第${prefs.getString(NOW_WEEK_NUM, "1")}周" 66 | } else { 67 | when (bundle.getString(ARG_PREFERENCE_ROOT)) { 68 | "class_reminder" -> { 69 | classReminderUpTime = findPreference(CLASS_REMINDER_UP_TIME)!! 70 | classReminderDownTime = findPreference(CLASS_REMINDER_DOWN_TIME)!! 71 | classReminderUpTime.summary = classReminderUpTime.entry 72 | classReminderDownTime.summary = classReminderDownTime.entry 73 | } 74 | "phone_mute" -> { 75 | phoneMuteBeforeTime = findPreference(PHONE_MUTE_BEFORE_TIME)!! 76 | phoneMuteAfterTime = findPreference(PHONE_MUTE_AFTER_TIME)!! 77 | val phoneMuteStatus = findPreference(PHONE_MUTE_STATUS)!! 78 | phoneMuteStatus.setOnPreferenceChangeListener { _, newValue -> 79 | if (newValue as Boolean) { 80 | val notificationManager = requireContext().getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 81 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !notificationManager.isNotificationPolicyAccessGranted) { 82 | Toast.makeText(requireContext(), "请授予免打扰权限", Toast.LENGTH_LONG).show() 83 | Toast.makeText(requireContext(), "权限授予后请重新开启自动静音", Toast.LENGTH_LONG).show() 84 | startActivity(Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS)) 85 | return@setOnPreferenceChangeListener false 86 | } else { 87 | return@setOnPreferenceChangeListener true 88 | } 89 | } 90 | true 91 | } 92 | phoneMuteBeforeTime.summary = phoneMuteBeforeTime.entry 93 | phoneMuteAfterTime.summary = phoneMuteAfterTime.entry 94 | } 95 | } 96 | } 97 | } 98 | 99 | override fun onDestroyView() { 100 | Log.d(TAG, "on Destroy View") 101 | prefs.unregisterOnSharedPreferenceChangeListener(this) 102 | super.onDestroyView() 103 | } 104 | 105 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 106 | val bundle = arguments 107 | if (bundle == null) { 108 | setPreferencesFromResource(R.xml.preference_settings, rootKey) 109 | } else { 110 | setPreferencesFromResource(R.xml.preference_settings, bundle.getString(ARG_PREFERENCE_ROOT)) 111 | } 112 | } 113 | 114 | override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { 115 | when (key) { 116 | DEFAULT_SHOW_MAIN_FRAGMENT -> { 117 | defaultShowMainFragmentListPreference.summary = defaultShowMainFragmentListPreference.entry 118 | } 119 | CLASS_REMINDER_UP_TIME -> { 120 | classReminderUpTime.summary = classReminderUpTime.entry 121 | } 122 | CLASS_REMINDER_DOWN_TIME -> { 123 | classReminderDownTime.summary = classReminderDownTime.entry 124 | } 125 | PHONE_MUTE_BEFORE_TIME -> { 126 | phoneMuteBeforeTime.summary = phoneMuteBeforeTime.entry 127 | } 128 | PHONE_MUTE_AFTER_TIME -> { 129 | phoneMuteAfterTime.summary = phoneMuteAfterTime.entry 130 | } 131 | APP_COLOR_PRIMARY -> { 132 | ThemeChangeUtil.changeColor() 133 | } 134 | APP_COLOR_PRIMARY_DARK -> { 135 | ThemeChangeUtil.changeColor() 136 | } 137 | APP_COLOR_ACCENT -> { 138 | ThemeChangeUtil.changeColor() 139 | } 140 | APP_COLOR_PROGRESS -> { 141 | ThemeChangeUtil.changeColor() 142 | } 143 | NOW_WEEK_NUM -> { 144 | nowWeekNumEditTextPreference.summary = sharedPreferences.getString(key, "1") 145 | App.sharedPreferences.edit().putLong(ConstantPool.Str.NEXT_WEEK_OF_MONDAY.get(), getNextMondayOfTimeInMillis()).apply() 146 | EventBus.getDefault().post(EventEntity(ConstantPool.Int.TIME_TICK_CHANGE, "")) 147 | } 148 | } 149 | } 150 | 151 | companion object { 152 | private const val TAG = "SettingsFragment" 153 | const val CLASS_REMINDER_UP_TIME = "class_reminder_up_time" 154 | const val CLASS_REMINDER_DOWN_TIME = "class_reminder_down_time" 155 | const val PHONE_MUTE_STATUS = "phone_mute_status" 156 | const val PHONE_MUTE_BEFORE_TIME = "phone_mute_before_time" 157 | const val PHONE_MUTE_AFTER_TIME = "phone_mute_after_time" 158 | const val DEFAULT_SHOW_MAIN_FRAGMENT = "default_show_main_fragment" 159 | const val APP_COLOR_PRIMARY = "app_color_primary" 160 | const val APP_COLOR_PRIMARY_DARK = "app_color_primary_dark" 161 | const val APP_COLOR_ACCENT = "app_color_accent" 162 | const val APP_COLOR_PROGRESS = "app_color_progress" 163 | const val FOREGROUND_SERVICE_STATUS = "foreground_service_status" 164 | const val NOW_WEEK_NUM = "now_week_num" 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /app/src/main/java/top/itning/yunshuclassschedule/ui/activity/LoginActivity.kt: -------------------------------------------------------------------------------- 1 | package top.itning.yunshuclassschedule.ui.activity 2 | 3 | import android.app.Activity 4 | import android.content.ActivityNotFoundException 5 | import android.content.Intent 6 | import android.os.Bundle 7 | import android.util.Log 8 | import android.widget.Toast 9 | import androidx.appcompat.app.AlertDialog 10 | import androidx.preference.PreferenceManager 11 | import com.google.gson.Gson 12 | import com.jaeger.library.StatusBarUtil 13 | import kotlinx.android.synthetic.main.activity_login.* 14 | import org.greenrobot.eventbus.EventBus 15 | import org.greenrobot.eventbus.Subscribe 16 | import org.greenrobot.eventbus.ThreadMode 17 | import top.itning.yunshuclassschedule.R 18 | import top.itning.yunshuclassschedule.common.App 19 | import top.itning.yunshuclassschedule.common.BaseActivity 20 | import top.itning.yunshuclassschedule.common.ConstantPool 21 | import top.itning.yunshuclassschedule.entity.DataEntity 22 | import top.itning.yunshuclassschedule.entity.EventEntity 23 | import top.itning.yunshuclassschedule.ui.activity.ShareActivity.Companion.FILE_SELECT_CODE 24 | import top.itning.yunshuclassschedule.ui.fragment.setting.SettingsFragment 25 | import top.itning.yunshuclassschedule.util.DateUtils 26 | import top.itning.yunshuclassschedule.util.DateUtils.getNextMondayOfTimeInMillis 27 | import java.util.* 28 | 29 | /** 30 | * 登陆 31 | * 32 | * @author itning 33 | */ 34 | class LoginActivity : BaseActivity() { 35 | override fun onCreate(savedInstanceState: Bundle?) { 36 | super.onCreate(savedInstanceState) 37 | setContentView(R.layout.activity_login) 38 | EventBus.getDefault().register(this) 39 | StatusBarUtil.setTransparent(this) 40 | initData() 41 | btn_custom.setOnClickListener { onCustomBtnClicked() } 42 | btn_share.setOnClickListener { onViewClicked() } 43 | } 44 | 45 | /** 46 | * 加载专业数据 47 | */ 48 | private fun initData() { 49 | if (App.sharedPreferences.getBoolean(ConstantPool.Str.FIRST_IN_APP.get(), true)) { 50 | PreferenceManager.getDefaultSharedPreferences(this).edit() 51 | .putString(SettingsFragment.NOW_WEEK_NUM, "1") 52 | .apply() 53 | App.sharedPreferences.edit() 54 | .putLong(ConstantPool.Str.NEXT_WEEK_OF_MONDAY.get(), getNextMondayOfTimeInMillis()) 55 | .putString("1", "08:20-09:50") 56 | .putString("2", "10:05-11:35") 57 | .putString("3", "12:55-14:25") 58 | .putString("4", "14:40-16:10") 59 | .putString("5", "17:30-20:00") 60 | .putString("6", "20:05-20:08") 61 | .putString("7", "20:10-20:15") 62 | .putString("8", "20:18-20:20") 63 | .putString("9", "20:30-20:35") 64 | .putString("10", "20:40-20:45") 65 | .putString("11", "20:50-21:01") 66 | .putString("12", "21:10-21:15").apply() 67 | } 68 | } 69 | 70 | @Subscribe(threadMode = ThreadMode.MAIN) 71 | override fun onMessageEvent(eventEntity: EventEntity) { 72 | } 73 | 74 | override fun onDestroy() { 75 | EventBus.getDefault().unregister(this) 76 | super.onDestroy() 77 | } 78 | 79 | /** 80 | * 消息事件 81 | * 82 | * @param what what 83 | */ 84 | @Subscribe(threadMode = ThreadMode.MAIN) 85 | fun onMessageEvent(what: Int?) { 86 | Log.d(TAG, what!!.toString() + "") 87 | } 88 | 89 | /** 90 | * 自定义课程 91 | */ 92 | private fun onCustomBtnClicked() { 93 | startActivity(Intent(this, CustomActivity::class.java)) 94 | finish() 95 | } 96 | 97 | private fun onViewClicked() { 98 | val intent = Intent(Intent.ACTION_GET_CONTENT) 99 | intent.type = "*/*" 100 | intent.addCategory(Intent.CATEGORY_OPENABLE) 101 | try { 102 | startActivityForResult(Intent.createChooser(intent, "选择课程数据文件进行导入"), FILE_SELECT_CODE) 103 | } catch (e: ActivityNotFoundException) { 104 | Toast.makeText(this, "没有找到文件管理APP", Toast.LENGTH_SHORT).show() 105 | } 106 | 107 | } 108 | 109 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 110 | if (requestCode == FILE_SELECT_CODE && resultCode == Activity.RESULT_OK) { 111 | doImportFile(data!!) 112 | } 113 | super.onActivityResult(requestCode, resultCode, data) 114 | } 115 | 116 | private fun doImportFile(data: Intent) { 117 | try { 118 | val uri = data.data ?: run { 119 | Toast.makeText(this, "解析失败", Toast.LENGTH_LONG).show() 120 | return 121 | } 122 | Log.d(TAG, "File Uri: $uri") 123 | val openInputStream = contentResolver.openInputStream(uri) ?: run { 124 | Toast.makeText(this, "解析失败", Toast.LENGTH_LONG).show() 125 | return 126 | } 127 | val inputAsString = openInputStream.bufferedReader().use { it.readText() } 128 | val dataEntity = Gson().fromJson(inputAsString, DataEntity::class.java) 129 | val classScheduleList = dataEntity.classScheduleList 130 | val timeList = dataEntity.timeList 131 | if (classScheduleList == null || classScheduleList.isEmpty() || timeList == null || timeList.isEmpty()) { 132 | Toast.makeText(this, "解析失败", Toast.LENGTH_LONG).show() 133 | return 134 | } 135 | val timeListSize = timeList.size 136 | val classScheduleMaxSection = classScheduleList.map { it.section }.max() ?: 12 137 | if (timeListSize > 12 || classScheduleMaxSection > 12) { 138 | Toast.makeText(this, "最大课程数为12节课,解析失败", Toast.LENGTH_LONG).show() 139 | return 140 | } 141 | Log.d(TAG, "classScheduleMaxSection: $classScheduleMaxSection timeListSize: $timeListSize") 142 | val section = if (timeListSize < classScheduleMaxSection) { 143 | classScheduleMaxSection 144 | } else { 145 | timeListSize 146 | } 147 | AlertDialog.Builder(this) 148 | .setTitle("警告") 149 | .setMessage("即将导入课程数据,这会将原有课程信息清空,确定导入吗?") 150 | .setPositiveButton("确定") { _, _ -> 151 | val timeMap = TreeMap() 152 | for ((index, value) in timeList.withIndex()) { 153 | timeMap[index + 1] = value 154 | } 155 | if (!DateUtils.isDataLegitimate(timeMap, this)) { 156 | Toast.makeText(this, "解析失败", Toast.LENGTH_LONG).show() 157 | return@setPositiveButton 158 | } 159 | val edit = App.sharedPreferences.edit() 160 | for ((index, value) in timeList.withIndex()) { 161 | edit.putString((index + 1).toString(), value) 162 | } 163 | if (edit.commit()) { 164 | DateUtils.refreshTimeList() 165 | } else { 166 | Toast.makeText(this, "解析失败", Toast.LENGTH_LONG).show() 167 | return@setPositiveButton 168 | } 169 | val classScheduleDao = (application as App).daoSession.classScheduleDao 170 | classScheduleDao.deleteAll() 171 | classScheduleList.forEach { classScheduleDao.insert(it) } 172 | EventBus.getDefault().post(EventEntity(ConstantPool.Int.TIME_TICK_CHANGE, "")) 173 | if (classScheduleList.size.toLong() == classScheduleDao.count()) { 174 | if (App.sharedPreferences.edit() 175 | .putInt(ConstantPool.Str.CLASS_SECTION.get(), section) 176 | .commit()) { 177 | DateUtils.refreshTimeList() 178 | Toast.makeText(this, "导入成功", Toast.LENGTH_LONG).show() 179 | enterMainActivity() 180 | } else { 181 | Toast.makeText(this, "写入课程节数失败,请重试", Toast.LENGTH_LONG).show() 182 | } 183 | } else { 184 | Toast.makeText(this, "写入数据库失败,请重试", Toast.LENGTH_LONG).show() 185 | } 186 | } 187 | .setNegativeButton("取消", null) 188 | .show() 189 | } catch (e: Exception) { 190 | Log.e(TAG, " ", e) 191 | Toast.makeText(this, "解析失败", Toast.LENGTH_LONG).show() 192 | } 193 | } 194 | 195 | private fun enterMainActivity() { 196 | App.sharedPreferences.edit() 197 | .putBoolean(ConstantPool.Str.FIRST_IN_APP.get(), false) 198 | .apply() 199 | startActivity(Intent(this, MainActivity::class.java)) 200 | finish() 201 | } 202 | 203 | companion object { 204 | private const val TAG = "LoginActivity" 205 | } 206 | } 207 | --------------------------------------------------------------------------------