Skip to content

Commit

Permalink
add home widget
Browse files Browse the repository at this point in the history
  • Loading branch information
luckyvermeil committed Feb 23, 2022
1 parent 0625db0 commit ca57027
Show file tree
Hide file tree
Showing 15 changed files with 309 additions and 9 deletions.
5 changes: 5 additions & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ if (flutterVersionName == null) {
}

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

def keystorePropertiesFile = rootProject.file("key.properties")
Expand All @@ -43,6 +44,10 @@ android {
versionName flutterVersionName
}

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}

signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
Expand Down
19 changes: 19 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,25 @@
android:usesCleartextTraffic="true"
android:extractNativeLibs="true">
<!-- CNM 这也太吓人了 升级个gradle 6.5 包体积直接从25M翻倍到50+M 就nm离谱... -->
<!--小部件-->
<receiver android:name="SingleCourseWidgetProvider"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/single_course_widget" />
</receiver>
<!-- Used for Background Work -->
<receiver android:name="es.antonborri.home_widget.HomeWidgetBackgroundReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.lilystudio.wheretosleepinnju.action.BACKGROUND" />
</intent-filter>
</receiver>
<service android:name="es.antonborri.home_widget.HomeWidgetBackgroundService"
android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true"/>
<!--小部件End-->
<activity
android:name=".MainActivity"
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.lilystudio.wheretosleepinnju

import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import android.widget.RemoteViews
import es.antonborri.home_widget.HomeWidgetBackgroundIntent
import es.antonborri.home_widget.HomeWidgetLaunchIntent
import es.antonborri.home_widget.HomeWidgetProvider

class SingleCourseWidgetProvider : HomeWidgetProvider() {

override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, widgetData: SharedPreferences) {
appWidgetIds.forEach { widgetId ->
val views = RemoteViews(context.packageName, R.layout.single_course_layout).apply {
// Open App on Widget Click
val pendingIntent = HomeWidgetLaunchIntent.getActivity(
context,
MainActivity::class.java)
setOnClickPendingIntent(R.id.widget_container, pendingIntent)

// Swap Title Text by calling Dart Code in the Background
setTextViewText(R.id.widget_title, widgetData.getString("title", null)
?: "")
val backgroundIntent = HomeWidgetBackgroundIntent.getBroadcast(
context,
Uri.parse("singleCourseWidget://titleClicked")
)
setOnClickPendingIntent(R.id.widget_title, backgroundIntent)

val content = widgetData.getString("content", null)
setTextViewText(R.id.widget_content, content
?: "No Content")
// Detect App opened via Click inside Flutter
val pendingIntentWithData = HomeWidgetLaunchIntent.getActivity(
context,
MainActivity::class.java,
Uri.parse("singleCourseWidget://message?message=$content"))
setOnClickPendingIntent(R.id.widget_content, pendingIntentWithData)
}

appWidgetManager.updateAppWidget(widgetId, views)
}
}
}
5 changes: 5 additions & 0 deletions android/app/src/main/res/drawable/widget_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF"/>
<corners android:radius="16dp"/>
</shape>
29 changes: 29 additions & 0 deletions android/app/src/main/res/layout/single_course_layout.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="0dp"
android:orientation="vertical"
android:background="@drawable/widget_background"
android:padding="8dp"
android:id="@+id/widget_container">

<TextView
android:id="@+id/widget_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textStyle="bold"
android:lines="1"
tools:text="Title" />

<TextView
android:id="@+id/widget_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:lines="1"
tools:text="Content" />
</LinearLayout>
9 changes: 9 additions & 0 deletions android/app/src/main/res/xml/single_course_widget.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="180dp"
android:minHeight="40dp"
android:updatePeriodMillis="900000"
android:initialLayout="@layout/single_course_layout"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen">
</appwidget-provider>
151 changes: 151 additions & 0 deletions lib/Utils/HomeWidgetUtil.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:home_widget/home_widget.dart';
import 'package:path/path.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sqflite/sqflite.dart';
import 'package:wheretosleepinnju/Resources/Constant.dart';
import '../Models/CourseModel.dart';
import '../Models/Db/DbHelper.dart';

class HomeWidgetUtil {
final now = DateTime.now();

// final now = DateTime.parse("1970-01-01 17:00:00");

updateWidget() async {
int nowWeek = await _getWeekOrder();
int tableId = await _getClassTableId();
String title = "";
String content = "";
List tac = await _getNextCourse(tableId, nowWeek);
title = tac[0];
content = tac[1];
return Future.wait<bool?>([
HomeWidget.saveWidgetData(
'title',
title,
),
HomeWidget.saveWidgetData(
'content',
content,
),
HomeWidget.updateWidget(
name: 'SingleCourseWidgetProvider',
androidName: 'SingleCourseWidgetProvider',
iOSName: 'SingleCourseWidget',
),
]).then((value) {
return !value.contains(false);
});
}

_getCoursesToday(int tableId, nowWeek) async {
List<Course> coursesToday = [];
List<Course> allCourses = [];
List allCoursesMap = [];
dynamic dbbasePath = await getDatabasesPath();
String path = join(dbbasePath, DbHelper.DATABASE_NAME);
Database db = await openDatabase(path, readOnly: true);

List<Map> rst = await db.query(DbHelper.COURSE_TABLE_NAME,
where: '${DbHelper.COURSE_COLUMN_COURSETABLEID} = ?',
whereArgs: [tableId]);
allCoursesMap = rst.toList();
// List allCoursesMap = await CourseProvider().getAllCourses(tableId);

for (Map<String, dynamic> courseMap in allCoursesMap) {
allCourses.add(Course.fromMap(courseMap));
}
for (Course course in allCourses) {
List weeks = json.decode(course.weeks!);
if (weeks.contains(nowWeek) && course.weekTime == 1) {
coursesToday.add(course);
}
}
return coursesToday;
}

_getNextCourse(tableId, nowWeek) async {
String title = "";
String content = "";
List<Map> l = Constant.CLASS_TIME_LIST;

List<Course> courses = await _getCoursesToday(tableId, nowWeek);
courses.sort((a, b) => _s2t(l[a.startTime! - 1]['start'])
.compareTo(_s2t(l[b.startTime! - 1]['start'])));

if (courses.isEmpty) {
title = '${now.year}-${now.month}-${now.day}';
content = '今天没有课哦╰(*°▽°*)╯';
} else {
if (now.isBefore(_c2t(courses[0]))) {
// 如果在第一节课之前
title = '${courses[0].name}';
content =
'${l[courses[0].startTime! - 1]['start']}-${l[courses[0].startTime! + courses[0].timeCount! - 1]['end']} 第${courses[0].startTime}-${courses[0].startTime! + courses[0].timeCount!}节 @${courses[0].classroom}';
}
if (now.isAfter(_c2t(courses[courses.length - 1])) &&
now.isBefore(_c2t(courses[courses.length - 1], mode: 'end'))) {
//如果在最后一节课之中
title = '${courses[courses.length - 1].name}';
content =
'${l[courses[courses.length - 1].startTime! - 1]['start']}-${l[courses[courses.length - 1].startTime! + courses[courses.length - 1].timeCount! - 1]['end']} 第${courses[courses.length - 1].startTime}-${courses[courses.length - 1].startTime! + courses[courses.length - 1].timeCount!}节 @${courses[courses.length - 1].classroom}';
}
if (now.isAfter(_c2t(courses[courses.length - 1], mode: 'end'))) {
//如果在最后一节课之后
title = '${now.year}-${now.month}-${now.day}';
content = '今天课上完了哦╰(*°▽°*)╯';
}
for (int i = 1; i < courses.length; i++) {
if (now.isAfter(_c2t(courses[i - 1])) &&
now.isBefore(_c2t(courses[i]))) {
//如果在上节课上课后且在下节课上课前
title = '${courses[i].name}';
content =
'${l[courses[i].startTime! - 1]['start']}-${l[courses[i].startTime! + courses[i].timeCount! - 1]['end']} 第${courses[i].startTime}-${courses[i].startTime! + courses[i].timeCount!}节 @${courses[i].classroom}';
}
}
}

if (kDebugMode) {
title = title + " " + DateTime.now().toString().split('-').last.split('.').first;
}

return [title, content];
}

Future<int> _getWeekOrder() async {
SharedPreferences sp = await SharedPreferences.getInstance();
int? weekIndex = sp.getInt("weekIndex");
if (weekIndex != null) {
return weekIndex;
}
return 1;
}

Future<int> _getClassTableId() async {
SharedPreferences sp = await SharedPreferences.getInstance();
int? classTableIndex = sp.getInt("tableId");
if (classTableIndex != null) {
return classTableIndex;
}
return 0;
}

DateTime _c2t(Course course, {String mode = 'start'}) {
List<Map> l = Constant.CLASS_TIME_LIST;
if (mode == 'start') {
return _s2t(l[course.startTime! - 1]['start']);
} else {
return _s2t(l[course.startTime! + course.timeCount! - 1]['end']);
}
}

DateTime _s2t(String? time24) {
// 22:00格式转换为今天22:00:00的DateTime
String yymmddhhmm = '${now.toString().split(" ").first} $time24:00';
return DateTime.parse(yymmddhhmm);
}
}
2 changes: 1 addition & 1 deletion lib/generated/intl/messages_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ class MessageLookup extends MessageLookupByLibrary {
"online_parse_error_toast":
MessageLookupByLibrary.simpleMessage("导入课表失败,可能是 bug"),
"open_source_library_content": MessageLookupByLibrary.simpleMessage(
"shared_preferences: ^2.0.7\nflutter_swiper_null_safety: ^1.0.2\nscoped_model: ^2.0.0-nullsafety.0\nazlistview: ^2.0.0-nullsafety.0\nwebview_flutter: ^2.0.13\nflutter_linkify: ^5.0.2\nimage_picker: ^0.8.4\npackage_info: ^2.0.2\npath_provider: ^2.0.3\nurl_launcher: ^6.0.10\nflutter_html: ^2.1.3\nfluttertoast: ^8.0.1\nsqflite: ^2.0.0+4\nhtml: ^0.15.0\ndio: ^4.0.0"),
"shared_preferences: ^2.0.7\nflutter_swiper_null_safety: ^1.0.2\nscoped_model: ^2.0.0-nullsafety.0\nazlistview: ^2.0.0-nullsafety.0\nwebview_flutter: ^2.0.13\nflutter_linkify: ^5.0.2\nimage_picker: ^0.8.4\npackage_info: ^2.0.2\npath_provider: ^2.0.3\nurl_launcher: ^6.0.10\nflutter_html: ^2.1.3\nfluttertoast: ^8.0.1\nsqflite: ^2.0.0+4\nhtml: ^0.15.0\ndio: ^4.0.0\nhome_widget: ^0.1.5\nworkmanager: ^0.4.1"),
"open_source_library_title":
MessageLookupByLibrary.simpleMessage("所使用到的开源库"),
"password": MessageLookupByLibrary.simpleMessage("密码"),
Expand Down
2 changes: 1 addition & 1 deletion lib/generated/intl/messages_zh_CN.dart
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ class MessageLookup extends MessageLookupByLibrary {
"online_parse_error_toast":
MessageLookupByLibrary.simpleMessage("导入课表失败,可能是 bug"),
"open_source_library_content": MessageLookupByLibrary.simpleMessage(
"shared_preferences: ^2.0.7\nflutter_swiper_null_safety: ^1.0.2\nscoped_model: ^2.0.0-nullsafety.0\nazlistview: ^2.0.0-nullsafety.0\nwebview_flutter: ^2.0.13\nflutter_linkify: ^5.0.2\nimage_picker: ^0.8.4\npackage_info: ^2.0.2\npath_provider: ^2.0.3\nurl_launcher: ^6.0.10\nflutter_html: ^2.1.3\nfluttertoast: ^8.0.1\nsqflite: ^2.0.0+4\nhtml: ^0.15.0\ndio: ^4.0.0"),
"shared_preferences: ^2.0.7\nflutter_swiper_null_safety: ^1.0.2\nscoped_model: ^2.0.0-nullsafety.0\nazlistview: ^2.0.0-nullsafety.0\nwebview_flutter: ^2.0.13\nflutter_linkify: ^5.0.2\nimage_picker: ^0.8.4\npackage_info: ^2.0.2\npath_provider: ^2.0.3\nurl_launcher: ^6.0.10\nflutter_html: ^2.1.3\nfluttertoast: ^8.0.1\nsqflite: ^2.0.0+4\nhtml: ^0.15.0\ndio: ^4.0.0\nhome_widget: ^0.1.5\nworkmanager: ^0.4.1"),
"open_source_library_title":
MessageLookupByLibrary.simpleMessage("所使用到的开源库"),
"password": MessageLookupByLibrary.simpleMessage("密码"),
Expand Down
4 changes: 2 additions & 2 deletions lib/generated/l10n.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@
"developer": "开发者 idealclover",
"introduction": "博客:https://idealclover.top\nEmail:[email protected]",
"open_source_library_title": "所使用到的开源库",
"open_source_library_content": "shared_preferences: ^2.0.7\nflutter_swiper_null_safety: ^1.0.2\nscoped_model: ^2.0.0-nullsafety.0\nazlistview: ^2.0.0-nullsafety.0\nwebview_flutter: ^2.0.13\nflutter_linkify: ^5.0.2\nimage_picker: ^0.8.4\npackage_info: ^2.0.2\npath_provider: ^2.0.3\nurl_launcher: ^6.0.10\nflutter_html: ^2.1.3\nfluttertoast: ^8.0.1\nsqflite: ^2.0.0+4\nhtml: ^0.15.0\ndio: ^4.0.0",
"open_source_library_content": "shared_preferences: ^2.0.7\nflutter_swiper_null_safety: ^1.0.2\nscoped_model: ^2.0.0-nullsafety.0\nazlistview: ^2.0.0-nullsafety.0\nwebview_flutter: ^2.0.13\nflutter_linkify: ^5.0.2\nimage_picker: ^0.8.4\npackage_info: ^2.0.2\npath_provider: ^2.0.3\nurl_launcher: ^6.0.10\nflutter_html: ^2.1.3\nfluttertoast: ^8.0.1\nsqflite: ^2.0.0+4\nhtml: ^0.15.0\ndio: ^4.0.0\nhome_widget: ^0.1.5\nworkmanager: ^0.4.1", "easter_egg": "感谢小百合工作室\n感谢 @ns @lgt @FengChendian 协助开发\n感谢 @ovoclover 制作图标\n感谢 @无忌 @子枨 提供配色方案\n特别感谢 1A335 三位室友的支持\n感谢各位提供反馈的 NJUers\n谨以此 APP 敬我的大学时光",
"easter_egg": "感谢小百合工作室\n感谢 @ns @lgt @FengChendian 协助开发\n感谢 @ovoclover 制作图标\n感谢 @无忌 @子枨 提供配色方案\n特别感谢 1A335 三位室友的支持\n感谢各位提供反馈的 NJUers\n谨以此 APP 敬我的大学时光",
"love_and_donate": "完美导入!投喂傻翠w",
"bug_and_report": "似乎有bug,我要反馈",
Expand Down
2 changes: 1 addition & 1 deletion lib/l10n/intl_zh_CN.arb
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@
"developer": "开发者 idealclover",
"introduction": "博客:https://idealclover.top\nEmail:[email protected]",
"open_source_library_title": "所使用到的开源库",
"open_source_library_content": "shared_preferences: ^2.0.7\nflutter_swiper_null_safety: ^1.0.2\nscoped_model: ^2.0.0-nullsafety.0\nazlistview: ^2.0.0-nullsafety.0\nwebview_flutter: ^2.0.13\nflutter_linkify: ^5.0.2\nimage_picker: ^0.8.4\npackage_info: ^2.0.2\npath_provider: ^2.0.3\nurl_launcher: ^6.0.10\nflutter_html: ^2.1.3\nfluttertoast: ^8.0.1\nsqflite: ^2.0.0+4\nhtml: ^0.15.0\ndio: ^4.0.0",
"open_source_library_content": "shared_preferences: ^2.0.7\nflutter_swiper_null_safety: ^1.0.2\nscoped_model: ^2.0.0-nullsafety.0\nazlistview: ^2.0.0-nullsafety.0\nwebview_flutter: ^2.0.13\nflutter_linkify: ^5.0.2\nimage_picker: ^0.8.4\npackage_info: ^2.0.2\npath_provider: ^2.0.3\nurl_launcher: ^6.0.10\nflutter_html: ^2.1.3\nfluttertoast: ^8.0.1\nsqflite: ^2.0.0+4\nhtml: ^0.15.0\ndio: ^4.0.0\nhome_widget: ^0.1.5\nworkmanager: ^0.4.1",
"easter_egg": "感谢小百合工作室\n感谢 @ns @lgt @FengChendian 协助开发\n感谢 @ovoclover 制作图标\n感谢 @无忌 @子枨 提供配色方案\n特别感谢 1A335 三位室友的支持\n感谢各位提供反馈的 NJUers\n谨以此 APP 敬我的大学时光",
"love_and_donate": "完美导入!投喂傻翠w",
"bug_and_report": "似乎有bug,我要反馈",
Expand Down
Loading

0 comments on commit ca57027

Please sign in to comment.