Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

<div align="center">
<a href="https://techpie.geekpie.club">
<img src="./assets/logo/Logo-1.png" alt="TechPie logo" style="border-radius:50%"/>
Expand Down Expand Up @@ -78,12 +77,12 @@ export HOS_SDK_HOME="$HOME/dev/command-line-tools/sdk"
- macOS
- Xcode
- CocoaPods
- iOS Deployment Target 13.0
- iOS Deployment Target 14.0

### HarmonyOS

- Flutter (OHOS patch) 3.27.5-ohos-1.0.5
- [Huawei Command Tools 6.1.1 Beta1](https://developer.huawei.com/consumer/cn/download/command-line-tools-for-hmos)
- [Huawei Command Tools 6.1.1 Beta1](https://developer.huawei.com/consumer/cn/download/command-line-tools-for-hmos)

## License

Expand Down
2 changes: 1 addition & 1 deletion ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
platform :ios, '13.0'
platform :ios, '14.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
8 changes: 7 additions & 1 deletion ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ PODS:
- Flutter (1.0.0)
- flutter_secure_storage (6.0.0):
- Flutter
- home_widget (0.0.1):
- Flutter
- open_filex (0.0.2):
- Flutter
- package_info_plus (0.4.5):
Expand All @@ -21,6 +23,7 @@ PODS:
DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- home_widget (from `.symlinks/plugins/home_widget/ios`)
- open_filex (from `.symlinks/plugins/open_filex/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
Expand All @@ -33,6 +36,8 @@ EXTERNAL SOURCES:
:path: Flutter
flutter_secure_storage:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
home_widget:
:path: ".symlinks/plugins/home_widget/ios"
open_filex:
:path: ".symlinks/plugins/open_filex/ios"
package_info_plus:
Expand All @@ -49,13 +54,14 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
home_widget: 54b4f6b36ed8d64cfee594a476225c35c3e45091
open_filex: 432f3cd11432da3e39f47fcc0df2b1603854eff1
package_info_plus: 566e1b7a2f3900e4b0020914ad3fc051dcc95596
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
webview_flutter_wkwebview: 1821ceac936eba6f7984d89a9f3bcb4dea99ebb2

PODFILE CHECKSUM: 251cb053df7158f337c0712f2ab29f4e0fa474ce
PODFILE CHECKSUM: e30f02f9d1c72c47bb6344a0a748c9d268180865

COCOAPODS: 1.16.2
268 changes: 256 additions & 12 deletions ios/Runner.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions ios/Runner/Runner.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.example.techpie</string>
</array>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
35 changes: 35 additions & 0 deletions ios/TechPieWidget/Assets.xcassets/AppIcon.appiconset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
6 changes: 6 additions & 0 deletions ios/TechPieWidget/Assets.xcassets/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
11 changes: 11 additions & 0 deletions ios/TechPieWidget/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string>
</dict>
</dict>
</plist>
215 changes: 215 additions & 0 deletions ios/TechPieWidget/TechPieWidget.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
import WidgetKit
import SwiftUI

struct Provider: TimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), courses: [], assignments: [])
}

func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(),
courses: WidgetDataManager.shared.courses,
assignments: WidgetDataManager.shared.assignments)
completion(entry)
}

func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> ()) {
let entry = SimpleEntry(date: Date(),
courses: WidgetDataManager.shared.courses,
assignments: WidgetDataManager.shared.assignments)

// Update widget occasionally or when told by Flutter
let nextUpdate = Calendar.current.date(byAdding: .minute, value: 30, to: Date()) ?? Date().addingTimeInterval(30 * 60)
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
}
}

struct SimpleEntry: TimelineEntry {
let date: Date
let courses: [Course]
let assignments: [Assignment]
}

struct TechPieWidgetEntryView : View {
var entry: Provider.Entry
@Environment(\.widgetFamily) var family

var body: some View {
ZStack {
VStack(alignment: .leading, spacing: 10) {
if family == .systemSmall {
smallView
} else {
mediumView
}
}
.padding()
}
}

var smallView: some View {
VStack(alignment: .leading, spacing: 8) {
HStack {
Image(systemName: "book.fill")
.foregroundColor(.blue)
Text("Next Class")
.font(.headline)
}

if let firstClass = entry.courses.first {
Text(firstClass.name)
.font(.subheadline)
.fontWeight(.bold)
.lineLimit(2)
Text("\(firstClass.startTime) - \(firstClass.endTime)")
.font(.caption)
.foregroundColor(.secondary)
Text(firstClass.location)
.font(.caption)
.foregroundColor(.secondary)
} else {
Text("No more classes today! 🎉")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}

var mediumView: some View {
HStack(alignment: .top, spacing: 16) {
// Left Side: Schedule
VStack(alignment: .leading, spacing: 8) {
HStack {
Image(systemName: "calendar")
.foregroundColor(.blue)
Text("Today's Classes")
.font(.headline)
}

if entry.courses.isEmpty {
Text("Free day! 🎉")
.font(.subheadline)
.foregroundColor(.secondary)
} else {
ForEach(entry.courses.prefix(2)) { course in
VStack(alignment: .leading, spacing: 2) {
Text(course.name)
.font(.subheadline)
.fontWeight(.medium)
.lineLimit(1)
Text("\(course.startTime) | \(course.location)")
.font(.caption2)
.foregroundColor(.secondary)
}
}
}
}
.frame(maxWidth: .infinity, alignment: .leading)

Divider()

// Right Side: Assignments
VStack(alignment: .leading, spacing: 8) {
HStack {
Image(systemName: "doc.text.fill")
.foregroundColor(.orange)
Text("Pending Work")
.font(.headline)
}

if entry.assignments.isEmpty {
Text("All caught up! ✅")
.font(.subheadline)
.foregroundColor(.secondary)
} else {
ForEach(entry.assignments.prefix(2)) { assignment in
VStack(alignment: .leading, spacing: 2) {
Text(assignment.title)
.font(.subheadline)
.fontWeight(.medium)
.lineLimit(1)
Text(assignment.course)
.font(.caption2)
.foregroundColor(.secondary)
.lineLimit(1)
}
}
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}

struct TechPieWidget: Widget {
let kind: String = "TechPieWidget"

var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
if #available(iOS 17.0, *) {
TechPieWidgetEntryView(entry: entry)
.containerBackground(Color(UIColor.systemBackground), for: .widget)
} else {
TechPieWidgetEntryView(entry: entry)
.background(Color(UIColor.systemBackground))
}
}
.configurationDisplayName("TechPie Tracker")
.description("Track your daily schedule and pending assignments.")
.supportedFamilies([.systemSmall, .systemMedium])
}
}

// MARK: - Data Models

struct Course: Codable, Identifiable {
var id: String { name + startTime }
let name: String
let location: String
let startTime: String
let endTime: String
}

struct Assignment: Codable, Identifiable {
var id: String { title + course }
let title: String
let course: String
let due: String
}

class WidgetDataManager {
static let shared = WidgetDataManager()
// IMPORTANT: Match this with your App Group ID configured in Xcode
let appGroupID = "group.com.example.techpie"

var courses: [Course] {
guard let defaults = UserDefaults(suiteName: appGroupID),
let dataString = defaults.string(forKey: "schedule_data"),
let data = dataString.data(using: .utf8) else {
return []
}

do {
return try JSONDecoder().decode([Course].self, from: data)
} catch {
print("Error decoding courses: \(error)")
return []
}
}

var assignments: [Assignment] {
guard let defaults = UserDefaults(suiteName: appGroupID),
let dataString = defaults.string(forKey: "assignment_data"),
let data = dataString.data(using: .utf8) else {
return []
}

do {
return try JSONDecoder().decode([Assignment].self, from: data)
} catch {
print("Error decoding assignments: \(error)")
return []
}
}
}
16 changes: 16 additions & 0 deletions ios/TechPieWidget/TechPieWidgetBundle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// TechPieWidgetBundle.swift
// TechPieWidget
//
// Created by Henry Li on 2026/6/11.
//

import WidgetKit
import SwiftUI

@main
struct TechPieWidgetBundle: WidgetBundle {
var body: some Widget {
TechPieWidget()
}
}
Loading
Loading