├── .gitignore ├── .idea ├── libraries │ └── Gradle__com_squareup_okhttp3_okhttp_3_11_0_jar.xml └── vcs.xml ├── CyxbsMobile_Android_Refactoring.iml ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro ├── release │ ├── output.json │ ├── widget-v1.0.apk │ └── widget-v1.1.apk └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── zia │ │ └── widget │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ │ └── com │ │ │ └── zia │ │ │ └── widget │ │ │ ├── bean │ │ │ ├── Config.java │ │ │ └── Course.kt │ │ │ ├── net │ │ │ ├── SSLSocketClient.java │ │ │ └── Service.kt │ │ │ ├── page │ │ │ ├── ConfigActivity.kt │ │ │ ├── MainActivity.kt │ │ │ ├── little │ │ │ │ └── LittleConfigActivity.kt │ │ │ ├── normal │ │ │ │ └── NormalConfigActivity.kt │ │ │ └── trans │ │ │ │ ├── TransConfig.kt │ │ │ │ └── TransConfigActivity.kt │ │ │ ├── util │ │ │ ├── AutoUpdateCourse.kt │ │ │ ├── FileUtil.kt │ │ │ ├── ShortcutsUtil.java │ │ │ ├── TimeUtil.kt │ │ │ ├── Util.kt │ │ │ ├── Version.kt │ │ │ └── downlaodUtil │ │ │ │ ├── DownLoaderHelper.java │ │ │ │ ├── DownloadListener.java │ │ │ │ ├── DownloadRunnable.java │ │ │ │ └── ReLocateUtil.java │ │ │ └── widget │ │ │ ├── little │ │ │ ├── BaseLittleWidget.kt │ │ │ ├── LittleTransWidget.kt │ │ │ └── LittleWidget.kt │ │ │ └── normal │ │ │ └── NormalWidget.kt │ └── res │ │ ├── drawable-v24 │ │ ├── widget_back.png │ │ ├── widget_bg_title.xml │ │ ├── widget_down.png │ │ ├── widget_fresh.png │ │ ├── widget_front.png │ │ ├── widget_ic_launcher_foreground.xml │ │ ├── widget_little_example.png │ │ ├── widget_little_trans_example.png │ │ ├── widget_normal_example.png │ │ └── widget_up.png │ │ ├── drawable │ │ ├── widget_back.png │ │ ├── widget_bg_button.xml │ │ ├── widget_bg_savebt.xml │ │ ├── widget_bg_title.xml │ │ ├── widget_down.png │ │ ├── widget_fresh.png │ │ ├── widget_front.png │ │ ├── widget_ic_launcher_background.xml │ │ ├── widget_ic_launcher_foreground.xml │ │ ├── widget_little_example.png │ │ ├── widget_little_trans_example.png │ │ ├── widget_normal_example.png │ │ └── widget_up.png │ │ ├── layout │ │ ├── widget_activity_config.xml │ │ ├── widget_activity_little_config.xml │ │ ├── widget_activity_main.xml │ │ ├── widget_activity_normal_config.xml │ │ ├── widget_activity_trans_config.xml │ │ ├── widget_little.xml │ │ ├── widget_little_trans.xml │ │ └── widget_normal.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ ├── file_path.xml │ │ ├── network_security_config.xml │ │ ├── widget_little.xml │ │ ├── widget_little_trans.xml │ │ └── widget_normal.xml │ └── test │ └── java │ └── com │ └── zia │ └── widget │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradlew ├── gradlew.bat ├── local.properties ├── readme.md ├── screenshots └── example1.png ├── settings.gradle └── widget.iml /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .idea 5 | /.idea 6 | /.idea/caches/build_file_checksums.ser 7 | /.idea/libraries 8 | /.idea/modules.xml 9 | /.idea/workspace.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /.idea/libraries/Gradle__com_squareup_okhttp3_okhttp_3_11_0_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CyxbsMobile_Android_Refactoring.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | build/* 3 | release/* 4 | /release/* 5 | *.iml 6 | .gradle 7 | /local.properties 8 | .idea 9 | /.idea 10 | /.idea/caches/build_file_checksums.ser 11 | /.idea/libraries 12 | /.idea/modules.xml 13 | /.idea/workspace.xml 14 | .DS_Store 15 | /captures 16 | .externalNativeBuild -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 28 9 | defaultConfig { 10 | applicationId "com.zia.widget" 11 | minSdkVersion 19 12 | targetSdkVersion 28 13 | versionCode 5 14 | versionName "1.4" 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled true 22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | 26 | packagingOptions { 27 | exclude 'META-INF/LICENSE.txt' 28 | exclude 'META-INF/NOTICE.txt' 29 | } 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | lintOptions { 37 | abortOnError false 38 | } 39 | } 40 | 41 | dependencies { 42 | implementation fileTree(include: ['*.jar'], dir: 'libs') 43 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 44 | implementation 'com.android.support:appcompat-v7:28.0.0' 45 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 46 | implementation 'com.android.support:support-v4:28.0.0' 47 | testImplementation 'junit:junit:4.12' 48 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 49 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 50 | implementation 'com.android.support:cardview-v7:28.0.0' 51 | implementation 'com.android.support:design:28.0.0' 52 | implementation 'com.jaredrummler:colorpicker:1.0.2' 53 | implementation 'com.google.code.gson:gson:2.8.5' 54 | implementation 'com.github.tbruyelle:rxpermissions:0.10.2' 55 | implementation 'com.tencent.bugly:crashreport_upgrade:1.3.5' 56 | implementation 'com.tencent.bugly:nativecrashreport:3.3.1' 57 | implementation 'com.squareup.okhttp3:okhttp:3.11.0' 58 | } 59 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/dayaa/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | #-------------------------------------------定制化区域---------------------------------------------- 20 | #---------------------------------1.实体类--------------------------------- 21 | -keep class com.zia.widget.bean.**{ *; } 22 | -keep class com.zia.widget.net.**{ *;} 23 | 24 | 25 | -keep public class * extends android.app.Fragment 26 | #-keepclasseswithmembernames class * { # 保持native方法不被混淆 27 | # native ; 28 | #} 29 | #------------------------------------------------------------------------- 30 | 31 | #---------------------------------2.第三方包------------------------------- 32 | 33 | -dontwarn org.simpleframework.xml.stream.** 34 | -keep class org.simpleframework.xml.** { *; } 35 | 36 | 37 | -dontwarn com.a.a.** 38 | -dontwarn com.autonavi.** 39 | -ignorewarnings 40 | 41 | 42 | -keep class com.autonavi.** {*;} 43 | -keep class com.a.a.** {*;} 44 | #-keepresourcexmlelements manifest/application/meta-data@value=GlideModule 45 | #okhttp with retrofit 46 | 47 | -keepattributes Signature 48 | -keepattributes *Annotation* 49 | -keep class com.squareup.okhttp3.** { *; } 50 | -keep interface com.squareup.okhttp3.** { *; } 51 | -dontwarn com.squareup.okhttp3.** 52 | 53 | 54 | -keep class okhttp3.** { *; } 55 | 56 | -keep interface okhttp3.** { *; } 57 | 58 | -dontwarn okhttp3.** 59 | 60 | -dontwarn rx.** 61 | -dontwarn retrofit2.** 62 | -keep class retrofit2.** { *; } 63 | -keepclasseswithmembers class * { 64 | @retrofit.http.* ; 65 | } 66 | 67 | -keep class sun.misc.Unsafe { *; } 68 | 69 | -dontwarn okio.** 70 | -dontwarn com.squareup.retrofit2.** 71 | -dontwarn retrofit.appengine.UrlFetchClient 72 | 73 | -keep class com.tbruyelle.rxpermissions.**{*;} 74 | -keep class rxpermissions.**{*;} 75 | -keep interface rxpermissions.**{*;} 76 | 77 | #your package path where your gson models are stored 78 | 79 | #gson 80 | -keepattributes Signature 81 | -keepattributes *Annotation* 82 | -keep class sun.misc.Unsafe { *; } 83 | -keep class com.google.gson.stream.** { *; } 84 | 85 | #rxjava 86 | -dontwarn sun.misc.** 87 | 88 | -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { 89 | long producerIndex; 90 | long consumerIndex; 91 | } 92 | 93 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { 94 | rx.internal.util.atomic.LinkedQueueNode producerNode; 95 | } 96 | 97 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { 98 | rx.internal.util.atomic.LinkedQueueNode consumerNode; 99 | } 100 | #rxandroid 101 | -keep class rx.schedulers.Schedulers { 102 | public static ; 103 | } 104 | -keep class rx.schedulers.ImmediateScheduler { 105 | public ; 106 | } 107 | -keep class rx.schedulers.TestScheduler { 108 | public ; 109 | } 110 | -keep class rx.schedulers.Schedulers { 111 | public static ** test(); 112 | } 113 | -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { 114 | long producerIndex; 115 | long consumerIndex; 116 | } 117 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { 118 | long producerNode; 119 | long consumerNode; 120 | } 121 | 122 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { 123 | rx.internal.util.atomic.LinkedQueueNode producerNode; 124 | } 125 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { 126 | rx.internal.util.atomic.LinkedQueueNode consumerNode; 127 | } 128 | -keepclassmembers class rx.internal.util.unsafe.** { 129 | long producerIndex; 130 | long consumerIndex; 131 | } 132 | -dontwarn rx.internal.util.unsafe.** 133 | 134 | #nineoldandroids 135 | -keep class com.nineoldandroids.** { *; } 136 | 137 | ##---------------End: proguard configuration for Gson ---------- 138 | 139 | #------------------------------------------------------------------------- 140 | 141 | #---------------------------------3.与js互相调用的类------------------------ 142 | 143 | 144 | 145 | #------------------------------------------------------------------------- 146 | 147 | #---------------------------------4.反射相关的类和方法----------------------- 148 | 149 | 150 | 151 | #---------------------------------------------------------------------------- 152 | #--------------------------------------------------------------------------------------------------- 153 | 154 | #-------------------------------------------基本不用动区域-------------------------------------------- 155 | #---------------------------------基本指令区---------------------------------- 156 | -optimizationpasses 5 157 | -dontusemixedcaseclassnames 158 | -dontskipnonpubliclibraryclasses 159 | -dontskipnonpubliclibraryclassmembers 160 | -dontpreverify 161 | -verbose 162 | -printmapping proguardMapping.txt 163 | -optimizations !code/simplification/cast,!field/*,!class/merging/* 164 | -keepattributes *Annotation*,InnerClasses 165 | -keepattributes Signature 166 | -keepattributes SourceFile,LineNumberTable 167 | #---------------------------------------------------------------------------- 168 | 169 | #---------------------------------默认保留区--------------------------------- 170 | -keep public class * extends android.app.Activity 171 | -keep public class * extends android.app.AppCompatActivity 172 | -keep public class * extends android.app.Application 173 | -keep public class * extends android.app.Service 174 | -keep public class * extends android.content.BroadcastReceiver 175 | -keep public class * extends android.content.ContentProvider 176 | -keep public class * extends android.app.backup.BackupAgentHelper 177 | -keep public class * extends android.preference.Preference 178 | -keep public class * extends android.view.View 179 | -keep public class com.android.vending.licensing.ILicensingService 180 | -keep class android.support.** {*;} 181 | 182 | -keepclasseswithmembernames class * { 183 | native ; 184 | } 185 | -keepclassmembers class * extends android.app.Activity{ 186 | public void *(android.view.View); 187 | } 188 | -keepclassmembers enum * { 189 | public static **[] values(); 190 | public static ** valueOf(java.lang.String); 191 | } 192 | 193 | -keep public class * extends android.view.View{ 194 | *** get*(); 195 | void set*(***); 196 | public (android.content.Context); 197 | public (android.content.Context, android.util.AttributeSet); 198 | public (android.content.Context, android.util.AttributeSet, int); 199 | } 200 | -keepclasseswithmembers class * { 201 | public (android.content.Context, android.util.AttributeSet); 202 | public (android.content.Context, android.util.AttributeSet, int); 203 | } 204 | -keep class * implements android.os.Parcelable { 205 | public static final android.os.Parcelable$Creator *; 206 | } 207 | -keepclassmembers class * implements java.io.Serializable { 208 | static final long serialVersionUID; 209 | private static final java.io.ObjectStreamField[] serialPersistentFields; 210 | private void writeObject(java.io.ObjectOutputStream); 211 | private void readObject(java.io.ObjectInputStream); 212 | java.lang.Object writeReplace(); 213 | java.lang.Object readResolve(); 214 | } 215 | -keep class **.R$* { 216 | *; 217 | } 218 | -keepclassmembers class * { 219 | void *(**On*Event); 220 | } 221 | #---------------------------------------------------------------------------- 222 | 223 | #---------------------------------webview------------------------------------ 224 | -keepclassmembers class fqcn.of.javascript.interface.for.Webview { 225 | public *; 226 | } 227 | -keepclassmembers class * extends android.webkit.WebViewClient { 228 | public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap); 229 | public boolean *(android.webkit.WebView, java.lang.String); 230 | } 231 | -keepclassmembers class * extends android.webkit.WebViewClient { 232 | public void *(android.webkit.WebView, jav.lang.String); 233 | } 234 | #---------------------------------------------------------------------------- 235 | #--------------------------------------------------------------------------------------------------- 236 | 237 | -dontwarn com.taobao.** 238 | -dontwarn anet.channel.** 239 | -dontwarn anetwork.channel.** 240 | -dontwarn org.android.** 241 | -dontwarn org.apache.thrift.** 242 | -dontwarn com.xiaomi.** 243 | -dontwarn com.huawei.** 244 | 245 | -keepattributes *Annotation* 246 | 247 | -keep class com.taobao.** {*;} 248 | -keep class org.android.** {*;} 249 | -keep class anet.channel.** {*;} 250 | -keep class com.umeng.** {*;} 251 | -keep class com.xiaomi.** {*;} 252 | -keep class com.huawei.** {*;} 253 | -keep class org.apache.thrift.** {*;} 254 | 255 | -keep class com.alibaba.sdk.android.**{*;} 256 | -keep class com.ut.**{*;} 257 | -keep class com.ta.**{*;} 258 | 259 | -keep public class **.R$*{ 260 | public static final int *; 261 | } 262 | 263 | -dontshrink 264 | -keep,allowshrinking class com.umeng.message.* { 265 | public ; 266 | public ; 267 | } 268 | -keep,allowshrinking class com.umeng.message.protobuffer.MessageResponse$PushResponse$Info { 269 | public ; 270 | public ; 271 | } 272 | -keep,allowshrinking class com.umeng.message.protobuffer.MessageResponse$PushResponse$Info$Builder { 273 | public ; 274 | public ; 275 | } 276 | -keep,allowshrinking class org.android.agoo.impl.*{ 277 | public ; 278 | public ; 279 | } 280 | -keep,allowshrinking class org.android.agoo.service.* {*;} 281 | -keep,allowshrinking class org.android.spdy.**{*;} 282 | -keep public class com.mredrock.cyxbs.R$*{ 283 | public static final int *; 284 | } 285 | -------------------------------------------------------------------------------- /app/release/output.json: -------------------------------------------------------------------------------- 1 | [{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":3,"versionName":"1.2","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] -------------------------------------------------------------------------------- /app/release/widget-v1.0.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/release/widget-v1.0.apk -------------------------------------------------------------------------------- /app/release/widget-v1.1.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/release/widget-v1.1.apk -------------------------------------------------------------------------------- /app/src/androidTest/java/com/zia/widget/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget 2 | 3 | import android.support.test.InstrumentationRegistry 4 | import android.support.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getTargetContext() 22 | assertEquals("com.mredrock.cyxbs.module_widget", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 28 | 29 | 32 | 33 | 36 | 37 | 40 | 41 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 57 | 58 | 61 | 62 | 68 | 72 | 73 | 74 | 77 | 78 | 79 | 80 | 81 | 84 | 85 | 88 | 89 | 90 | 91 | 92 | 95 | 96 | 99 | 100 | 101 | 102 | 103 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/bean/Config.java: -------------------------------------------------------------------------------- 1 | package com.zia.widget.bean; 2 | 3 | /** 4 | * Created By zia on 2018/10/13. 5 | */ 6 | public class Config { 7 | public String key; 8 | public int version; 9 | public String url; 10 | public String able; 11 | public String message; 12 | public String other; 13 | 14 | public Config(String key, int version, String url, String able, String message, String other) { 15 | this.key = key; 16 | this.version = version; 17 | this.url = url; 18 | this.able = able; 19 | this.message = message; 20 | this.other = other; 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return "Config{" + 26 | "key=" + key + 27 | ", version=" + version + 28 | ", url='" + url + '\'' + 29 | ", able=" + able + 30 | ", message='" + message + '\'' + 31 | ", other='" + other + '\'' + 32 | '}'; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/bean/Course.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.bean 2 | 3 | /** 4 | * Created by zia on 2018/10/9. 5 | */ 6 | class Course { 7 | 8 | /** 9 | * status : 200 10 | * success : true 11 | * version : 18.9.8 12 | * term : 2018-2019学年第1学期 13 | * stuNum : 2016211541 14 | * data : [{"hash_day":0,"hash_lesson":1,"begin_lesson":3,"day":"星期一","lesson":"三四节","course":"飞盘中级","course_num":"A1090030","teacher":"王燕妮","classroom":"待定","rawWeek":"1-15周单周","weekModel":"single","weekBegin":1,"weekEnd":15,"week":[1,3,5,7,9,11,13,15],"type":"必修","period":2},{"hash_day":0,"hash_lesson":2,"begin_lesson":5,"day":"星期一","lesson":"五六节","course":"嵌入式系统设计A","course_num":"A2040080","teacher":"龙林波","classroom":"3104","rawWeek":"2-13周,16周","weekModel":"all","weekBegin":2,"weekEnd":16,"week":[2,3,4,5,6,7,8,9,10,11,12,13,16],"type":"选修","period":2},{"hash_day":1,"hash_lesson":0,"begin_lesson":1,"day":"星期二","lesson":"一二节","course":"计算机图形学B","course_num":"A2040291","teacher":"魏秉铎","classroom":"3101","rawWeek":"3-13周,16-17周","weekModel":"all","weekBegin":3,"weekEnd":17,"week":[3,4,5,6,7,8,9,10,11,12,13,16,17],"type":"选修","period":2},{"hash_day":1,"hash_lesson":0,"begin_lesson":1,"day":"星期二","lesson":"一二节","course":"电装实习","course_num":"A2080010","teacher":"罗萍","classroom":"电力系统仿真实验室(综合实验楼A606/A607)","rawWeek":"1周","weekModel":"all","weekBegin":1,"weekEnd":1,"week":[1],"type":"必修","period":4},{"hash_day":1,"hash_lesson":2,"begin_lesson":5,"day":"星期二","lesson":"五六节","course":"计算机网络B","course_num":"A2040050","teacher":"黄俊(信息)","classroom":"3508","rawWeek":"2-13周,16-18周","weekModel":"all","weekBegin":2,"weekEnd":18,"week":[2,3,4,5,6,7,8,9,10,11,12,13,16,17,18],"type":"必修","period":2},{"hash_day":1,"hash_lesson":2,"begin_lesson":5,"day":"星期二","lesson":"五六节","course":"课程设计(计算机网络)","course_num":"A2040300","teacher":"杜欢","classroom":"数据科学与大数据技术实验室(综合实验楼B516/B517)","rawWeek":"18周","weekModel":"all","weekBegin":18,"weekEnd":18,"week":[18],"type":"必修","period":4},{"hash_day":1,"hash_lesson":2,"begin_lesson":5,"day":"星期二","lesson":"五六节","course":"课程设计(软件综合设计开发)","course_num":"A2040450","teacher":"刘伯红","classroom":"计算机系统实验室(综合实验楼B505/B506)","rawWeek":"19周","weekModel":"all","weekBegin":19,"weekEnd":19,"week":[19],"type":"必修","period":4},{"hash_day":1,"hash_lesson":2,"begin_lesson":5,"day":"星期二","lesson":"五六节","course":"金工实习B","course_num":"A2140010","teacher":"朱小均","classroom":"先进设计技术实训室207","rawWeek":"14周","weekModel":"all","weekBegin":14,"weekEnd":14,"week":[14],"type":"必修","period":4},{"hash_day":2,"hash_lesson":0,"begin_lesson":1,"day":"星期三","lesson":"一二节","course":"电装实习","course_num":"A2080010","teacher":"王大军","classroom":"PCB实训室(实训楼301)","rawWeek":"1周","weekModel":"all","weekBegin":1,"weekEnd":1,"week":[1],"type":"必修","period":4},{"hash_day":2,"hash_lesson":1,"begin_lesson":3,"day":"星期三","lesson":"三四节","course":"WEB动态网页设计","course_num":"A2040010","teacher":"刘立","classroom":"3101","rawWeek":"2-13周,16-18周","weekModel":"all","weekBegin":2,"weekEnd":18,"week":[2,3,4,5,6,7,8,9,10,11,12,13,16,17,18],"type":"选修","period":2},{"hash_day":2,"hash_lesson":2,"begin_lesson":5,"day":"星期三","lesson":"五六节","course":"课程设计(计算机网络)","course_num":"A2040300","teacher":"杜欢","classroom":"数据科学与大数据技术实验室(综合实验楼B516/B517)","rawWeek":"18周","weekModel":"all","weekBegin":18,"weekEnd":18,"week":[18],"type":"必修","period":4},{"hash_day":2,"hash_lesson":2,"begin_lesson":5,"day":"星期三","lesson":"五六节","course":"课程设计(软件综合设计开发)","course_num":"A2040450","teacher":"刘伯红","classroom":"计算机系统实验室(综合实验楼B505/B506)","rawWeek":"19周","weekModel":"all","weekBegin":19,"weekEnd":19,"week":[19],"type":"必修","period":4},{"hash_day":2,"hash_lesson":2,"begin_lesson":5,"day":"星期三","lesson":"五六节","course":"金工实习B","course_num":"A2140010","teacher":"朱小均","classroom":"先进设计技术实训室207","rawWeek":"14周","weekModel":"all","weekBegin":14,"weekEnd":14,"week":[14],"type":"必修","period":4},{"hash_day":2,"hash_lesson":3,"begin_lesson":7,"day":"星期三","lesson":"七八节","course":"数据挖掘基础B","course_num":"A2040501","teacher":"刘群","classroom":"3207","rawWeek":"2-13周,16-17周","weekModel":"all","weekBegin":2,"weekEnd":17,"week":[2,3,4,5,6,7,8,9,10,11,12,13,16,17],"type":"选修","period":2},{"hash_day":3,"hash_lesson":0,"begin_lesson":1,"day":"星期四","lesson":"一二节","course":"计算机网络B","course_num":"A2040050","teacher":"黄俊(信息)","classroom":"3508","rawWeek":"3-13周单周,17周","weekModel":"single","weekBegin":3,"weekEnd":17,"week":[3,5,7,9,11,13,17],"type":"必修","period":2},{"hash_day":3,"hash_lesson":0,"begin_lesson":1,"day":"星期四","lesson":"一二节","course":"电装实习","course_num":"A2080010","teacher":"陈绍明","classroom":"SMT实训室(实训楼302)","rawWeek":"1周","weekModel":"all","weekBegin":1,"weekEnd":1,"week":[1],"type":"必修","period":4},{"hash_day":3,"hash_lesson":1,"begin_lesson":3,"day":"星期四","lesson":"三四节","course":"嵌入式系统设计A","course_num":"A2040080","teacher":"曾素华","classroom":"智能终端实验室(综合实验楼B509/B510)","rawWeek":"9-16周","weekModel":"all","weekBegin":9,"weekEnd":16,"week":[9,10,11,12,13,14,15,16],"type":"选修","period":2},{"hash_day":3,"hash_lesson":2,"begin_lesson":5,"day":"星期四","lesson":"五六节","course":"课程设计(计算机网络)","course_num":"A2040300","teacher":"杜欢","classroom":"数据科学与大数据技术实验室(综合实验楼B516/B517)","rawWeek":"18周","weekModel":"all","weekBegin":18,"weekEnd":18,"week":[18],"type":"必修","period":4},{"hash_day":3,"hash_lesson":2,"begin_lesson":5,"day":"星期四","lesson":"五六节","course":"课程设计(软件综合设计开发)","course_num":"A2040450","teacher":"刘伯红","classroom":"计算机系统实验室(综合实验楼B505/B506)","rawWeek":"19周","weekModel":"all","weekBegin":19,"weekEnd":19,"week":[19],"type":"必修","period":4},{"hash_day":3,"hash_lesson":2,"begin_lesson":5,"day":"星期四","lesson":"五六节","course":"金工实习B","course_num":"A2140010","teacher":"朱小均","classroom":"先进设计技术实训室207","rawWeek":"14周","weekModel":"all","weekBegin":14,"weekEnd":14,"week":[14],"type":"必修","period":4},{"hash_day":3,"hash_lesson":4,"begin_lesson":9,"day":"星期四","lesson":"九十节","course":"嵌入式系统设计A","course_num":"A2040080","teacher":"龙林波","classroom":"3104","rawWeek":"2-12周双周,16周","weekModel":"double","weekBegin":2,"weekEnd":16,"week":[2,4,6,8,10,12,16],"type":"选修","period":2},{"hash_day":4,"hash_lesson":0,"begin_lesson":1,"day":"星期五","lesson":"一二节","course":"中国近现代史纲要","course_num":"A1100040","teacher":"邓庆伟","classroom":"3301","rawWeek":"2-11周","weekModel":"all","weekBegin":2,"weekEnd":11,"week":[2,3,4,5,6,7,8,9,10,11],"type":"必修","period":2},{"hash_day":4,"hash_lesson":0,"begin_lesson":1,"day":"星期五","lesson":"一二节","course":"课程设计(计算机网络)","course_num":"A2040300","teacher":"杜欢","classroom":"数据科学与大数据技术实验室(综合实验楼B516/B517)","rawWeek":"18周","weekModel":"all","weekBegin":18,"weekEnd":18,"week":[18],"type":"必修","period":4},{"hash_day":4,"hash_lesson":0,"begin_lesson":1,"day":"星期五","lesson":"一二节","course":"课程设计(软件综合设计开发)","course_num":"A2040450","teacher":"刘伯红","classroom":"计算机系统实验室(综合实验楼B505/B506)","rawWeek":"19周","weekModel":"all","weekBegin":19,"weekEnd":19,"week":[19],"type":"必修","period":4},{"hash_day":4,"hash_lesson":0,"begin_lesson":1,"day":"星期五","lesson":"一二节","course":"电装实习","course_num":"A2080010","teacher":"林海波","classroom":"电装实训室(先进制造实训楼A303)","rawWeek":"1周","weekModel":"all","weekBegin":1,"weekEnd":1,"week":[1],"type":"必修","period":4},{"hash_day":4,"hash_lesson":2,"begin_lesson":5,"day":"星期五","lesson":"五六节","course":"形势与政策","course_num":"A1100010","teacher":"郑兴刚","classroom":"2402","rawWeek":"5-8周","weekModel":"all","weekBegin":5,"weekEnd":8,"week":[5,6,7,8],"type":"必修","period":2},{"hash_day":4,"hash_lesson":2,"begin_lesson":5,"day":"星期五","lesson":"五六节","course":"金工实习B","course_num":"A2140010","teacher":"朱小均","classroom":"先进设计技术实训室207","rawWeek":"14周","weekModel":"all","weekBegin":14,"weekEnd":14,"week":[14],"type":"必修","period":4}] 15 | * cachedTimestamp : 1539089073698 16 | * outOfDateTimestamp : 1539693873698 17 | * nowWeek : 5 18 | */ 19 | 20 | var status: Int = 0 21 | var isSuccess: Boolean = false 22 | var version: String? = null 23 | var term: String? = null 24 | var stuNum: String? = null 25 | var cachedTimestamp: Long = 0 26 | var outOfDateTimestamp: Long = 0 27 | var nowWeek: Int = 0 28 | var data: List? = null 29 | 30 | override fun toString(): String { 31 | return "Course{" + 32 | "status=" + status + 33 | ", success=" + isSuccess + 34 | ", version='" + version + '\''.toString() + 35 | ", term='" + term + '\''.toString() + 36 | ", stuNum='" + stuNum + '\''.toString() + 37 | ", cachedTimestamp=" + cachedTimestamp + 38 | ", outOfDateTimestamp=" + outOfDateTimestamp + 39 | ", nowWeek=" + nowWeek + 40 | ", data=" + data + 41 | '}'.toString() 42 | } 43 | 44 | class DataBean { 45 | /** 46 | * hash_day : 0 47 | * hash_lesson : 1 48 | * begin_lesson : 3 49 | * day : 星期一 50 | * lesson : 三四节 51 | * course : 飞盘中级 52 | * course_num : A1090030 53 | * teacher : 王燕妮 54 | * classroom : 待定 55 | * rawWeek : 1-15周单周 56 | * weekModel : single 57 | * weekBegin : 1 58 | * weekEnd : 15 59 | * week : [1,3,5,7,9,11,13,15] 60 | * type : 必修 61 | * period : 2 62 | */ 63 | 64 | var hash_day: Int = 0 65 | var hash_lesson: Int = 0 66 | var begin_lesson: Int = 0 67 | var day: String? = null 68 | var lesson: String? = null 69 | var course: String? = null 70 | var course_num: String? = null 71 | var teacher: String? = null 72 | var classroom: String? = null 73 | var rawWeek: String? = null 74 | var weekModel: String? = null 75 | var weekBegin: Int = 0 76 | var weekEnd: Int = 0 77 | var type: String? = null 78 | var period: Int = 0 79 | var week: List? = null 80 | 81 | override fun toString(): String { 82 | return "DataBean{" + 83 | "hash_day=" + hash_day + 84 | ", hash_lesson=" + hash_lesson + 85 | ", begin_lesson=" + begin_lesson + 86 | ", day='" + day + '\''.toString() + 87 | ", lesson='" + lesson + '\''.toString() + 88 | ", course='" + course + '\''.toString() + 89 | ", course_num='" + course_num + '\''.toString() + 90 | ", classroom='" + classroom + '\''.toString() + 91 | ", rawWeek='" + rawWeek + '\''.toString() + 92 | ", weekModel='" + weekModel + '\''.toString() + 93 | ", weekBegin=" + weekBegin + 94 | ", weekEnd=" + weekEnd + 95 | ", type='" + type + '\''.toString() + 96 | ", period=" + period + 97 | ", week=" + week + 98 | '}'.toString() 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/net/SSLSocketClient.java: -------------------------------------------------------------------------------- 1 | package com.zia.widget.net; 2 | 3 | import java.security.SecureRandom; 4 | import java.security.cert.X509Certificate; 5 | 6 | import javax.net.ssl.HostnameVerifier; 7 | import javax.net.ssl.SSLContext; 8 | import javax.net.ssl.SSLSocketFactory; 9 | import javax.net.ssl.TrustManager; 10 | import javax.net.ssl.X509TrustManager; 11 | 12 | /** 13 | * 忽略https证书验证 14 | */ 15 | 16 | public class SSLSocketClient { 17 | //获取这个SSLSocketFactory 18 | public static SSLSocketFactory getSSLSocketFactory() { 19 | try { 20 | SSLContext sslContext = SSLContext.getInstance("SSL"); 21 | sslContext.init(null, getTrustManager(), new SecureRandom()); 22 | return sslContext.getSocketFactory(); 23 | } catch (Exception e) { 24 | throw new RuntimeException(e); 25 | } 26 | } 27 | 28 | //获取TrustManager 29 | private static TrustManager[] getTrustManager() { 30 | return new TrustManager[]{new X509TrustManager() { 31 | @Override 32 | public void checkClientTrusted(X509Certificate[] chain, String authType) { 33 | } 34 | 35 | @Override 36 | public void checkServerTrusted(X509Certificate[] chain, String authType) { 37 | } 38 | 39 | @Override 40 | public X509Certificate[] getAcceptedIssuers() { 41 | return new X509Certificate[]{}; 42 | } 43 | }}; 44 | } 45 | 46 | //获取HostnameVerifier 47 | public static HostnameVerifier getHostnameVerifier() { 48 | return (s, sslSession) -> true; 49 | } 50 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/net/Service.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.net 2 | 3 | import okhttp3.OkHttpClient 4 | import java.util.concurrent.TimeUnit 5 | 6 | /** 7 | * Created by zia on 2018/10/16. 8 | */ 9 | object Service { 10 | val okHttpClient = OkHttpClient.Builder() 11 | .sslSocketFactory(SSLSocketClient.getSSLSocketFactory()) 12 | .hostnameVerifier(SSLSocketClient.getHostnameVerifier()) 13 | .connectTimeout(5, TimeUnit.SECONDS) 14 | .writeTimeout(10, TimeUnit.SECONDS) 15 | .readTimeout(20, TimeUnit.SECONDS) 16 | .build() 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/page/ConfigActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.page 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import android.support.v7.app.AppCompatActivity 6 | import com.zia.widget.R 7 | import com.zia.widget.page.little.LittleConfigActivity 8 | import com.zia.widget.page.normal.NormalConfigActivity 9 | import com.zia.widget.page.trans.TransConfigActivity 10 | import kotlinx.android.synthetic.main.widget_activity_config.* 11 | 12 | 13 | /** 14 | * Created by zia on 2018/10/11. 15 | * 设置主页面 16 | */ 17 | class ConfigActivity : AppCompatActivity() { 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | setContentView(R.layout.widget_activity_config) 22 | 23 | widget_config_littleTransLayout.setOnClickListener { 24 | startActivity(Intent(this@ConfigActivity, TransConfigActivity::class.java)) 25 | } 26 | 27 | widget_config_littleLayout.setOnClickListener { 28 | startActivity(Intent(this@ConfigActivity, LittleConfigActivity::class.java)) 29 | } 30 | 31 | widget_config_normalLayout.setOnClickListener { 32 | startActivity(Intent(this@ConfigActivity, NormalConfigActivity::class.java)) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/page/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.page 2 | 3 | import android.Manifest 4 | import android.annotation.SuppressLint 5 | import android.app.ProgressDialog 6 | import android.content.ClipData 7 | import android.content.ClipboardManager 8 | import android.content.Intent 9 | import android.content.pm.ShortcutInfo 10 | import android.graphics.drawable.Icon 11 | import android.net.Uri 12 | import android.os.Build 13 | import android.os.Bundle 14 | import android.os.Environment 15 | import android.support.v7.app.AlertDialog 16 | import android.support.v7.app.AppCompatActivity 17 | import android.widget.Toast 18 | import com.google.gson.Gson 19 | import com.tbruyelle.rxpermissions2.RxPermissions 20 | import com.tencent.bugly.crashreport.CrashReport 21 | import com.zia.widget.R 22 | import com.zia.widget.bean.Config 23 | import com.zia.widget.bean.Course 24 | import com.zia.widget.net.Service 25 | import com.zia.widget.util.* 26 | import com.zia.widget.util.downlaodUtil.DownloadRunnable 27 | import com.zia.widget.widget.little.LittleTransWidget 28 | import com.zia.widget.widget.little.LittleWidget 29 | import com.zia.widget.widget.normal.NormalWidget 30 | import kotlinx.android.synthetic.main.widget_activity_main.* 31 | import okhttp3.* 32 | import java.io.File 33 | import java.io.IOException 34 | 35 | 36 | /** 37 | * Created by zia on 2018/10/10. 38 | * 小部件独立模块的界面,用于登录,验证 39 | */ 40 | class MainActivity : AppCompatActivity() { 41 | 42 | private lateinit var rxPermissions: RxPermissions 43 | 44 | @SuppressLint("SetTextI18n") 45 | override fun onCreate(savedInstanceState: Bundle?) { 46 | super.onCreate(savedInstanceState) 47 | setContentView(R.layout.widget_activity_main) 48 | 49 | CrashReport.initCrashReport(applicationContext) 50 | 51 | addShortcut() 52 | 53 | rxPermissions = RxPermissions(this@MainActivity) 54 | 55 | widget_main_version.text = "v${Version.packageName(this)}" 56 | 57 | checkVersion() 58 | 59 | widget_main_configBt.setOnClickListener { startActivity(Intent(this@MainActivity, ConfigActivity::class.java)) } 60 | 61 | widget_main_joinGroup.setOnClickListener { joinQQ() } 62 | 63 | initViews() 64 | 65 | val needFresh = intent.getBooleanExtra("needFresh", false) 66 | if (needFresh) { 67 | Toast.makeText(this, "正在更新", Toast.LENGTH_SHORT).show() 68 | refresh(defaultSharedPreferences.getString(SP_STUNUM, "")) 69 | } 70 | 71 | //修复自动更新bug,设置为不自动更新 72 | defaultSharedPreferences.editor { 73 | putBoolean(SP_IS_AUTO_UPDATE, false) 74 | } 75 | } 76 | 77 | private fun checkVersion() { 78 | val versionRequest = Request.Builder() 79 | .url("http://zzzia.net:8080/version/get") 80 | .post(FormBody.Builder().add("key", "widget").build()) 81 | .build() 82 | 83 | showWaitDialog() 84 | Service.okHttpClient.newCall(versionRequest).enqueue(object : Callback { 85 | override fun onFailure(call: Call, e: IOException) { 86 | runOnUiThread { 87 | hideWaitDialog() 88 | e.printStackTrace() 89 | showErrorDialog("连接服务器失败,请检查网络") 90 | } 91 | } 92 | 93 | override fun onResponse(call: Call, response: Response) { 94 | val json = response.body()?.string() 95 | 96 | runOnUiThread { 97 | hideWaitDialog() 98 | val config = Gson().fromJson(json, Config::class.java) 99 | if (config == null || config.able != "true") { 100 | //服务器认证失败,无法访问 101 | showErrorDialog("由于某些原因,软件不再提供使用") 102 | } else if (config.version > Version.packageCode(this@MainActivity)) { 103 | showUpdateDialog(config) 104 | } 105 | } 106 | } 107 | 108 | }) 109 | } 110 | 111 | private fun initViews() { 112 | //填充学号记录 113 | val stuNumHistory = defaultSharedPreferences.getString(SP_STUNUM, "") 114 | widget_main_stuNumEt.setText(stuNumHistory) 115 | //填充自动更新记录 116 | // val isAutoUpdate = defaultSharedPreferences.getBoolean(SP_IS_AUTO_UPDATE, false) 117 | // widget_main_autoUpdate_check.isChecked = isAutoUpdate 118 | // 119 | // widget_main_autoUpdate_check.setOnTouchListener { _, _ -> 120 | // return@setOnTouchListener false 121 | // } 122 | 123 | //自动更新点击事件 124 | // widget_main_autoUpdate_layout.setOnClickListener { 125 | // val checked = !widget_main_autoUpdate_check.isChecked 126 | // widget_main_autoUpdate_check.isChecked = checked 127 | // Log.d(javaClass.simpleName, "checked:$checked") 128 | // defaultSharedPreferences.editor { 129 | // putBoolean(SP_IS_AUTO_UPDATE, checked) 130 | // } 131 | // if (checked) { 132 | // autoFreshCourse(this) 133 | // } 134 | // } 135 | 136 | //刷新课表点击事件 137 | widget_main_loginBt.setOnClickListener { 138 | 139 | val stuNum = widget_main_stuNumEt.text.toString() 140 | 141 | if (stuNum.isEmpty() || stuNum.length < 4) { 142 | Toast.makeText(this, "请输入学号", Toast.LENGTH_SHORT).show() 143 | return@setOnClickListener 144 | } 145 | 146 | refresh(stuNum) 147 | } 148 | } 149 | 150 | private fun refresh(stuNum: String?) { 151 | if (stuNum == null || stuNum.isEmpty()) { 152 | Toast.makeText(this, "还没有填写账号", Toast.LENGTH_SHORT).show() 153 | return 154 | } 155 | val courseRequest = Request.Builder() 156 | .url("https://wx.idsbllp.cn/api/kebiao") 157 | .post(FormBody.Builder().add("stuNum", stuNum).build()) 158 | .build() 159 | 160 | showWaitDialog() 161 | 162 | Service.okHttpClient.newCall(courseRequest).enqueue(object : Callback { 163 | override fun onFailure(call: Call, e: IOException) { 164 | runOnUiThread { 165 | hideWaitDialog() 166 | e.printStackTrace() 167 | Toast.makeText(this@MainActivity, "网络出错", Toast.LENGTH_SHORT).show() 168 | } 169 | } 170 | 171 | override fun onResponse(call: Call, response: Response) { 172 | val json = response.body()?.string() 173 | val course: Course 174 | try { 175 | //服务器数据有问题时json解析会出错 176 | course = Gson().fromJson(json, Course::class.java) 177 | } catch (e: java.lang.Exception) { 178 | Toast.makeText(this@MainActivity, "服务器数据出了点问题..", Toast.LENGTH_SHORT).show() 179 | return 180 | } 181 | 182 | runOnUiThread { 183 | hideWaitDialog() 184 | if (course.data == null) { 185 | Toast.makeText(this@MainActivity, "服务器没有课表...", Toast.LENGTH_SHORT).show() 186 | return@runOnUiThread 187 | } 188 | defaultSharedPreferences.editor { 189 | putString(WIDGET_COURSE, json) 190 | putBoolean(SP_WIDGET_NEED_FRESH, true) 191 | putString(SP_STUNUM, stuNum) 192 | } 193 | LittleTransWidget().refresh(this@MainActivity) 194 | LittleWidget().refresh(this@MainActivity) 195 | NormalWidget().fresh(this@MainActivity, 0) 196 | Toast.makeText(this@MainActivity, "更新成功", Toast.LENGTH_SHORT).show() 197 | } 198 | } 199 | 200 | }) 201 | } 202 | 203 | private fun showUpdateDialog(config: Config) { 204 | AlertDialog.Builder(this) 205 | .setTitle("是否更新版本") 206 | .setMessage(config.message) 207 | .setNegativeButton("取消", null) 208 | .setPositiveButton("更新") { _, _ -> 209 | rxPermissions 210 | .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) 211 | .subscribe { 212 | if (it) { 213 | downloadApk(config.url) 214 | } else { 215 | Toast.makeText(this@MainActivity, "请提供文件读写权限..", Toast.LENGTH_SHORT).show() 216 | } 217 | } 218 | } 219 | .setCancelable(true) 220 | .show() 221 | } 222 | 223 | private fun downloadApk(url: String) { 224 | val dialog = ProgressDialog(this@MainActivity) 225 | dialog.setCancelable(false) 226 | dialog.progress = 0 227 | dialog.setTitle("正在下载") 228 | dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL) 229 | dialog.show() 230 | 231 | val apkName = "widget.apk" 232 | val savePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path 233 | 234 | val downloadRunnable = DownloadRunnable(url, savePath, apkName) { ratio, part, total -> 235 | runOnUiThread { 236 | if (ratio == 100F) { 237 | dialog.dismiss() 238 | val intent = Intent(Intent.ACTION_VIEW) 239 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 240 | intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION 241 | } else { 242 | intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK 243 | } 244 | intent.setDataAndType(FileUtil.getFileUri(this@MainActivity, File(savePath, apkName)), 245 | "application/vnd.android.package-archive") 246 | this@MainActivity.startActivity(intent) 247 | return@runOnUiThread 248 | } 249 | dialog.progress = ratio.toInt() 250 | dialog.setProgressNumberFormat(String.format("%.2fm / %.2fm", part / 1024f / 1024f, total / 1024f / 1024f)) 251 | } 252 | } 253 | 254 | Thread(downloadRunnable).start() 255 | } 256 | 257 | private fun showErrorDialog(massage: String) { 258 | AlertDialog.Builder(this) 259 | .setTitle("提示") 260 | .setMessage(massage) 261 | .setPositiveButton("确定") { _, _ -> finish() } 262 | .setCancelable(false) 263 | .show() 264 | } 265 | 266 | private val waitDialog by lazy { 267 | AlertDialog.Builder(this@MainActivity) 268 | .setTitle("请稍等") 269 | .setMessage("正在连接服务器") 270 | .setCancelable(false) 271 | .create() 272 | } 273 | 274 | private fun showWaitDialog() { 275 | if (!waitDialog.isShowing) { 276 | waitDialog.show() 277 | } 278 | } 279 | 280 | private fun hideWaitDialog() { 281 | if (waitDialog.isShowing) { 282 | waitDialog.dismiss() 283 | } 284 | } 285 | 286 | private fun joinQQGroup(key: String): Boolean { 287 | val intent = Intent() 288 | intent.data = Uri.parse("mqqopensdkapi://bizAgent/qm/qr?url=http%3A%2F%2Fqm.qq.com%2Fcgi-bin%2Fqm%2Fqr%3Ffrom%3Dapp%26p%3Dandroid%26k%3D$key") 289 | // 此Flag可根据具体产品需要自定义,如设置,则在加群界面按返回,返回手Q主界面,不设置,按返回会返回到呼起产品界面 //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 290 | return try { 291 | startActivity(intent) 292 | true 293 | } catch (e: Exception) { 294 | // 未安装手Q或安装的版本不支持 295 | false 296 | } 297 | } 298 | 299 | private fun joinQQ() { 300 | if (!joinQQGroup("DXvamN9Ox1Kthaab1N_0w7s5N3aUYVIf")) { 301 | val clipboard = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager 302 | val data = ClipData.newPlainText("QQ Group", "570919844") 303 | clipboard.primaryClip = data 304 | Toast.makeText(this, "抱歉,由于您未安装手机QQ或版本不支持,无法跳转至掌邮bug反馈群。" + "已将群号复制至您的手机剪贴板,请您手动添加", 305 | Toast.LENGTH_LONG).show() 306 | } 307 | } 308 | 309 | private fun addShortcut() { 310 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { 311 | val intent = Intent(this, MainActivity::class.java) 312 | intent.action = Intent.ACTION_VIEW 313 | intent.putExtra("needFresh", true) 314 | val shortcut = ShortcutInfo.Builder(this, "update") 315 | .setShortLabel("刷新课表") 316 | .setLongLabel("刷新课表") 317 | .setRank(1) 318 | .setIcon(Icon.createWithResource(this, R.mipmap.ic_launcher_round)) 319 | .setIntent(intent) 320 | .build() 321 | ShortcutsUtil.addShortcut(this, shortcut) 322 | } 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/page/little/LittleConfigActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.page.little 2 | 3 | import android.support.v7.app.AppCompatActivity 4 | import android.os.Bundle 5 | import com.zia.widget.R 6 | 7 | class LittleConfigActivity : AppCompatActivity() { 8 | 9 | override fun onCreate(savedInstanceState: Bundle?) { 10 | super.onCreate(savedInstanceState) 11 | setContentView(R.layout.widget_activity_little_config) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/page/normal/NormalConfigActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.page.normal 2 | 3 | import android.support.v7.app.AppCompatActivity 4 | import android.os.Bundle 5 | import com.zia.widget.R 6 | 7 | class NormalConfigActivity : AppCompatActivity() { 8 | 9 | override fun onCreate(savedInstanceState: Bundle?) { 10 | super.onCreate(savedInstanceState) 11 | setContentView(R.layout.widget_activity_normal_config) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/page/trans/TransConfig.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.page.trans 2 | 3 | import android.content.Context 4 | import android.support.annotation.ColorInt 5 | import com.zia.widget.util.editor 6 | import com.zia.widget.util.sharedPreferences 7 | 8 | /** 9 | * Created by zia on 2018/10/11. 10 | */ 11 | class TransConfig { 12 | 13 | private val shareName = "cyxbs_widget_trans_config" 14 | 15 | var timeTextSize = 15 16 | var courseTextSize = 19 17 | var roomTextSize = 15 18 | 19 | @ColorInt 20 | var timeTextColor = "#FFFFFF" 21 | 22 | @ColorInt 23 | var courseTextColor = "#FFFFFF" 24 | 25 | @ColorInt 26 | var roomTextColor = "#FFFFFF" 27 | 28 | @ColorInt 29 | var holderColor = "#FFFFFF" 30 | 31 | fun save(context: Context) { 32 | context.sharedPreferences(shareName).editor { 33 | putInt("timeTextSize", timeTextSize) 34 | putInt("courseTextSize", courseTextSize) 35 | putInt("roomTextSize", roomTextSize) 36 | putString("timeTextColor", timeTextColor) 37 | putString("courseTextColor", courseTextColor) 38 | putString("roomTextColor", roomTextColor) 39 | putString("holderColor", holderColor) 40 | } 41 | } 42 | 43 | override fun toString(): String { 44 | return "TransConfig(timeTextSize=$timeTextSize, courseTextSize=$courseTextSize, roomTextSize=$roomTextSize, timeTextColor='$timeTextColor', courseTextColor='$courseTextColor', roomTextColor='$roomTextColor', holderColor='$holderColor')" 45 | } 46 | 47 | 48 | companion object { 49 | 50 | fun getUserConfig(context: Context): TransConfig { 51 | return TransConfig().apply { 52 | timeTextColor = context.sharedPreferences(shareName).getString("timeTextColor", timeTextColor) 53 | courseTextColor = context.sharedPreferences(shareName).getString("courseTextColor", courseTextColor) 54 | roomTextColor = context.sharedPreferences(shareName).getString("roomTextColor", roomTextColor) 55 | holderColor = context.sharedPreferences(shareName).getString("holderColor", holderColor) 56 | timeTextSize = context.sharedPreferences(shareName).getInt("timeTextSize", timeTextSize) 57 | courseTextSize = context.sharedPreferences(shareName).getInt("courseTextSize", courseTextSize) 58 | roomTextSize = context.sharedPreferences(shareName).getInt("roomTextSize", roomTextSize) 59 | } 60 | } 61 | 62 | fun getDefaultWhite(): TransConfig { 63 | return TransConfig() 64 | } 65 | 66 | fun getDefaultBlack(): TransConfig { 67 | return TransConfig().apply { 68 | timeTextColor = "#000000" 69 | courseTextColor = "#000000" 70 | roomTextColor = "#000000" 71 | holderColor = "#E7E7E7" 72 | } 73 | } 74 | 75 | fun getDefaultPink(): TransConfig { 76 | return TransConfig().apply { 77 | timeTextColor = "#F1AAA6" 78 | courseTextColor = "#FF6E97" 79 | roomTextColor = "#FF6E97" 80 | holderColor = "#E2E2E2" 81 | } 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/page/trans/TransConfigActivity.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.page.trans 2 | 3 | import android.graphics.Color 4 | import android.os.Bundle 5 | import android.support.v7.app.AppCompatActivity 6 | import android.text.Editable 7 | import android.text.TextWatcher 8 | import android.view.View 9 | import android.widget.EditText 10 | import android.widget.SeekBar 11 | import android.widget.TextView 12 | import android.widget.Toast 13 | import com.jaredrummler.android.colorpicker.ColorPickerDialog 14 | import com.jaredrummler.android.colorpicker.ColorPickerDialogListener 15 | import com.zia.widget.R 16 | import com.zia.widget.widget.little.LittleTransWidget 17 | import kotlinx.android.synthetic.main.widget_activity_trans_config.* 18 | 19 | 20 | /** 21 | * Created by zzzia on 2018/10/11. 22 | * 透明版设置 23 | * 写了一天辣鸡代码,等你们优化了 24 | */ 25 | class TransConfigActivity : AppCompatActivity() { 26 | 27 | private val userConfig by lazy { 28 | TransConfig.getUserConfig(this@TransConfigActivity) 29 | } 30 | 31 | override fun onCreate(savedInstanceState: Bundle?) { 32 | super.onCreate(savedInstanceState) 33 | setContentView(R.layout.widget_activity_trans_config) 34 | 35 | //从数据库读配置文件并复制给所有控件 36 | setAll(userConfig) 37 | 38 | setEditListener() 39 | 40 | //设置颜色选择的点击事件,联动 41 | setColorPicker(widget_config_trans_courseColorLayout, widget_config_trans_courseColor, widget_config_trans_courseTv, widget_config_trans_courseColorEt) 42 | setColorPicker(widget_config_trans_timeColorLayout, widget_config_trans_timeColor, widget_config_trans_timeTv, widget_config_trans_timeColorEt) 43 | setColorPicker(widget_config_trans_roomColorLayout, widget_config_trans_roomColor, widget_config_trans_roomTv, widget_config_trans_roomColorEt) 44 | setColorPicker(widget_config_trans_holderColorLayout, widget_config_trans_holderColor, null, widget_config_trans_holderColorEt) 45 | 46 | //预设方案的点击事件 47 | widget_config_trans_default_black.setOnClickListener { setAll(TransConfig.getDefaultBlack()) } 48 | widget_config_trans_default_white.setOnClickListener { setAll(TransConfig.getDefaultWhite()) } 49 | widget_config_trans_default_pink.setOnClickListener { setAll(TransConfig.getDefaultPink()) } 50 | 51 | //保存按钮监听 52 | widget_config_trans_saveBt.setOnClickListener { 53 | val timeSize = widget_config_trans_timeSb.progress 54 | val courseSize = widget_config_trans_courseSb.progress 55 | val roomSize = widget_config_trans_roomSb.progress 56 | 57 | val timeColor = widget_config_trans_timeColorEt.text.toString().replace(Regex("[^(#A-Fa-f0-9)]"), "") 58 | val courseColor = widget_config_trans_courseColorEt.text.toString().replace(Regex("[^(#A-Fa-f0-9)]"), "") 59 | val roomColor = widget_config_trans_roomColorEt.text.toString().replace(Regex("[^(#A-Fa-f0-9)]"), "") 60 | val holderColor = widget_config_trans_holderColorEt.text.toString().replace(Regex("[^(#A-Fa-f0-9)]"), "") 61 | 62 | if ((timeColor.length != 7 && timeColor.length != 9) || 63 | (courseColor.length != 7 && courseColor.length != 9) || 64 | (roomColor.length != 7 && roomColor.length != 9) || 65 | holderColor.length != 7 && holderColor.length != 9) { 66 | Toast.makeText(this@TransConfigActivity, "颜色有误,请修改,如#ffffff", Toast.LENGTH_SHORT).show() 67 | return@setOnClickListener 68 | } 69 | 70 | TransConfig().apply { 71 | this@apply.timeTextSize = timeSize 72 | this@apply.courseTextSize = courseSize 73 | this@apply.roomTextSize = roomSize 74 | this@apply.timeTextColor = timeColor 75 | this@apply.courseTextColor = courseColor 76 | this@apply.roomTextColor = roomColor 77 | this@apply.holderColor = holderColor 78 | }.save(this) 79 | 80 | LittleTransWidget().refresh(this@TransConfigActivity) 81 | 82 | Toast.makeText(this@TransConfigActivity, "已刷新", Toast.LENGTH_SHORT).show() 83 | } 84 | } 85 | 86 | private fun setAll(config: TransConfig) { 87 | setTvColor(config) 88 | setTextSize(config) 89 | setColorEditText(config) 90 | setPreviewColor(config) 91 | 92 | //设置拖动条的监听 93 | setSeekBar(config) 94 | } 95 | 96 | private fun setEditListener() { 97 | widget_config_trans_holderColorEt.addTextChangedListener(getTextChangedListener(widget_config_trans_holderColor, null)) 98 | widget_config_trans_timeColorEt.addTextChangedListener(getTextChangedListener(widget_config_trans_timeColor, widget_config_trans_timeTv)) 99 | widget_config_trans_roomColorEt.addTextChangedListener(getTextChangedListener(widget_config_trans_roomColor, widget_config_trans_roomTv)) 100 | widget_config_trans_courseColorEt.addTextChangedListener(getTextChangedListener(widget_config_trans_courseColor, widget_config_trans_courseTv)) 101 | } 102 | 103 | private fun getTextChangedListener(colorView: View, textView: TextView?): TextWatcher { 104 | return object : TextWatcher { 105 | override fun afterTextChanged(s: Editable?) { 106 | 107 | } 108 | 109 | override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { 110 | 111 | } 112 | 113 | override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { 114 | if (s == null) return 115 | s.replace(Regex("[^(a-fA-F0-9#)]"), "") 116 | if (!s.isEmpty() && s.first() == '#' && (s.length == 7 || s.length == 9)) { 117 | try { 118 | val color = Color.parseColor(s.toString()) 119 | colorView.setBackgroundColor(color) 120 | textView?.setTextColor(color) 121 | } catch (e: Exception) { 122 | e.printStackTrace() 123 | } 124 | 125 | } 126 | } 127 | 128 | } 129 | } 130 | 131 | private fun setPreviewColor(config: TransConfig) { 132 | try { 133 | widget_config_trans_timeColor.setBackgroundColor(Color.parseColor(config.timeTextColor)) 134 | widget_config_trans_roomColor.setBackgroundColor(Color.parseColor(config.roomTextColor)) 135 | widget_config_trans_courseColor.setBackgroundColor(Color.parseColor(config.courseTextColor)) 136 | widget_config_trans_holderColor.setBackgroundColor(Color.parseColor(config.holderColor)) 137 | } catch (e: Exception) { 138 | e.printStackTrace() 139 | } 140 | } 141 | 142 | private fun setColorEditText(config: TransConfig) { 143 | widget_config_trans_courseColorEt.setText(config.courseTextColor) 144 | widget_config_trans_roomColorEt.setText(config.roomTextColor) 145 | widget_config_trans_timeColorEt.setText(config.timeTextColor) 146 | widget_config_trans_holderColorEt.setText(config.holderColor) 147 | } 148 | 149 | private fun setTvColor(config: TransConfig) { 150 | try { 151 | widget_config_trans_courseTv.setTextColor(Color.parseColor(config.courseTextColor)) 152 | widget_config_trans_roomTv.setTextColor(Color.parseColor(config.roomTextColor)) 153 | widget_config_trans_timeTv.setTextColor(Color.parseColor(config.timeTextColor)) 154 | } catch (e: Exception) { 155 | e.printStackTrace() 156 | } 157 | } 158 | 159 | private fun setTextSize(config: TransConfig) { 160 | widget_config_trans_courseTv.textSize = config.courseTextSize.toFloat() 161 | widget_config_trans_roomTv.textSize = config.roomTextSize.toFloat() 162 | widget_config_trans_timeTv.textSize = config.timeTextSize.toFloat() 163 | } 164 | 165 | private fun setSeekBar(config: TransConfig) { 166 | 167 | widget_config_trans_courseSb.max = 50 168 | widget_config_trans_courseSb.progress = config.courseTextSize 169 | widget_config_trans_roomSb.max = 50 170 | widget_config_trans_roomSb.progress = config.roomTextSize 171 | widget_config_trans_timeSb.max = 50 172 | widget_config_trans_timeSb.progress = config.timeTextSize 173 | 174 | widget_config_trans_courseSb.setOnSeekBarChangeListener(getSeekBarListener(widget_config_trans_courseTv)) 175 | widget_config_trans_roomSb.setOnSeekBarChangeListener(getSeekBarListener(widget_config_trans_roomTv)) 176 | widget_config_trans_timeSb.setOnSeekBarChangeListener(getSeekBarListener(widget_config_trans_timeTv)) 177 | } 178 | 179 | private fun getSeekBarListener(tv: TextView): SeekBar.OnSeekBarChangeListener { 180 | return object : SeekBar.OnSeekBarChangeListener { 181 | override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { 182 | runOnUiThread { 183 | tv.textSize = progress.toFloat() 184 | } 185 | } 186 | 187 | override fun onStartTrackingTouch(seekBar: SeekBar?) { 188 | 189 | } 190 | 191 | override fun onStopTrackingTouch(seekBar: SeekBar?) { 192 | 193 | } 194 | 195 | } 196 | } 197 | 198 | private fun setColorPicker(touchView: View, colorView: View, textView: TextView?, editText: EditText) { 199 | touchView.setOnClickListener { 200 | val dialog = ColorPickerDialog.newBuilder() 201 | .setShowAlphaSlider(true) 202 | .create() 203 | 204 | dialog.setColorPickerDialogListener(object : ColorPickerDialogListener { 205 | override fun onDialogDismissed(dialogId: Int) { 206 | 207 | } 208 | 209 | override fun onColorSelected(dialogId: Int, color: Int) { 210 | val stringColor = String.format("#%06X", (0xFFFFFFFF and color.toLong())) 211 | editText.setText(stringColor) 212 | textView?.setTextColor(color) 213 | colorView.setBackgroundColor(color) 214 | } 215 | }) 216 | dialog.show(fragmentManager, "颜色选择") 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/util/AutoUpdateCourse.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.util 2 | 3 | import android.app.IntentService 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.util.Log 7 | import com.google.gson.Gson 8 | import com.zia.widget.bean.Course 9 | import com.zia.widget.net.Service 10 | import com.zia.widget.widget.little.LittleTransWidget 11 | import com.zia.widget.widget.little.LittleWidget 12 | import com.zia.widget.widget.normal.NormalWidget 13 | import okhttp3.FormBody 14 | import okhttp3.Request 15 | import java.util.* 16 | 17 | /** 18 | * Created by zia on 2018/12/17. 19 | */ 20 | 21 | /** 22 | * 自动更新课表 23 | */ 24 | //同步方法,避免不必要的网络请求 25 | @Synchronized fun autoFreshCourse(context: Context) { 26 | //如果没开启该功能 27 | if (!context.defaultSharedPreferences.getBoolean(SP_IS_AUTO_UPDATE, false)){ 28 | return 29 | } 30 | //获取应该更新的时间 31 | val updateTime = context.defaultSharedPreferences 32 | .getLong(SP_COURSE_UPDATE_TIME, Calendar.getInstance().timeInMillis - 1) 33 | //如果当前时间大于应该更新的时间,并且是晚上12点,那么更新 34 | val calendar = Calendar.getInstance() 35 | if (calendar.timeInMillis > updateTime && calendar.get(Calendar.HOUR_OF_DAY) == 0) { 36 | context.startService(Intent(context, UpdateCourseService::class.java)) 37 | } 38 | } 39 | 40 | class UpdateCourseService : IntentService("UpdateCourseService") { 41 | override fun onHandleIntent(intent: Intent?) { 42 | val stuNum = defaultSharedPreferences.getString(SP_STUNUM, "") 43 | if (stuNum == null || stuNum.isEmpty()) return 44 | 45 | val courseRequest = Request.Builder() 46 | .url("https://wx.idsbllp.cn/api/kebiao") 47 | .post(FormBody.Builder().add("stuNum", stuNum).build()) 48 | .build() 49 | try { 50 | val json = Service.okHttpClient.newCall(courseRequest).execute().body()?.string() 51 | val course: Course = Gson().fromJson(json, Course::class.java) 52 | if (course.data == null) { 53 | return 54 | } 55 | val calendar = Calendar.getInstance() 56 | calendar.add(Calendar.DATE, 7) 57 | defaultSharedPreferences.editor { 58 | putString(WIDGET_COURSE, json) 59 | putBoolean(SP_WIDGET_NEED_FRESH, true) 60 | putString(SP_STUNUM, stuNum) 61 | putLong(SP_COURSE_UPDATE_TIME, calendar.timeInMillis) 62 | } 63 | LittleTransWidget().refresh(this@UpdateCourseService) 64 | LittleWidget().refresh(this@UpdateCourseService) 65 | NormalWidget().fresh(this@UpdateCourseService, 0) 66 | Log.d(javaClass.simpleName, "autoUpdate:\n$json") 67 | } catch (e: Exception) { 68 | e.printStackTrace() 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/util/FileUtil.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.util 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import android.os.Build 6 | import android.support.v4.content.FileProvider 7 | import java.io.File 8 | 9 | /** 10 | * Created by zia on 2018/10/13. 11 | */ 12 | object FileUtil { 13 | fun getFileUri(context: Context, file: File): Uri { 14 | return if (Build.VERSION.SDK_INT >= 24) { 15 | FileProvider.getUriForFile(context, "com.zia.widget.FileProvider", file) 16 | } else { 17 | Uri.fromFile(file) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/util/ShortcutsUtil.java: -------------------------------------------------------------------------------- 1 | package com.zia.widget.util; 2 | 3 | import android.content.Context; 4 | import android.content.pm.ShortcutInfo; 5 | import android.content.pm.ShortcutManager; 6 | import android.widget.Toast; 7 | 8 | import java.util.Collections; 9 | import java.util.List; 10 | 11 | /** 12 | * Created by zia on 2018/12/14. 13 | */ 14 | public class ShortcutsUtil { 15 | 16 | /** 17 | * 删除shortcut 18 | * 19 | * @param context 20 | * @param id 21 | */ 22 | public static void removeShortcut(Context context, String id) { 23 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) { 24 | try { 25 | ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class); 26 | shortcutManager.removeDynamicShortcuts(Collections.singletonList(id)); 27 | } catch (Exception e) { 28 | e.printStackTrace(); 29 | if (context != null) { 30 | Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show(); 31 | } 32 | } 33 | } 34 | } 35 | 36 | public static void setShortcuts(Context context, List infos) { 37 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) { 38 | try { 39 | ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class); 40 | shortcutManager.setDynamicShortcuts(infos); 41 | } catch (Exception e) { 42 | e.printStackTrace(); 43 | if (context != null) { 44 | Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show(); 45 | } 46 | } 47 | } 48 | } 49 | 50 | /** 51 | * 添加shortcut 52 | * 53 | * @param context 54 | * @param shortcutInfo 55 | */ 56 | public static void addShortcut(Context context, ShortcutInfo shortcutInfo) { 57 | try { 58 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) { 59 | ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class); 60 | shortcutManager.addDynamicShortcuts(Collections.singletonList(shortcutInfo)); 61 | } 62 | } catch (Exception e) { 63 | e.printStackTrace(); 64 | if (context != null) { 65 | Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show(); 66 | } 67 | } 68 | } 69 | 70 | /** 71 | * 添加shortcut到指定position 72 | * 73 | * @param context 74 | * @param shortcutInfo 75 | * @param index 76 | */ 77 | public static void addShortcut(Context context, ShortcutInfo shortcutInfo, int index) { 78 | try { 79 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) { 80 | ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class); 81 | List dynamicShortcuts = shortcutManager.getDynamicShortcuts(); 82 | for (ShortcutInfo dynamicShortcut : dynamicShortcuts) { 83 | if (dynamicShortcut.getId().equals(shortcutInfo.getId())) { 84 | return; 85 | } 86 | } 87 | dynamicShortcuts.add(index, shortcutInfo); 88 | setShortcuts(context, dynamicShortcuts); 89 | } 90 | } catch (Exception e) { 91 | e.printStackTrace(); 92 | if (context != null) { 93 | Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show(); 94 | } 95 | } 96 | 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/util/TimeUtil.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.util 2 | 3 | import java.util.Calendar 4 | import java.util.Date 5 | 6 | /** 7 | * Created by zia on 2018/10/12. 8 | */ 9 | object TimeUtil { 10 | 11 | fun calcDayOffset(date1: Date, date2: Date): Int { 12 | val cal1 = Calendar.getInstance() 13 | cal1.time = date1 14 | 15 | val cal2 = Calendar.getInstance() 16 | cal2.time = date2 17 | val day1 = cal1.get(Calendar.DAY_OF_YEAR) 18 | val day2 = cal2.get(Calendar.DAY_OF_YEAR) 19 | 20 | val year1 = cal1.get(Calendar.YEAR) 21 | val year2 = cal2.get(Calendar.YEAR) 22 | if (year1 != year2) { //同一年 23 | var timeDistance = 0 24 | for (i in year1 until year2) { 25 | if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0) { //闰年 26 | timeDistance += 366 27 | } else { //不是闰年 28 | 29 | timeDistance += 365 30 | } 31 | } 32 | return timeDistance + (day2 - day1) 33 | } else { //不同年 34 | return day2 - day1 35 | } 36 | } 37 | 38 | fun calcWeekOffset(startTime: Date, endTime: Date): Int { 39 | val cal = Calendar.getInstance() 40 | cal.time = startTime 41 | var dayOfWeek = cal.get(Calendar.DAY_OF_WEEK) 42 | dayOfWeek = dayOfWeek - 1 43 | if (dayOfWeek == 0) dayOfWeek = 7 44 | 45 | val dayOffset = calcDayOffset(startTime, endTime) 46 | 47 | var weekOffset = dayOffset / 7 48 | val a: Int 49 | if (dayOffset > 0) { 50 | a = if (dayOffset % 7 + dayOfWeek > 7) 1 else 0 51 | } else { 52 | a = if (dayOfWeek + dayOffset % 7 < 1) -1 else 0 53 | } 54 | weekOffset = weekOffset + a 55 | return weekOffset 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/util/Util.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.util 2 | 3 | import android.app.PendingIntent 4 | import android.appwidget.AppWidgetProvider 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.content.SharedPreferences 8 | import android.net.Uri 9 | import android.support.annotation.IdRes 10 | import android.util.Log 11 | import com.google.gson.Gson 12 | import com.zia.widget.bean.Course 13 | import java.text.SimpleDateFormat 14 | import java.util.* 15 | import kotlin.collections.ArrayList 16 | 17 | /** 18 | * Created by zia on 2018/10/10. 19 | * 精力憔悴,这些方法直接揉在一起了 20 | */ 21 | 22 | fun getTodayCourse(context: Context): List? { 23 | return getCourseByCalendar(context, Calendar.getInstance()) 24 | } 25 | 26 | fun getCourseByCalendar(context: Context, calendar: Calendar): ArrayList? { 27 | val json = context.defaultSharedPreferences.getString(WIDGET_COURSE, "") 28 | val course = Gson().fromJson(json, Course::class.java) ?: return null 29 | if (course.data == null) return null 30 | 31 | val needFresh = context.defaultSharedPreferences.getBoolean(SP_WIDGET_NEED_FRESH, true) 32 | //计算当前周数 33 | var beginTime = context.defaultSharedPreferences.getLong(beginTimeShareName, 0L) 34 | //需要刷新当前周 35 | if (needFresh) { 36 | beginTime = saveBeginTime(context, course.nowWeek) 37 | context.defaultSharedPreferences.editor { 38 | putBoolean(SP_WIDGET_NEED_FRESH, false) 39 | putString(WIDGET_COURSE, json) 40 | } 41 | } 42 | val week = TimeUtil.calcWeekOffset(Date(beginTime), calendar.time) 43 | Log.d("Widget", "week:$week") 44 | /* 45 | * 转换表,老外从周日开始计数,orz 46 | * 7 1 2 3 4 5 6 老外 47 | * 1 2 3 4 5 6 7 Calendar.DAY_OF_WEEK 48 | * 6 0 1 2 3 4 5 需要的结果(hash_day) 49 | * */ 50 | val hash_day = (calendar.get(Calendar.DAY_OF_WEEK) + 5) % 7 51 | 52 | val list = ArrayList() 53 | course.data!!.forEach { 54 | if (it.hash_day == hash_day && it.week!!.contains(week)) { 55 | // LogUtils.d("Widget", it.toString()) 56 | list.add(it) 57 | } 58 | } 59 | list.sortBy { it.hash_lesson } 60 | return list 61 | } 62 | 63 | private val beginTimeShareName = "zscy_widget_beginTime" 64 | 65 | private fun saveBeginTime(context: Context, nowWeek: Int): Long { 66 | val calendar = Calendar.getInstance() 67 | val targetWeek = calendar.get(Calendar.WEEK_OF_YEAR) - nowWeek 68 | calendar.set(Calendar.WEEK_OF_YEAR, targetWeek) 69 | val hash_day = (calendar.get(Calendar.DAY_OF_WEEK) + 5) % 7 70 | calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - hash_day) 71 | calendar.set(Calendar.HOUR_OF_DAY, 0) 72 | calendar.set(Calendar.MINUTE, 0) 73 | calendar.set(Calendar.MILLISECOND, 0) 74 | context.defaultSharedPreferences.editor { 75 | putLong(beginTimeShareName, calendar.time.time) 76 | } 77 | return calendar.time.time 78 | } 79 | 80 | fun getErrorCourseList(): ArrayList { 81 | val data = Course.DataBean() 82 | data.hash_lesson = 0 83 | data.course = "数据异常,请刷新" 84 | data.classroom = "" 85 | val list = ArrayList() 86 | list.add(data) 87 | return list 88 | } 89 | 90 | fun getNoCourse(): Course.DataBean { 91 | val data = Course.DataBean() 92 | data.hash_lesson = 0 93 | data.course = "无课" 94 | data.classroom = "" 95 | return data 96 | } 97 | 98 | fun saveHashLesson(context: Context, hash_lesson: Int, shareName: String) { 99 | context.defaultSharedPreferences.editor { 100 | putInt(shareName, hash_lesson) 101 | } 102 | } 103 | 104 | fun getHashLesson(context: Context, shareName: String): Int { 105 | return context.defaultSharedPreferences.getInt(shareName, 0) 106 | } 107 | 108 | 109 | private const val SP_DayOffset = "dayOffset" 110 | //天数偏移量,用于LittleWidget切换明天课程 111 | fun saveDayOffset(context: Context, offset: Int) { 112 | context.defaultSharedPreferences.editor { 113 | putInt(SP_DayOffset, offset) 114 | } 115 | } 116 | fun getDayOffset(context: Context): Int { 117 | return context.defaultSharedPreferences.getInt(SP_DayOffset, 0) 118 | } 119 | 120 | 121 | fun isNight(): Boolean { 122 | val calendar = Calendar.getInstance() 123 | return calendar.get(Calendar.HOUR_OF_DAY) > 19 124 | } 125 | 126 | /** 127 | * hash_lesson == 0 第1节 返回8:00 128 | */ 129 | fun getStartCalendarByNum(hash_lesson: Int): Calendar { 130 | val calendar = Calendar.getInstance() 131 | when (hash_lesson) { 132 | 0 -> { 133 | calendar.set(Calendar.HOUR_OF_DAY, 8) 134 | calendar.set(Calendar.MINUTE, 0) 135 | } 136 | 1 -> { 137 | calendar.set(Calendar.HOUR_OF_DAY, 10) 138 | calendar.set(Calendar.MINUTE, 15) 139 | } 140 | 2 -> { 141 | calendar.set(Calendar.HOUR_OF_DAY, 14) 142 | calendar.set(Calendar.MINUTE, 0) 143 | } 144 | 3 -> { 145 | calendar.set(Calendar.HOUR_OF_DAY, 16) 146 | calendar.set(Calendar.MINUTE, 15) 147 | } 148 | 4 -> { 149 | calendar.set(Calendar.HOUR_OF_DAY, 19) 150 | calendar.set(Calendar.MINUTE, 0) 151 | } 152 | 5 -> { 153 | calendar.set(Calendar.HOUR_OF_DAY, 21) 154 | calendar.set(Calendar.MINUTE, 15) 155 | } 156 | 6 -> { 157 | calendar.set(Calendar.HOUR_OF_DAY, 19) 158 | calendar.set(Calendar.MINUTE, 0) 159 | } 160 | 7 -> { 161 | calendar.set(Calendar.HOUR_OF_DAY, 21) 162 | calendar.set(Calendar.MINUTE, 0) 163 | } 164 | } 165 | return calendar 166 | } 167 | 168 | fun getWeekDayChineseName(weekDay: Int): String { 169 | when (weekDay) { 170 | 1 -> return "星期天" 171 | 2 -> return "星期一" 172 | 3 -> return "星期二" 173 | 4 -> return "星期三" 174 | 5 -> return "星期四" 175 | 6 -> return "星期五" 176 | 7 -> return "星期六" 177 | else -> return "null" 178 | } 179 | } 180 | 181 | fun getClickPendingIntent(context: Context, @IdRes resId: Int, action: String, clazz: Class): PendingIntent { 182 | val intent = Intent() 183 | intent.setClass(context, clazz) 184 | intent.action = action 185 | intent.data = Uri.parse("id:$resId") 186 | return PendingIntent.getBroadcast(context, 0, intent, 0) 187 | } 188 | 189 | fun formatTime(calendar: Calendar): String { 190 | return SimpleDateFormat("HH:mm", Locale.SIMPLIFIED_CHINESE).format(calendar.time) 191 | } 192 | 193 | fun filterClassRoom(classRoom: String): String { 194 | if (classRoom.length > 8) { 195 | return classRoom.replace(Regex("[\\u4e00-\\u9fa5()()]"), "") 196 | } else { 197 | return classRoom 198 | } 199 | } 200 | 201 | //桌面小部件课表json数据 202 | const val WIDGET_COURSE = "widget_kb" 203 | 204 | const val SP_WIDGET_NEED_FRESH = "widget_need_fresh" 205 | const val SP_STUNUM = "student_number" 206 | const val SP_COURSE_UPDATE_TIME = "course_update_time" 207 | const val SP_IS_AUTO_UPDATE = "is_auto_update" 208 | 209 | val Context.defaultSharedPreferences get() = sharedPreferences("share_data") 210 | 211 | fun Context.sharedPreferences(name: String): SharedPreferences = getSharedPreferences(name, Context.MODE_PRIVATE) 212 | fun SharedPreferences.editor(editorBuilder: SharedPreferences.Editor.() -> Unit) = edit().apply(editorBuilder).apply() -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/util/Version.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.util 2 | 3 | import android.content.Context 4 | import android.content.pm.PackageManager 5 | 6 | /** 7 | * Created by zia on 2018/10/13. 8 | */ 9 | object Version { 10 | fun packageCode(context: Context): Int { 11 | val manager = context.packageManager 12 | var code = 0 13 | try { 14 | val info = manager.getPackageInfo(context.packageName, 0) 15 | code = info.versionCode 16 | } catch (e: PackageManager.NameNotFoundException) { 17 | e.printStackTrace() 18 | } 19 | 20 | return code 21 | } 22 | 23 | fun packageName(context: Context): String? { 24 | val manager = context.packageManager 25 | var name: String? = null 26 | try { 27 | val info = manager.getPackageInfo(context.packageName, 0) 28 | name = info.versionName 29 | } catch (e: PackageManager.NameNotFoundException) { 30 | e.printStackTrace() 31 | } 32 | 33 | return name 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/util/downlaodUtil/DownLoaderHelper.java: -------------------------------------------------------------------------------- 1 | package com.zia.widget.util.downlaodUtil; 2 | 3 | import android.util.Log; 4 | 5 | import java.io.File; 6 | 7 | public class DownLoaderHelper { 8 | public static boolean mkDir(String root) { 9 | File file = new File(root); 10 | if (!file.exists() && !file.mkdirs()) { 11 | Log.d("DownLoaderHelper", "路径创建失败.."); 12 | return false; 13 | } 14 | return true; 15 | } 16 | 17 | public static String convertSize(int fileSize) { 18 | float kb = fileSize / 1024; 19 | float mb = kb / 1024; 20 | float gb = mb / 1024; 21 | if (gb != 0) { 22 | return mb / 1024f + " Gb"; 23 | } else if (mb != 0) { 24 | return kb / 1024f + " Mb"; 25 | } else { 26 | return fileSize / 1024f + " Kb"; 27 | } 28 | } 29 | 30 | /** 31 | * 检查路径并返回带有/结尾的路径 32 | * 33 | * @param source 路径 34 | * @return 35 | */ 36 | static String optimizeRoot(String source) { 37 | source = source.trim(); 38 | int length = source.length(); 39 | if (source.charAt(length - 1) != File.separatorChar) { 40 | source = source + File.separatorChar; 41 | } 42 | return source; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/util/downlaodUtil/DownloadListener.java: -------------------------------------------------------------------------------- 1 | package com.zia.widget.util.downlaodUtil; 2 | 3 | public interface DownloadListener { 4 | void getRatio(float ratio,float part,float total); 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/util/downlaodUtil/DownloadRunnable.java: -------------------------------------------------------------------------------- 1 | package com.zia.widget.util.downlaodUtil; 2 | 3 | import android.util.Log; 4 | 5 | import java.io.BufferedInputStream; 6 | import java.io.BufferedOutputStream; 7 | import java.io.FileOutputStream; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.net.HttpURLConnection; 11 | import java.net.URL; 12 | 13 | public class DownloadRunnable implements Runnable { 14 | 15 | private String url, path, fileName; 16 | private int fileLength, currentFileLength = 0; 17 | private DownloadListener downloadListener; 18 | private int position; 19 | 20 | public DownloadRunnable(String url, String path, String fileName) { 21 | this(url, path, fileName, null); 22 | } 23 | 24 | public DownloadRunnable(String url, String path, String fileName, DownloadListener downloadListener) { 25 | this.url = url; 26 | this.path = DownLoaderHelper.optimizeRoot(path); 27 | this.fileName = fileName; 28 | this.downloadListener = downloadListener; 29 | } 30 | 31 | @Override 32 | public void run() { 33 | if (!DownLoaderHelper.mkDir(path)) { 34 | Log.e("DownloadRunnable", "创建文件路径失败.."); 35 | return; 36 | } 37 | 38 | HttpURLConnection conn = null; 39 | InputStream inputStream = null; 40 | 41 | try { 42 | 43 | url = ReLocateUtil.getLocate(url); 44 | URL httpUrl = new URL(url); 45 | 46 | conn = (HttpURLConnection) httpUrl.openConnection(); 47 | conn.setConnectTimeout(5000); 48 | conn.setDoInput(true); 49 | conn.connect(); 50 | 51 | fileLength = conn.getContentLength(); 52 | Log.d("DownloadRunnable", fileName + " Size: " + DownLoaderHelper.convertSize(fileLength)); 53 | 54 | inputStream = conn.getInputStream(); 55 | loadSteam(inputStream); 56 | } catch (IOException e) { 57 | e.printStackTrace(); 58 | } finally { 59 | try { 60 | if (inputStream != null) { 61 | inputStream.close(); 62 | } 63 | } catch (IOException e) { 64 | e.printStackTrace(); 65 | } 66 | if (conn != null) { 67 | conn.disconnect(); 68 | } 69 | // Thread.currentThread().interrupt(); 70 | } 71 | } 72 | 73 | //写入本地 74 | private void loadSteam(InputStream inputStream) throws IOException { 75 | BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); 76 | FileOutputStream fileOut = new FileOutputStream(path + fileName); 77 | BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOut); 78 | byte[] buf = new byte[4096]; 79 | int lastRatio = 0; 80 | int length = bufferedInputStream.read(buf); 81 | while (length != -1) { 82 | bufferedOutputStream.write(buf, 0, length); 83 | length = bufferedInputStream.read(buf); 84 | currentFileLength = currentFileLength + length; 85 | float ratio = (float) currentFileLength / (float) fileLength * 100; 86 | if (downloadListener != null && lastRatio != (int) (ratio * 100)) {//变化超过0.01时回调 87 | downloadListener.getRatio(ratio, currentFileLength, fileLength); 88 | lastRatio = (int) (ratio * 100); 89 | } 90 | } 91 | bufferedOutputStream.close(); 92 | bufferedInputStream.close(); 93 | if (downloadListener != null) downloadListener.getRatio(100f, fileLength, fileLength); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/util/downlaodUtil/ReLocateUtil.java: -------------------------------------------------------------------------------- 1 | package com.zia.widget.util.downlaodUtil; 2 | 3 | import java.io.IOException; 4 | import java.net.HttpURLConnection; 5 | import java.net.URL; 6 | 7 | public class ReLocateUtil { 8 | 9 | public static String getLocate(String url) throws IOException { 10 | URL u = new URL(url); 11 | HttpURLConnection conn = (HttpURLConnection) u.openConnection(); 12 | String location = conn.getHeaderField("Location"); 13 | if (location == null) { 14 | return url; 15 | } else { 16 | System.out.println("reLocation:\n" + location); 17 | return location; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/widget/little/BaseLittleWidget.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.widget.little 2 | 3 | import android.appwidget.AppWidgetManager 4 | import android.appwidget.AppWidgetProvider 5 | import android.content.ComponentName 6 | import android.content.Context 7 | import android.content.Intent 8 | import android.support.annotation.IdRes 9 | import android.support.annotation.LayoutRes 10 | import android.widget.RemoteViews 11 | import android.widget.Toast 12 | import com.zia.widget.bean.Course 13 | import com.zia.widget.util.* 14 | import java.util.* 15 | 16 | /** 17 | * Created by zia on 2018/10/10. 18 | * 精简版桌面小控件 19 | */ 20 | abstract class BaseLittleWidget : AppWidgetProvider() { 21 | 22 | private val shareName = "share_hash_lesson_trans" 23 | 24 | @LayoutRes 25 | protected abstract fun getLayoutResId(): Int 26 | 27 | protected abstract fun getShareName(): String 28 | @IdRes 29 | protected abstract fun getUpResId(): Int 30 | 31 | @IdRes 32 | protected abstract fun getDownResId(): Int 33 | 34 | @IdRes 35 | protected abstract fun getTitleResId(): Int 36 | 37 | @IdRes 38 | protected abstract fun getCourseNameResId(): Int 39 | 40 | @IdRes 41 | protected abstract fun getRoomResId(): Int 42 | 43 | @IdRes 44 | protected abstract fun getRefreshResId(): Int 45 | 46 | protected abstract fun getRemoteViews(context: Context, course: Course.DataBean?, timeTv: String = "课程安排"): RemoteViews 47 | 48 | 49 | override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { 50 | super.onUpdate(context, appWidgetManager, appWidgetIds) 51 | autoFreshCourse(context) 52 | refresh(context) 53 | } 54 | 55 | override fun onReceive(context: Context, intent: Intent) { 56 | super.onReceive(context, intent) 57 | 58 | if (intent.action == "btn.text.com") { 59 | val data = intent.data 60 | var rId = -1 61 | if (data != null) { 62 | rId = data.schemeSpecificPart.toInt() 63 | } 64 | if (rId == getUpResId() && getUpResId() != 0) { 65 | goUp(context) 66 | } else if (rId == getDownResId() && getDownResId() != 0) { 67 | goDown(context) 68 | } else if (rId == getRefreshResId() && getRefreshResId() != 0) { 69 | refresh(context) 70 | Toast.makeText(context, "已刷新", Toast.LENGTH_SHORT).show() 71 | } 72 | } 73 | } 74 | 75 | private fun show(context: Context, course: Course.DataBean?, startTime: String = "课程安排") { 76 | val manager = AppWidgetManager.getInstance(context) 77 | val componentName = ComponentName(context, javaClass) 78 | manager.updateAppWidget(componentName, getRemoteViews(context, course, startTime)) 79 | } 80 | 81 | //获取正常显示的下一节课 82 | fun refresh(context: Context) { 83 | saveDayOffset(context, 0)//重置天数偏移 84 | val list = getTodayCourse(context) 85 | ?: getErrorCourseList() 86 | 87 | var isFound = false 88 | list.forEach { 89 | val endCalendar = getStartCalendarByNum(it.hash_lesson) 90 | //如果今天还有下一节课,显示下一节 91 | if (Calendar.getInstance() < endCalendar) { 92 | show(context, it, formatTime(getStartCalendarByNum(it.hash_lesson))) 93 | return 94 | } 95 | isFound = true 96 | } 97 | 98 | if (isFound) {//今天有课,但是上完了 99 | //新策略:显示明天第一节 100 | showTomorrowCourse(context) 101 | } else {//今天没有课 102 | if (isNight()) {//如果在晚上,显示明天课程 103 | showTomorrowCourse(context) 104 | } else { 105 | //白天显示今天无课 106 | show(context, null) 107 | } 108 | } 109 | } 110 | 111 | private fun showTomorrowCourse(context: Context) { 112 | saveDayOffset(context, 1)//天数偏移加一,上下切换课程将会切换明天的 113 | 114 | val tomorrowCalendar = Calendar.getInstance() 115 | tomorrowCalendar.set(Calendar.DAY_OF_YEAR, tomorrowCalendar.get(Calendar.DAY_OF_YEAR) + 1) 116 | val tomorrowList = getCourseByCalendar(context, tomorrowCalendar) 117 | if (tomorrowList == null) {//数据出错 118 | show(context, getErrorCourseList().first(), "掌上重邮") 119 | } else { 120 | if (tomorrowList.isEmpty()) {//明日无课 121 | show(context, getNoCourse(), "明日课程") 122 | } else {//显示明天第一节课 123 | show(context, tomorrowList.first(), "明日:" + formatTime(getStartCalendarByNum(tomorrowList.first().hash_lesson))) 124 | } 125 | } 126 | } 127 | 128 | //获取当前课程的上一节课 129 | private fun goUp(context: Context) { 130 | val hash_lesson = getHashLesson(context, getShareName()) 131 | 132 | val dayOffset = getDayOffset(context) 133 | val calendar = Calendar.getInstance() 134 | calendar.set(Calendar.DATE, calendar.get(Calendar.DATE) + dayOffset) 135 | 136 | //拿到当前课程的上一个课程 137 | val list = getCourseByCalendar(context, calendar) 138 | ?: getErrorCourseList() 139 | 140 | var course: Course.DataBean? = null 141 | list.forEach { 142 | if (hash_lesson > it.hash_lesson) { 143 | course = it 144 | } 145 | } 146 | if (course != null) { 147 | val beforeTitle = if (dayOffset == 1) "明日:" else "" 148 | show(context, course, beforeTitle + formatTime(getStartCalendarByNum(course!!.hash_lesson))) 149 | } else { 150 | Toast.makeText(context, "没有上一节课了~", Toast.LENGTH_SHORT).show() 151 | } 152 | } 153 | 154 | //获取当前课程的下一节课 155 | private fun goDown(context: Context) { 156 | val hash_lesson = getHashLesson(context, getShareName()) 157 | 158 | val dayOffset = getDayOffset(context) 159 | val calendar = Calendar.getInstance() 160 | calendar.set(Calendar.DATE, calendar.get(Calendar.DATE) + dayOffset) 161 | 162 | //拿到最后一节 163 | val list = getCourseByCalendar(context, calendar) 164 | ?: getErrorCourseList() 165 | list.forEach { 166 | if (hash_lesson < it.hash_lesson) { 167 | val beforeTitle = if (dayOffset == 1) "明日:" else "" 168 | show(context, it, beforeTitle + formatTime(getStartCalendarByNum(it.hash_lesson))) 169 | return 170 | } 171 | } 172 | Toast.makeText(context, "没有下一节课了~", Toast.LENGTH_SHORT).show() 173 | } 174 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/widget/little/LittleTransWidget.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.widget.little 2 | 3 | import android.content.Context 4 | import android.graphics.Bitmap 5 | import android.graphics.Color 6 | import android.util.TypedValue 7 | import android.view.View 8 | import android.widget.RemoteViews 9 | import com.zia.widget.R 10 | import com.zia.widget.bean.Course 11 | import com.zia.widget.page.trans.TransConfig 12 | import com.zia.widget.util.filterClassRoom 13 | import com.zia.widget.util.getWeekDayChineseName 14 | import java.util.* 15 | 16 | /** 17 | * Created by zia on 2018/10/10. 18 | * 小型部件,不透明 19 | */ 20 | class LittleTransWidget : BaseLittleWidget() { 21 | override fun getLayoutResId(): Int { 22 | return R.layout.widget_little_trans 23 | } 24 | 25 | override fun getShareName(): String { 26 | return "widget_share_little_trans" 27 | } 28 | 29 | override fun getUpResId(): Int { 30 | return 0 31 | } 32 | 33 | override fun getDownResId(): Int { 34 | return 0 35 | } 36 | 37 | override fun getTitleResId(): Int { 38 | return R.id.widget_little_title_trans 39 | } 40 | 41 | override fun getCourseNameResId(): Int { 42 | return R.id.widget_little_trans_courseName 43 | } 44 | 45 | override fun getRoomResId(): Int { 46 | return R.id.widget_little_trans_room 47 | } 48 | 49 | override fun getRefreshResId(): Int { 50 | return 0 51 | } 52 | 53 | override fun getRemoteViews(context: Context, course: Course.DataBean?, timeTv: String): RemoteViews { 54 | val rv = RemoteViews(context.packageName, getLayoutResId()) 55 | if (course == null) { 56 | rv.setTextViewText(getTitleResId(), getWeekDayChineseName(Calendar.getInstance().get(Calendar.DAY_OF_WEEK))) 57 | rv.setTextViewText(getCourseNameResId(), "今天没有课~") 58 | rv.setTextViewText(getRoomResId(), "") 59 | } else { 60 | rv.setTextViewText(getTitleResId(), timeTv) 61 | rv.setTextViewText(getCourseNameResId(), course.course) 62 | rv.setTextViewText(getRoomResId(), filterClassRoom(course.classroom!!)) 63 | } 64 | 65 | //设置用户自定义定义配置 66 | 67 | try {//这个tryCatch防止用户输入的颜色有误,parseColor报错 68 | //标题(时间) 69 | val config = TransConfig.getUserConfig(context) 70 | rv.setTextColor(getTitleResId(), Color.parseColor(config.timeTextColor)) 71 | rv.setTextViewTextSize(getTitleResId(), TypedValue.COMPLEX_UNIT_SP, config.timeTextSize.toFloat()) 72 | 73 | //课程名 74 | rv.setTextColor(getCourseNameResId(), Color.parseColor(config.courseTextColor)) 75 | rv.setTextViewTextSize(getCourseNameResId(), TypedValue.COMPLEX_UNIT_SP, config.courseTextSize.toFloat()) 76 | 77 | //教室 78 | rv.setTextColor(getRoomResId(), Color.parseColor(config.roomTextColor)) 79 | rv.setTextViewTextSize(getRoomResId(), TypedValue.COMPLEX_UNIT_SP, config.roomTextSize.toFloat()) 80 | 81 | rv.setViewVisibility(R.id.widget_little_trans_holder, View.VISIBLE) 82 | 83 | //装饰,用ARGB_8888是为了设置透明度 84 | val holderBm = Bitmap.createBitmap(300, 1, Bitmap.Config.ARGB_8888) 85 | holderBm.eraseColor(Color.parseColor(config.holderColor)) 86 | rv.setImageViewBitmap(R.id.widget_little_trans_holder, holderBm) 87 | 88 | //这个方法不能运行,上面的代码性能不行,来个人优化下? 89 | // rv.setInt(R.id.widget_little_trans_holder,"setBackgroundColor",Color.parseColor(config.holderColor)) 90 | 91 | if (config.holderColor.length == 9) { 92 | if (config.holderColor.subSequence(1, 3) == "00") { 93 | rv.setViewVisibility(R.id.widget_little_trans_holder, View.GONE) 94 | } 95 | } 96 | } catch (e: Exception) { 97 | e.printStackTrace() 98 | } 99 | 100 | return rv 101 | } 102 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/widget/little/LittleWidget.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.widget.little 2 | 3 | import android.content.Context 4 | import android.widget.RemoteViews 5 | import com.zia.widget.R 6 | import com.zia.widget.bean.Course 7 | import com.zia.widget.util.filterClassRoom 8 | import com.zia.widget.util.getClickPendingIntent 9 | import com.zia.widget.util.getWeekDayChineseName 10 | import com.zia.widget.util.saveHashLesson 11 | import java.util.* 12 | 13 | /** 14 | * Created by zia on 2018/10/10. 15 | * 小型部件,不透明 16 | */ 17 | class LittleWidget : BaseLittleWidget() { 18 | override fun getLayoutResId(): Int { 19 | return R.layout.widget_little 20 | } 21 | 22 | override fun getShareName(): String { 23 | return "widget_share_little" 24 | } 25 | 26 | override fun getUpResId(): Int { 27 | return R.id.widget_little_up 28 | } 29 | 30 | override fun getDownResId(): Int { 31 | return R.id.widget_little_down 32 | } 33 | 34 | override fun getTitleResId(): Int { 35 | return R.id.widget_little_title 36 | } 37 | 38 | override fun getCourseNameResId(): Int { 39 | return R.id.widget_little_courseName 40 | } 41 | 42 | override fun getRoomResId(): Int { 43 | return R.id.widget_little_room 44 | } 45 | 46 | override fun getRefreshResId(): Int { 47 | return R.id.widget_little_refresh 48 | } 49 | 50 | override fun getRemoteViews(context: Context, course: Course.DataBean?, timeTv: String): RemoteViews { 51 | val rv = RemoteViews(context.packageName, getLayoutResId()) 52 | rv.setOnClickPendingIntent(getUpResId(), getClickPendingIntent(context, getUpResId(), "btn.text.com", javaClass)) 53 | rv.setOnClickPendingIntent(getDownResId(), getClickPendingIntent(context, getDownResId(), "btn.text.com", javaClass)) 54 | rv.setOnClickPendingIntent(getRefreshResId(), getClickPendingIntent(context, getRefreshResId(), "btn.text.com", javaClass)) 55 | if (course == null) { 56 | rv.setTextViewText(getTitleResId(), getWeekDayChineseName(Calendar.getInstance().get(Calendar.DAY_OF_WEEK))) 57 | rv.setTextViewText(getCourseNameResId(), "今天没有课~") 58 | rv.setTextViewText(getRoomResId(), "") 59 | } else { 60 | //保存当前hash_lesson 61 | saveHashLesson(context, course.hash_lesson, getShareName()) 62 | rv.setTextViewText(getTitleResId(), timeTv) 63 | rv.setTextViewText(getCourseNameResId(), course.course) 64 | rv.setTextViewText(getRoomResId(), filterClassRoom(course.classroom!!)) 65 | } 66 | return rv 67 | } 68 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zia/widget/widget/normal/NormalWidget.kt: -------------------------------------------------------------------------------- 1 | package com.zia.widget.widget.normal 2 | 3 | import android.appwidget.AppWidgetManager 4 | import android.appwidget.AppWidgetProvider 5 | import android.content.ComponentName 6 | import android.content.Context 7 | import android.content.Intent 8 | import android.support.annotation.IdRes 9 | import android.view.View 10 | import android.widget.RemoteViews 11 | import android.widget.Toast 12 | import com.zia.widget.R 13 | import com.zia.widget.util.* 14 | import java.util.* 15 | import kotlin.math.abs 16 | 17 | /** 18 | * Created by zia on 2018/10/10. 19 | * 大课表 20 | * 简单原理:因为一天最多6节课,直接写了6个布局,隐藏后面没有用到的布局就完了 21 | */ 22 | class NormalWidget : AppWidgetProvider() { 23 | 24 | private val shareName = "zscy_widget_normal" 25 | 26 | override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { 27 | super.onUpdate(context, appWidgetManager, appWidgetIds) 28 | context.defaultSharedPreferences.editor { 29 | putInt(shareName, 0) 30 | } 31 | autoFreshCourse(context) 32 | fresh(context, 0) 33 | } 34 | 35 | override fun onReceive(context: Context, intent: Intent) { 36 | super.onReceive(context, intent) 37 | if (intent.action == "btn.text.com") { 38 | val data = intent.data 39 | var rId = -1 40 | if (data != null) { 41 | rId = data.schemeSpecificPart.toInt() 42 | } 43 | 44 | val offsetTime = context.defaultSharedPreferences.getInt(shareName, 0) 45 | 46 | if (abs(offsetTime) == 3) { 47 | Toast.makeText(context, "提示:点击星期返回今日", Toast.LENGTH_SHORT).show() 48 | } 49 | 50 | when (rId) { 51 | R.id.widget_normal_back -> { 52 | context.defaultSharedPreferences.editor { 53 | putInt(shareName, offsetTime - 1) 54 | } 55 | fresh(context, offsetTime - 1) 56 | } 57 | R.id.widget_normal_front -> { 58 | context.defaultSharedPreferences.editor { 59 | putInt(shareName, offsetTime + 1) 60 | } 61 | fresh(context, offsetTime + 1) 62 | } 63 | R.id.widget_normal_title -> { 64 | context.defaultSharedPreferences.editor { 65 | putInt(shareName, 0) 66 | } 67 | fresh(context, 0) 68 | // Toast.makeText(context, "已刷新", Toast.LENGTH_SHORT).show() 69 | } 70 | } 71 | } 72 | } 73 | 74 | /** 75 | * 刷新,传入offsetTime作为今天的偏移量 76 | */ 77 | fun fresh(context: Context, offsetTime: Int) { 78 | //生成calendar 79 | val calendar = Calendar.getInstance() 80 | val nowHour = calendar.get(Calendar.HOUR_OF_DAY) 81 | calendar.set(Calendar.DATE, calendar.get(Calendar.DATE) + offsetTime) 82 | 83 | //获取数据 84 | val list = getCourseByCalendar(context, calendar) 85 | ?: getErrorCourseList() 86 | if (list.isEmpty()){ 87 | list.add(getNoCourse()) 88 | } 89 | 90 | //如果课已经上完了,而且过了晚上7点,显示明天的课程 91 | if (nowHour > 19) { 92 | var i = 0 93 | list.forEach { 94 | val hour = getStartCalendarByNum(it.hash_lesson).get(Calendar.HOUR_OF_DAY) 95 | if (nowHour > hour) { 96 | i++ 97 | } else { 98 | return@forEach 99 | } 100 | } 101 | if (i == list.size) { 102 | calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) + 1) 103 | val tomorrowList = getCourseByCalendar(context, calendar) 104 | ?: getErrorCourseList() 105 | list.clear() 106 | list.addAll(tomorrowList) 107 | } 108 | } 109 | 110 | val rv = RemoteViews(context.packageName, R.layout.widget_normal) 111 | 112 | //显示星期几 113 | rv.setTextViewText(R.id.widget_normal_title, getWeekDayChineseName(calendar.get(Calendar.DAY_OF_WEEK))) 114 | 115 | //显示课程 116 | var index = 1 117 | list.forEach { course -> 118 | rv.setViewVisibility(getLayoutId(index), View.VISIBLE) 119 | rv.setTextViewText(getTimeId(index), formatTime(getStartCalendarByNum(course.hash_lesson))) 120 | rv.setTextViewText(getCourseId(index), course.course) 121 | rv.setTextViewText(getRoomId(index), filterClassRoom(course.classroom!!)) 122 | index++ 123 | } 124 | //隐藏后面的item 125 | for (i in index..6) { 126 | rv.setViewVisibility(getLayoutId(i), View.GONE) 127 | } 128 | 129 | //设置前后按钮操作 130 | addClickPendingIntent(rv, context) 131 | 132 | show(rv, context) 133 | } 134 | 135 | private fun addClickPendingIntent(rv: RemoteViews, context: Context) { 136 | rv.setOnClickPendingIntent(R.id.widget_normal_title, 137 | getClickPendingIntent(context, R.id.widget_normal_title, "btn.text.com", javaClass)) 138 | rv.setOnClickPendingIntent(R.id.widget_normal_front, 139 | getClickPendingIntent(context, R.id.widget_normal_front, "btn.text.com", javaClass)) 140 | rv.setOnClickPendingIntent(R.id.widget_normal_back, 141 | getClickPendingIntent(context, R.id.widget_normal_back, "btn.text.com", javaClass)) 142 | } 143 | 144 | private fun show(remoteViews: RemoteViews, context: Context) { 145 | val manager = AppWidgetManager.getInstance(context) 146 | val componentName = ComponentName(context, javaClass) 147 | manager.updateAppWidget(componentName, remoteViews) 148 | } 149 | 150 | @IdRes 151 | private fun getLayoutId(num: Int): Int { 152 | when (num) { 153 | 1 -> { 154 | return R.id.widget_normal_layout1 155 | } 156 | 2 -> { 157 | return R.id.widget_normal_layout2 158 | } 159 | 3 -> { 160 | return R.id.widget_normal_layout3 161 | } 162 | 4 -> { 163 | return R.id.widget_normal_layout4 164 | } 165 | 5 -> { 166 | return R.id.widget_normal_layout5 167 | } 168 | 6 -> { 169 | return R.id.widget_normal_layout6 170 | } 171 | else -> { 172 | return R.id.widget_normal_layout1 173 | } 174 | } 175 | } 176 | 177 | @IdRes 178 | private fun getTimeId(num: Int): Int { 179 | when (num) { 180 | 1 -> { 181 | return R.id.widget_normal_time1 182 | } 183 | 2 -> { 184 | return R.id.widget_normal_time2 185 | } 186 | 3 -> { 187 | return R.id.widget_normal_time3 188 | } 189 | 4 -> { 190 | return R.id.widget_normal_time4 191 | } 192 | 5 -> { 193 | return R.id.widget_normal_time5 194 | } 195 | 6 -> { 196 | return R.id.widget_normal_time6 197 | } 198 | else -> { 199 | return R.id.widget_normal_time1 200 | } 201 | } 202 | } 203 | 204 | @IdRes 205 | private fun getCourseId(num: Int): Int { 206 | when (num) { 207 | 1 -> { 208 | return R.id.widget_normal_course1 209 | } 210 | 2 -> { 211 | return R.id.widget_normal_course2 212 | } 213 | 3 -> { 214 | return R.id.widget_normal_course3 215 | } 216 | 4 -> { 217 | return R.id.widget_normal_course4 218 | } 219 | 5 -> { 220 | return R.id.widget_normal_course5 221 | } 222 | 6 -> { 223 | return R.id.widget_normal_course6 224 | } 225 | else -> { 226 | return R.id.widget_normal_course1 227 | } 228 | } 229 | } 230 | 231 | @IdRes 232 | private fun getRoomId(num: Int): Int { 233 | when (num) { 234 | 1 -> { 235 | return R.id.widget_normal_room1 236 | } 237 | 2 -> { 238 | return R.id.widget_normal_room2 239 | } 240 | 3 -> { 241 | return R.id.widget_normal_room3 242 | } 243 | 4 -> { 244 | return R.id.widget_normal_room4 245 | } 246 | 5 -> { 247 | return R.id.widget_normal_room5 248 | } 249 | 6 -> { 250 | return R.id.widget_normal_room6 251 | } 252 | else -> { 253 | return R.id.widget_normal_room1 254 | } 255 | } 256 | } 257 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/widget_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable-v24/widget_back.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/widget_bg_title.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/widget_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable-v24/widget_down.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/widget_fresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable-v24/widget_fresh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/widget_front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable-v24/widget_front.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/widget_ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/widget_little_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable-v24/widget_little_example.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/widget_little_trans_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable-v24/widget_little_trans_example.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/widget_normal_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable-v24/widget_normal_example.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/widget_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable-v24/widget_up.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable/widget_back.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_bg_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_bg_savebt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_bg_title.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable/widget_down.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_fresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable/widget_fresh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable/widget_front.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_little_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable/widget_little_example.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_little_trans_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable/widget_little_trans_example.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_normal_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable/widget_normal_example.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/widget_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zzzia/widget/541037a26cc607ec4a3e30ebc123a9a7ceba2338/app/src/main/res/drawable/widget_up.png -------------------------------------------------------------------------------- /app/src/main/res/layout/widget_activity_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 16 | 22 | 23 | 29 | 30 | 34 | 35 | 43 | 44 | 48 | 49 | 55 | 56 | 57 | 62 | 63 | 71 | 72 | 76 | 77 | 83 | 84 | 85 | 90 | 91 | 99 | 100 | 104 | 105 | 111 | 112 | 113 | 114 | 115 | 121 | 122 | 134 | 135 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /app/src/main/res/layout/widget_activity_little_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/widget_activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 22 | 23 | 29 | 30 | 31 |