Initial commit
Whisper model weights excluded from git — auto-downloaded at first Xcode build via Scripts/download_whisper_model.sh (~600 MB, one-time).
This commit is contained in:
18
LockInBroWidget/AppIntent.swift
Normal file
18
LockInBroWidget/AppIntent.swift
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// AppIntent.swift
|
||||
// LockInBroWidget
|
||||
//
|
||||
// Created by Aditya Pulipaka on 3/28/26.
|
||||
//
|
||||
|
||||
import WidgetKit
|
||||
import AppIntents
|
||||
|
||||
struct ConfigurationAppIntent: WidgetConfigurationIntent {
|
||||
static var title: LocalizedStringResource { "Configuration" }
|
||||
static var description: IntentDescription { "This is an example widget." }
|
||||
|
||||
// An example configurable parameter.
|
||||
@Parameter(title: "Favorite Emoji", default: "😃")
|
||||
var favoriteEmoji: String
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -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
LockInBroWidget/Assets.xcassets/Contents.json
Normal file
6
LockInBroWidget/Assets.xcassets/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
15
LockInBroWidget/Info.plist
Normal file
15
LockInBroWidget/Info.plist
Normal file
@@ -0,0 +1,15 @@
|
||||
<?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>NSSupportsLiveActivities</key>
|
||||
<true/>
|
||||
<key>NSSupportsLiveActivitiesFrequentUpdates</key>
|
||||
<true/>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.widgetkit-extension</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
88
LockInBroWidget/LockInBroWidget.swift
Normal file
88
LockInBroWidget/LockInBroWidget.swift
Normal file
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// LockInBroWidget.swift
|
||||
// LockInBroWidget
|
||||
//
|
||||
// Created by Aditya Pulipaka on 3/28/26.
|
||||
//
|
||||
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
struct Provider: AppIntentTimelineProvider {
|
||||
func placeholder(in context: Context) -> SimpleEntry {
|
||||
SimpleEntry(date: Date(), configuration: ConfigurationAppIntent())
|
||||
}
|
||||
|
||||
func snapshot(for configuration: ConfigurationAppIntent, in context: Context) async -> SimpleEntry {
|
||||
SimpleEntry(date: Date(), configuration: configuration)
|
||||
}
|
||||
|
||||
func timeline(for configuration: ConfigurationAppIntent, in context: Context) async -> Timeline<SimpleEntry> {
|
||||
var entries: [SimpleEntry] = []
|
||||
|
||||
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
|
||||
let currentDate = Date()
|
||||
for hourOffset in 0 ..< 5 {
|
||||
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
|
||||
let entry = SimpleEntry(date: entryDate, configuration: configuration)
|
||||
entries.append(entry)
|
||||
}
|
||||
|
||||
return Timeline(entries: entries, policy: .atEnd)
|
||||
}
|
||||
|
||||
// func relevances() async -> WidgetRelevances<ConfigurationAppIntent> {
|
||||
// // Generate a list containing the contexts this widget is relevant in.
|
||||
// }
|
||||
}
|
||||
|
||||
struct SimpleEntry: TimelineEntry {
|
||||
let date: Date
|
||||
let configuration: ConfigurationAppIntent
|
||||
}
|
||||
|
||||
struct LockInBroWidgetEntryView : View {
|
||||
var entry: Provider.Entry
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Time:")
|
||||
Text(entry.date, style: .time)
|
||||
|
||||
Text("Favorite Emoji:")
|
||||
Text(entry.configuration.favoriteEmoji)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LockInBroWidget: Widget {
|
||||
let kind: String = "LockInBroWidget"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
AppIntentConfiguration(kind: kind, intent: ConfigurationAppIntent.self, provider: Provider()) { entry in
|
||||
LockInBroWidgetEntryView(entry: entry)
|
||||
.containerBackground(.fill.tertiary, for: .widget)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ConfigurationAppIntent {
|
||||
fileprivate static var smiley: ConfigurationAppIntent {
|
||||
let intent = ConfigurationAppIntent()
|
||||
intent.favoriteEmoji = "😀"
|
||||
return intent
|
||||
}
|
||||
|
||||
fileprivate static var starEyes: ConfigurationAppIntent {
|
||||
let intent = ConfigurationAppIntent()
|
||||
intent.favoriteEmoji = "🤩"
|
||||
return intent
|
||||
}
|
||||
}
|
||||
|
||||
#Preview(as: .systemSmall) {
|
||||
LockInBroWidget()
|
||||
} timeline: {
|
||||
SimpleEntry(date: .now, configuration: .smiley)
|
||||
SimpleEntry(date: .now, configuration: .starEyes)
|
||||
}
|
||||
18
LockInBroWidget/LockInBroWidgetBundle.swift
Normal file
18
LockInBroWidget/LockInBroWidgetBundle.swift
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// LockInBroWidgetBundle.swift
|
||||
// LockInBroWidget
|
||||
//
|
||||
// Created by Aditya Pulipaka on 3/28/26.
|
||||
//
|
||||
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct LockInBroWidgetBundle: WidgetBundle {
|
||||
var body: some Widget {
|
||||
LockInBroWidget()
|
||||
LockInBroWidgetControl()
|
||||
LockInBroWidgetLiveActivity()
|
||||
}
|
||||
}
|
||||
77
LockInBroWidget/LockInBroWidgetControl.swift
Normal file
77
LockInBroWidget/LockInBroWidgetControl.swift
Normal file
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// LockInBroWidgetControl.swift
|
||||
// LockInBroWidget
|
||||
//
|
||||
// Created by Aditya Pulipaka on 3/28/26.
|
||||
//
|
||||
|
||||
import AppIntents
|
||||
import SwiftUI
|
||||
import WidgetKit
|
||||
|
||||
struct LockInBroWidgetControl: ControlWidget {
|
||||
static let kind: String = "com.adipu.LockInBroMobile.LockInBroWidget"
|
||||
|
||||
var body: some ControlWidgetConfiguration {
|
||||
AppIntentControlConfiguration(
|
||||
kind: Self.kind,
|
||||
provider: Provider()
|
||||
) { value in
|
||||
ControlWidgetToggle(
|
||||
"Start Timer",
|
||||
isOn: value.isRunning,
|
||||
action: StartTimerIntent(value.name)
|
||||
) { isRunning in
|
||||
Label(isRunning ? "On" : "Off", systemImage: "timer")
|
||||
}
|
||||
}
|
||||
.displayName("Timer")
|
||||
.description("A an example control that runs a timer.")
|
||||
}
|
||||
}
|
||||
|
||||
extension LockInBroWidgetControl {
|
||||
struct Value {
|
||||
var isRunning: Bool
|
||||
var name: String
|
||||
}
|
||||
|
||||
struct Provider: AppIntentControlValueProvider {
|
||||
func previewValue(configuration: TimerConfiguration) -> Value {
|
||||
LockInBroWidgetControl.Value(isRunning: false, name: configuration.timerName)
|
||||
}
|
||||
|
||||
func currentValue(configuration: TimerConfiguration) async throws -> Value {
|
||||
let isRunning = true // Check if the timer is running
|
||||
return LockInBroWidgetControl.Value(isRunning: isRunning, name: configuration.timerName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TimerConfiguration: ControlConfigurationIntent {
|
||||
static let title: LocalizedStringResource = "Timer Name Configuration"
|
||||
|
||||
@Parameter(title: "Timer Name", default: "Timer")
|
||||
var timerName: String
|
||||
}
|
||||
|
||||
struct StartTimerIntent: SetValueIntent {
|
||||
static let title: LocalizedStringResource = "Start a timer"
|
||||
|
||||
@Parameter(title: "Timer Name")
|
||||
var name: String
|
||||
|
||||
@Parameter(title: "Timer is running")
|
||||
var value: Bool
|
||||
|
||||
init() {}
|
||||
|
||||
init(_ name: String) {
|
||||
self.name = name
|
||||
}
|
||||
|
||||
func perform() async throws -> some IntentResult {
|
||||
// Start the timer…
|
||||
return .result()
|
||||
}
|
||||
}
|
||||
120
LockInBroWidget/LockInBroWidgetLiveActivity.swift
Normal file
120
LockInBroWidget/LockInBroWidgetLiveActivity.swift
Normal file
@@ -0,0 +1,120 @@
|
||||
//
|
||||
// LockInBroWidgetLiveActivity.swift
|
||||
// LockInBroWidget
|
||||
//
|
||||
|
||||
import ActivityKit
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
struct LockInBroWidgetLiveActivity: Widget {
|
||||
var body: some WidgetConfiguration {
|
||||
ActivityConfiguration(for: FocusSessionAttributes.self) { context in
|
||||
// Lock screen/banner UI
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
HStack {
|
||||
Image(systemName: "clock.fill")
|
||||
.foregroundStyle(.blue)
|
||||
Text("Focus Mode")
|
||||
.font(.headline)
|
||||
.fontWeight(.bold)
|
||||
Spacer()
|
||||
Text(Date(timeIntervalSince1970: TimeInterval(context.state.startedAt)), style: .timer)
|
||||
.font(.title3.monospacedDigit())
|
||||
.foregroundStyle(.blue)
|
||||
.multilineTextAlignment(.trailing)
|
||||
}
|
||||
Text(context.state.taskTitle)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(1)
|
||||
if context.state.stepsTotal > 0 {
|
||||
HStack(spacing: 6) {
|
||||
ProgressView(value: Double(context.state.stepsCompleted), total: Double(context.state.stepsTotal))
|
||||
.tint(.blue)
|
||||
Text("\(context.state.stepsCompleted)/\(context.state.stepsTotal)")
|
||||
.font(.caption2.monospacedDigit())
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
if let step = context.state.currentStepTitle {
|
||||
Text("Now: \(step)")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.white.opacity(0.7))
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.activityBackgroundTint(Color.black.opacity(0.8))
|
||||
.activitySystemActionForegroundColor(Color.white)
|
||||
|
||||
} dynamicIsland: { context in
|
||||
DynamicIsland {
|
||||
// Expanded UI
|
||||
DynamicIslandExpandedRegion(.leading) {
|
||||
Label("Focus", systemImage: "clock.fill")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.blue)
|
||||
}
|
||||
DynamicIslandExpandedRegion(.trailing) {
|
||||
Text(Date(timeIntervalSince1970: TimeInterval(context.state.startedAt)), style: .timer)
|
||||
.font(.caption.monospacedDigit())
|
||||
.foregroundStyle(.blue)
|
||||
}
|
||||
DynamicIslandExpandedRegion(.center) {
|
||||
Text(context.state.taskTitle)
|
||||
.font(.subheadline)
|
||||
.lineLimit(1)
|
||||
}
|
||||
DynamicIslandExpandedRegion(.bottom) {
|
||||
if context.state.stepsTotal > 0 {
|
||||
Text("\(context.state.stepsCompleted)/\(context.state.stepsTotal) steps — \(context.state.currentStepTitle ?? "Stay locked in!")")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(1)
|
||||
} else {
|
||||
Text("Stay locked in!")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
} compactLeading: {
|
||||
Image(systemName: "clock.fill")
|
||||
.foregroundStyle(.blue)
|
||||
} compactTrailing: {
|
||||
Text(Date(timeIntervalSince1970: TimeInterval(context.state.startedAt)), style: .timer)
|
||||
.font(.caption2.monospacedDigit())
|
||||
.frame(maxWidth: 40)
|
||||
} minimal: {
|
||||
Image(systemName: "clock.fill")
|
||||
.foregroundStyle(.blue)
|
||||
}
|
||||
.widgetURL(URL(string: "lockinbro://resume-session"))
|
||||
.keylineTint(Color.blue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FocusSessionAttributes {
|
||||
fileprivate static var preview: FocusSessionAttributes {
|
||||
FocusSessionAttributes(sessionType: "Focus")
|
||||
}
|
||||
}
|
||||
|
||||
extension FocusSessionAttributes.ContentState {
|
||||
fileprivate static var dummy: FocusSessionAttributes.ContentState {
|
||||
FocusSessionAttributes.ContentState(
|
||||
taskTitle: "Finish Physics Assignment",
|
||||
startedAt: Int(Date().addingTimeInterval(-120).timeIntervalSince1970),
|
||||
stepsCompleted: 2,
|
||||
stepsTotal: 5,
|
||||
currentStepTitle: "Solve problem set 3"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview("Notification", as: .content, using: FocusSessionAttributes.preview) {
|
||||
LockInBroWidgetLiveActivity()
|
||||
} contentStates: {
|
||||
FocusSessionAttributes.ContentState.dummy
|
||||
}
|
||||
Reference in New Issue
Block a user