ruby-macrodroid 0.8.8 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -2
- data/lib/ruby-macrodroid.rb +65 -501
- data/lib/ruby-macrodroid/actions.rb +176 -60
- data/lib/ruby-macrodroid/base.rb +18 -4
- data/lib/ruby-macrodroid/constraints.rb +1338 -0
- data/lib/ruby-macrodroid/macro.rb +630 -0
- data/lib/ruby-macrodroid/triggers.rb +31 -6
- metadata +12 -10
- metadata.gz.sig +0 -0
@@ -0,0 +1,1338 @@
|
|
1
|
+
# file: ruby-macrodroid/constraints.rb
|
2
|
+
|
3
|
+
# This file contains the following classes:
|
4
|
+
#
|
5
|
+
#
|
6
|
+
# ## Constraint classes
|
7
|
+
#
|
8
|
+
# Constraint TimeOfDayConstraint BatteryLevelConstraint
|
9
|
+
# BatterySaverStateConstraint BatteryTemperatureConstraint
|
10
|
+
# ExternalPowerConstraint BluetoothConstraint GPSEnabledConstraint
|
11
|
+
# LocationModeConstraint SignalOnOffConstraint WifiConstraint
|
12
|
+
# CellTowerConstraint IsRoamingConstraint DataOnOffConstraint
|
13
|
+
# WifiHotSpotConstraint CalendarConstraint DayOfWeekConstraint
|
14
|
+
# TimeOfDayConstraint DayOfMonthConstraint MonthOfYearConstraint
|
15
|
+
# SunsetSunriseConstraint AirplaneModeConstraint AutoRotateConstraint
|
16
|
+
# DeviceLockedConstraint RoamingOnOffConstraint TimeSinceBootConstraint
|
17
|
+
# AutoSyncConstraint NFCStateConstraint IsRootedConstraint VpnConstraint
|
18
|
+
# MacroEnabledConstraint ModeConstraint TriggerThatInvokedConstraint
|
19
|
+
# LastRunTimeConstraint HeadphonesConnectionConstraint MusicActiveConstraint
|
20
|
+
# NotificationPresentConstraint PriorityModeConstraint
|
21
|
+
# NotificationVolumeConstraint InCallConstraint PhoneRingingConstraint
|
22
|
+
# BrightnessConstraint VolumeConstraint SpeakerPhoneConstraint
|
23
|
+
# DarkThemeConstraint ScreenOnOffConstraint VolumeLevelConstraint
|
24
|
+
# FaceUpDownConstraint LightLevelConstraint DeviceOrientationConstraint
|
25
|
+
# ProximitySensorConstraint
|
26
|
+
#
|
27
|
+
|
28
|
+
class Constraint < MacroObject
|
29
|
+
|
30
|
+
def initialize(h={})
|
31
|
+
super(h)
|
32
|
+
end
|
33
|
+
|
34
|
+
def match?(detail={}, model=nil)
|
35
|
+
|
36
|
+
detail.select {|k,v| @h.include? k }.all? {|key,value| @h[key] == value}
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
#def to_s()
|
41
|
+
# ''
|
42
|
+
#end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def toggle_match?(key, val)
|
47
|
+
|
48
|
+
if @h[key] == true and val == key.to_s then
|
49
|
+
true
|
50
|
+
elsif @h[key] == false and val != key.to_s
|
51
|
+
true
|
52
|
+
else
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# Category: application
|
62
|
+
#
|
63
|
+
class ActiveApplicationConstraint < Constraint
|
64
|
+
|
65
|
+
def initialize(h={})
|
66
|
+
|
67
|
+
options = {
|
68
|
+
:application_name_list=>["YouTube"],
|
69
|
+
:foreground=>true,
|
70
|
+
:package_name_list=>["com.google.android.youtube"]
|
71
|
+
}
|
72
|
+
|
73
|
+
super(options.merge h)
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_s(colour: false, indent: 0)
|
78
|
+
|
79
|
+
indentx = ' ' * indent
|
80
|
+
mode = @h[:foreground] ? 'foreground' : 'not foreground'
|
81
|
+
apps = @h[:application_name_list]
|
82
|
+
indentx + 'App ' + mode + "\n " + indentx + apps.join(', ') #+ @h.inspect
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_summary(colour: false, indent: 0)
|
86
|
+
|
87
|
+
mode = @h[:foreground] ? 'foreground' : 'not foreground'
|
88
|
+
apps = @h[:application_name_list]
|
89
|
+
'App ' + mode + " (%s)" % apps.join(', ') #+ @h.inspect
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Category: Battery/Power
|
94
|
+
#
|
95
|
+
class BatteryLevelConstraint < Constraint
|
96
|
+
|
97
|
+
def initialize(h={})
|
98
|
+
|
99
|
+
options = {
|
100
|
+
battery_level: 23,
|
101
|
+
equals: false,
|
102
|
+
greater_than: false
|
103
|
+
}
|
104
|
+
|
105
|
+
super(options.merge h)
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_s(colour: false, indent: 0)
|
110
|
+
|
111
|
+
operator = if @h[:greater_than] then
|
112
|
+
'>'
|
113
|
+
elsif @h[:equals]
|
114
|
+
'='
|
115
|
+
else
|
116
|
+
'<'
|
117
|
+
end
|
118
|
+
|
119
|
+
level = @h[:battery_level]
|
120
|
+
|
121
|
+
"Battery %s %s%%" % [operator, level]
|
122
|
+
end
|
123
|
+
|
124
|
+
alias to_summary to_s
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
# Category: Battery/Power
|
129
|
+
#
|
130
|
+
class BatterySaverStateConstraint < Constraint
|
131
|
+
|
132
|
+
def initialize(h={})
|
133
|
+
|
134
|
+
options = {
|
135
|
+
option: 0
|
136
|
+
}
|
137
|
+
|
138
|
+
super(options.merge h)
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
def to_s(colour: false, indent: 0)
|
143
|
+
'BatterySaverStateConstraint ' + @h.inspect
|
144
|
+
end
|
145
|
+
|
146
|
+
alias to_summary to_s
|
147
|
+
end
|
148
|
+
|
149
|
+
# Category: Battery/Power
|
150
|
+
#
|
151
|
+
class BatteryTemperatureConstraint < Constraint
|
152
|
+
|
153
|
+
def initialize(h={})
|
154
|
+
|
155
|
+
options = {
|
156
|
+
equals: false,
|
157
|
+
greater_than: false,
|
158
|
+
temperature: 30
|
159
|
+
}
|
160
|
+
|
161
|
+
super(options.merge h)
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
def to_s(colour: false, indent: 0)
|
166
|
+
'BatteryTemperatureConstraint ' + @h.inspect
|
167
|
+
end
|
168
|
+
|
169
|
+
alias to_summary to_s
|
170
|
+
end
|
171
|
+
|
172
|
+
# Category: Battery/Power
|
173
|
+
#
|
174
|
+
class ExternalPowerConstraint < Constraint
|
175
|
+
|
176
|
+
def initialize(h={})
|
177
|
+
|
178
|
+
options = {
|
179
|
+
external_power: true,
|
180
|
+
power_connected_options: [false, true, false]
|
181
|
+
}
|
182
|
+
|
183
|
+
super(options.merge h)
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
def to_s(colour: false, indent: 0)
|
188
|
+
connection = @h[:external_power] ? 'Connected' : 'Disconnected'
|
189
|
+
'Power ' + connection
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
# Category: Connectivity
|
195
|
+
#
|
196
|
+
class BluetoothConstraint < Constraint
|
197
|
+
|
198
|
+
def initialize(h={})
|
199
|
+
|
200
|
+
options = {
|
201
|
+
any_device: false,
|
202
|
+
bt_state: 0,
|
203
|
+
device_name: 'Any Device'
|
204
|
+
}
|
205
|
+
|
206
|
+
super(options.merge h)
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
def to_s(colour: false, indent: 0)
|
211
|
+
device = @h[:device_name] #== 'Any Device' ? 'Any' : @h[:device_name]
|
212
|
+
"Device Connected\n %s" % device
|
213
|
+
end
|
214
|
+
|
215
|
+
def to_summary(colour: false)
|
216
|
+
device = @h[:device_name] #== 'Any Device' ? 'Any' : @h[:device_name]
|
217
|
+
"Device Connected (%s)" % device
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
# Category: Connectivity
|
223
|
+
#
|
224
|
+
class GPSEnabledConstraint < Constraint
|
225
|
+
|
226
|
+
def initialize(h={})
|
227
|
+
|
228
|
+
options = {
|
229
|
+
enabled: true
|
230
|
+
}
|
231
|
+
|
232
|
+
super(options.merge h)
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
def to_s(colour: false, indent: 0)
|
237
|
+
'GPSEnabledConstraint ' + @h.inspect
|
238
|
+
end
|
239
|
+
|
240
|
+
alias to_summary to_s
|
241
|
+
end
|
242
|
+
|
243
|
+
# Category: Connectivity
|
244
|
+
#
|
245
|
+
class LocationModeConstraint < Constraint
|
246
|
+
|
247
|
+
def initialize(h={})
|
248
|
+
|
249
|
+
options = {
|
250
|
+
options: [false, false, false, true]
|
251
|
+
}
|
252
|
+
|
253
|
+
super(options.merge h)
|
254
|
+
|
255
|
+
end
|
256
|
+
|
257
|
+
def to_s(colour: false, indent: 0)
|
258
|
+
'LocationModeConstraint ' + @h.inspect
|
259
|
+
end
|
260
|
+
|
261
|
+
alias to_summary to_s
|
262
|
+
end
|
263
|
+
|
264
|
+
# Category: Connectivity
|
265
|
+
#
|
266
|
+
class SignalOnOffConstraint < Constraint
|
267
|
+
|
268
|
+
def initialize(h={})
|
269
|
+
|
270
|
+
options = {
|
271
|
+
option: 0
|
272
|
+
}
|
273
|
+
|
274
|
+
super(options.merge h)
|
275
|
+
|
276
|
+
end
|
277
|
+
|
278
|
+
def to_s(colour: false, indent: 0)
|
279
|
+
'SignalOnOffConstraint ' + @h.inspect
|
280
|
+
end
|
281
|
+
|
282
|
+
alias to_summary to_s
|
283
|
+
end
|
284
|
+
|
285
|
+
# Category: Connectivity
|
286
|
+
#
|
287
|
+
class WifiConstraint < Constraint
|
288
|
+
|
289
|
+
|
290
|
+
def initialize(h={})
|
291
|
+
|
292
|
+
options = {
|
293
|
+
ssid_list: [],
|
294
|
+
wifi_state: 0
|
295
|
+
}
|
296
|
+
|
297
|
+
super(options.merge h)
|
298
|
+
|
299
|
+
end
|
300
|
+
|
301
|
+
def to_s(colour: false, indent: 0)
|
302
|
+
|
303
|
+
state = ['Enabled','Disabled','Connected', 'Not Connected'][@h[:wifi_state]]
|
304
|
+
@s = 'Wifi ' + state + ': '
|
305
|
+
|
306
|
+
if @h[:ssid_list].length > 1 then
|
307
|
+
@s += "[%s]" % @h[:ssid_list].join(', ')
|
308
|
+
elsif @h[:ssid_list].length > 0
|
309
|
+
@s += @h[:ssid_list].first
|
310
|
+
end
|
311
|
+
|
312
|
+
end
|
313
|
+
|
314
|
+
alias to_summary to_s
|
315
|
+
end
|
316
|
+
|
317
|
+
# Category: Connectivity
|
318
|
+
#
|
319
|
+
class CellTowerConstraint < Constraint
|
320
|
+
|
321
|
+
def initialize(h={})
|
322
|
+
|
323
|
+
options = {
|
324
|
+
cell_group_name: 'test group',
|
325
|
+
cell_ids: ["524,14,41070731"],
|
326
|
+
in_range: true
|
327
|
+
}
|
328
|
+
|
329
|
+
super(options.merge h)
|
330
|
+
|
331
|
+
end
|
332
|
+
|
333
|
+
def to_s(colour: false, indent: 0)
|
334
|
+
'CellTowerConstraint ' + @h.inspect
|
335
|
+
end
|
336
|
+
|
337
|
+
alias to_summary to_s
|
338
|
+
end
|
339
|
+
|
340
|
+
# Category: Connectivity
|
341
|
+
#
|
342
|
+
class IsRoamingConstraint < Constraint
|
343
|
+
|
344
|
+
def initialize(h={})
|
345
|
+
|
346
|
+
options = {
|
347
|
+
is_roaming: true
|
348
|
+
}
|
349
|
+
|
350
|
+
super(options.merge h)
|
351
|
+
|
352
|
+
end
|
353
|
+
|
354
|
+
def to_s(colour: false, indent: 0)
|
355
|
+
'IsRoamingConstraint ' + @h.inspect
|
356
|
+
end
|
357
|
+
|
358
|
+
alias to_summary to_s
|
359
|
+
end
|
360
|
+
|
361
|
+
# Category: Connectivity
|
362
|
+
#
|
363
|
+
class DataOnOffConstraint < Constraint
|
364
|
+
|
365
|
+
def initialize(h={})
|
366
|
+
|
367
|
+
options = {
|
368
|
+
data_on: true
|
369
|
+
}
|
370
|
+
|
371
|
+
super(options.merge h)
|
372
|
+
|
373
|
+
end
|
374
|
+
|
375
|
+
def to_s(colour: false, indent: 0)
|
376
|
+
'DataOnOffConstraint ' + @h.inspect
|
377
|
+
end
|
378
|
+
|
379
|
+
alias to_summary to_s
|
380
|
+
end
|
381
|
+
|
382
|
+
# Category: Connectivity
|
383
|
+
#
|
384
|
+
class WifiHotSpotConstraint < Constraint
|
385
|
+
|
386
|
+
def initialize(h={})
|
387
|
+
|
388
|
+
options = {
|
389
|
+
check_connections: false,
|
390
|
+
comparison_value: 0,
|
391
|
+
connected_count: 0,
|
392
|
+
enabled: true
|
393
|
+
}
|
394
|
+
|
395
|
+
super(options.merge h)
|
396
|
+
|
397
|
+
end
|
398
|
+
|
399
|
+
def to_s(colour: false, indent: 0)
|
400
|
+
'WifiHotSpotConstraint ' + @h.inspect
|
401
|
+
end
|
402
|
+
|
403
|
+
alias to_summary to_s
|
404
|
+
end
|
405
|
+
|
406
|
+
# Category: Date/Time
|
407
|
+
#
|
408
|
+
class CalendarConstraint < Constraint
|
409
|
+
|
410
|
+
def initialize(h={})
|
411
|
+
|
412
|
+
options = {
|
413
|
+
enable_regex: false,
|
414
|
+
availability: 0,
|
415
|
+
calendar_id: '1',
|
416
|
+
calendar_name: 'PC Sync',
|
417
|
+
detail_text: '',
|
418
|
+
entry_set: true,
|
419
|
+
ignore_all_day: false,
|
420
|
+
title_text: ''
|
421
|
+
}
|
422
|
+
|
423
|
+
super(options.merge h)
|
424
|
+
|
425
|
+
end
|
426
|
+
|
427
|
+
def to_s(colour: false, indent: 0)
|
428
|
+
'CalendarConstraint ' + @h.inspect
|
429
|
+
end
|
430
|
+
|
431
|
+
alias to_summary to_s
|
432
|
+
end
|
433
|
+
|
434
|
+
# Category: Date/Time
|
435
|
+
#
|
436
|
+
class DayOfWeekConstraint < Constraint
|
437
|
+
|
438
|
+
def initialize(h={})
|
439
|
+
|
440
|
+
options = {
|
441
|
+
days_of_week: [false, false, true, false, false, false, false]
|
442
|
+
}
|
443
|
+
|
444
|
+
super(options.merge h)
|
445
|
+
|
446
|
+
end
|
447
|
+
|
448
|
+
def to_s(colour: false, indent: 0)
|
449
|
+
'DayOfWeekConstraint ' + @h.inspect
|
450
|
+
end
|
451
|
+
|
452
|
+
alias to_summary to_s
|
453
|
+
end
|
454
|
+
|
455
|
+
# Category: Date/Time
|
456
|
+
#
|
457
|
+
class StopWatchConstraint < Constraint
|
458
|
+
|
459
|
+
def initialize(h={})
|
460
|
+
|
461
|
+
options = {
|
462
|
+
days_of_week: [false, false, true, false, false, false, false]
|
463
|
+
}
|
464
|
+
|
465
|
+
super(options.merge h)
|
466
|
+
|
467
|
+
end
|
468
|
+
|
469
|
+
def to_s(colour: false, indent: 0)
|
470
|
+
#'StopWatchConstraint ' + @h.inspect
|
471
|
+
operator = @h[:greater_than] ? '>' : '<'
|
472
|
+
"%s %s %s" % [@h[:stopwatch_name], operator, @h[:variable_name] ] #+ @h.inspect
|
473
|
+
end
|
474
|
+
|
475
|
+
alias to_summary to_s
|
476
|
+
end
|
477
|
+
|
478
|
+
# Category: Date/Time
|
479
|
+
#
|
480
|
+
class TimeOfDayConstraint < Constraint
|
481
|
+
|
482
|
+
def initialize(h={})
|
483
|
+
|
484
|
+
options = {
|
485
|
+
end_hour: 8,
|
486
|
+
end_minute: 0,
|
487
|
+
start_hour: 22,
|
488
|
+
start_minute: 0
|
489
|
+
}
|
490
|
+
|
491
|
+
super(options.merge h)
|
492
|
+
|
493
|
+
end
|
494
|
+
|
495
|
+
def to_s(colour: false, indent: 0)
|
496
|
+
a = @h[:start_hour], @h[:start_minute], @h[:end_hour], @h[:start_minute]
|
497
|
+
'Time of Day ' + "%02d:%02d - %02d:%02d" % a
|
498
|
+
end
|
499
|
+
|
500
|
+
alias to_summary to_s
|
501
|
+
end
|
502
|
+
|
503
|
+
# Category: Date/Time
|
504
|
+
#
|
505
|
+
class DayOfMonthConstraint < Constraint
|
506
|
+
|
507
|
+
def initialize(h={})
|
508
|
+
|
509
|
+
options = {
|
510
|
+
day_names: ["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"],
|
511
|
+
days_of_month: [false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
|
512
|
+
}
|
513
|
+
|
514
|
+
super(options.merge h)
|
515
|
+
|
516
|
+
end
|
517
|
+
|
518
|
+
def to_s(colour: false, indent: 0)
|
519
|
+
'DayOfMonthConstraint ' + @h.inspect
|
520
|
+
end
|
521
|
+
|
522
|
+
alias to_summary to_s
|
523
|
+
end
|
524
|
+
|
525
|
+
# Category: Date/Time
|
526
|
+
#
|
527
|
+
class MonthOfYearConstraint < Constraint
|
528
|
+
|
529
|
+
def initialize(h={})
|
530
|
+
|
531
|
+
options = {
|
532
|
+
months: [false, false, false, false, false, false, false, true, false, false, false, false]
|
533
|
+
}
|
534
|
+
|
535
|
+
super(options.merge h)
|
536
|
+
|
537
|
+
end
|
538
|
+
|
539
|
+
def to_s(colour: false, indent: 0)
|
540
|
+
'MonthOfYearConstraint ' + @h.inspect
|
541
|
+
end
|
542
|
+
|
543
|
+
alias to_summary to_s
|
544
|
+
end
|
545
|
+
|
546
|
+
# Category: Date/Time
|
547
|
+
#
|
548
|
+
class SunsetSunriseConstraint < Constraint
|
549
|
+
|
550
|
+
def initialize(h={})
|
551
|
+
|
552
|
+
options = {
|
553
|
+
option: 0
|
554
|
+
}
|
555
|
+
|
556
|
+
super(options.merge h)
|
557
|
+
|
558
|
+
end
|
559
|
+
|
560
|
+
def to_s(colour: false, indent: 0)
|
561
|
+
'SunsetSunriseConstraint ' + @h.inspect
|
562
|
+
end
|
563
|
+
|
564
|
+
alias to_summary to_s
|
565
|
+
end
|
566
|
+
|
567
|
+
# Category: Device State
|
568
|
+
#
|
569
|
+
class AirplaneModeConstraint < Constraint
|
570
|
+
|
571
|
+
def initialize(h={})
|
572
|
+
|
573
|
+
options = {
|
574
|
+
enabled: true
|
575
|
+
}
|
576
|
+
|
577
|
+
super(options.merge h)
|
578
|
+
|
579
|
+
end
|
580
|
+
|
581
|
+
def match?(detail={}, model=nil)
|
582
|
+
|
583
|
+
puts 'inside airplaneModeConstraint#match?' if $debug
|
584
|
+
|
585
|
+
if detail.has_key? :enabled then
|
586
|
+
|
587
|
+
puts 'detail has the key' if $debug
|
588
|
+
super(detail)
|
589
|
+
|
590
|
+
elsif model
|
591
|
+
|
592
|
+
if $debug then
|
593
|
+
puts 'checking the model'
|
594
|
+
switch = model.connectivity.airplane_mode.switch
|
595
|
+
puts 'switch: ' + switch.inspect
|
596
|
+
end
|
597
|
+
|
598
|
+
toggle_match?(:enabled, switch)
|
599
|
+
|
600
|
+
end
|
601
|
+
|
602
|
+
end
|
603
|
+
|
604
|
+
def to_pc()
|
605
|
+
status = @h[:enabled] ? 'enabled?' : 'disabled?'
|
606
|
+
'airplane_mode.' + status
|
607
|
+
end
|
608
|
+
|
609
|
+
def to_s(colour: false, indent: 0)
|
610
|
+
|
611
|
+
status = @h[:enabled] ? 'Enabled' : 'Disabled'
|
612
|
+
'Airplane Mode ' + status
|
613
|
+
|
614
|
+
end
|
615
|
+
|
616
|
+
alias to_summary to_s
|
617
|
+
|
618
|
+
end
|
619
|
+
|
620
|
+
# Category: Device State
|
621
|
+
#
|
622
|
+
class AutoRotateConstraint < Constraint
|
623
|
+
|
624
|
+
def initialize(h={})
|
625
|
+
|
626
|
+
options = {
|
627
|
+
enabled: true
|
628
|
+
}
|
629
|
+
|
630
|
+
super(options.merge h)
|
631
|
+
|
632
|
+
end
|
633
|
+
|
634
|
+
def to_s(colour: false, indent: 0)
|
635
|
+
'AutoRotateConstraint ' + @h.inspect
|
636
|
+
end
|
637
|
+
|
638
|
+
alias to_summary to_s
|
639
|
+
end
|
640
|
+
|
641
|
+
# Category: Device State
|
642
|
+
#
|
643
|
+
class DeviceLockedConstraint < Constraint
|
644
|
+
|
645
|
+
def initialize(h={})
|
646
|
+
|
647
|
+
options = {
|
648
|
+
locked: true
|
649
|
+
}
|
650
|
+
|
651
|
+
super(options.merge h)
|
652
|
+
|
653
|
+
end
|
654
|
+
|
655
|
+
def to_s(colour: false, indent: 0)
|
656
|
+
'Device ' + (@h[:locked] ? 'Locked' : 'Unlocked')
|
657
|
+
end
|
658
|
+
|
659
|
+
alias to_summary to_s
|
660
|
+
|
661
|
+
end
|
662
|
+
|
663
|
+
# Category: Device State
|
664
|
+
#
|
665
|
+
class RoamingOnOffConstraint < Constraint
|
666
|
+
|
667
|
+
def initialize(h={})
|
668
|
+
|
669
|
+
options = {
|
670
|
+
roaming_on: true
|
671
|
+
}
|
672
|
+
|
673
|
+
super(options.merge h)
|
674
|
+
|
675
|
+
end
|
676
|
+
|
677
|
+
def to_s(colour: false, indent: 0)
|
678
|
+
'RoamingOnOffConstraint ' + @h.inspect
|
679
|
+
end
|
680
|
+
|
681
|
+
alias to_summary to_s
|
682
|
+
end
|
683
|
+
|
684
|
+
# Category: Device State
|
685
|
+
#
|
686
|
+
class TimeSinceBootConstraint < Constraint
|
687
|
+
|
688
|
+
def initialize(h={})
|
689
|
+
|
690
|
+
options = {
|
691
|
+
less_than: true,
|
692
|
+
time_period_seconds: 10921
|
693
|
+
}
|
694
|
+
|
695
|
+
super(options.merge h)
|
696
|
+
|
697
|
+
end
|
698
|
+
|
699
|
+
def to_s(colour: false, indent: 0)
|
700
|
+
'TimeSinceBootConstraint ' + @h.inspect
|
701
|
+
end
|
702
|
+
|
703
|
+
alias to_summary to_s
|
704
|
+
end
|
705
|
+
|
706
|
+
# Category: Device State
|
707
|
+
#
|
708
|
+
class AutoSyncConstraint < Constraint
|
709
|
+
|
710
|
+
def initialize(h={})
|
711
|
+
|
712
|
+
options = {
|
713
|
+
enabled: false
|
714
|
+
}
|
715
|
+
|
716
|
+
super(options.merge h)
|
717
|
+
|
718
|
+
end
|
719
|
+
|
720
|
+
def to_s(colour: false, indent: 0)
|
721
|
+
'AutoSyncConstraint ' + @h.inspect
|
722
|
+
end
|
723
|
+
|
724
|
+
alias to_summary to_s
|
725
|
+
end
|
726
|
+
|
727
|
+
# Category: Device State
|
728
|
+
#
|
729
|
+
class NFCStateConstraint < Constraint
|
730
|
+
|
731
|
+
def initialize(h={})
|
732
|
+
|
733
|
+
options = {
|
734
|
+
enabled: true
|
735
|
+
}
|
736
|
+
|
737
|
+
super(options.merge h)
|
738
|
+
|
739
|
+
end
|
740
|
+
|
741
|
+
def to_s(colour: false, indent: 0)
|
742
|
+
'NFCStateConstraint ' + @h.inspect
|
743
|
+
end
|
744
|
+
|
745
|
+
alias to_summary to_s
|
746
|
+
end
|
747
|
+
|
748
|
+
# Category: Device State
|
749
|
+
#
|
750
|
+
class IsRootedConstraint < Constraint
|
751
|
+
|
752
|
+
def initialize(h={})
|
753
|
+
|
754
|
+
options = {
|
755
|
+
rooted: true
|
756
|
+
}
|
757
|
+
|
758
|
+
super(options.merge h)
|
759
|
+
|
760
|
+
end
|
761
|
+
|
762
|
+
def to_s(colour: false, indent: 0)
|
763
|
+
'IsRootedConstraint ' + @h.inspect
|
764
|
+
end
|
765
|
+
|
766
|
+
alias to_summary to_s
|
767
|
+
end
|
768
|
+
|
769
|
+
# Category: Device State
|
770
|
+
#
|
771
|
+
class VpnConstraint < Constraint
|
772
|
+
|
773
|
+
def initialize(h={})
|
774
|
+
|
775
|
+
options = {
|
776
|
+
option: 0
|
777
|
+
}
|
778
|
+
|
779
|
+
super(options.merge h)
|
780
|
+
|
781
|
+
end
|
782
|
+
|
783
|
+
def to_s(colour: false, indent: 0)
|
784
|
+
'VpnConstraint ' + @h.inspect
|
785
|
+
end
|
786
|
+
|
787
|
+
alias to_summary to_s
|
788
|
+
end
|
789
|
+
|
790
|
+
|
791
|
+
# Category: MacroDroid Specific
|
792
|
+
#
|
793
|
+
class MacroDroidVariableConstraint < Constraint
|
794
|
+
|
795
|
+
def initialize(h={})
|
796
|
+
|
797
|
+
options = {
|
798
|
+
|
799
|
+
:enable_regex=>false,
|
800
|
+
:boolean_value=>false,
|
801
|
+
:double_value=>0.0,
|
802
|
+
:int_compare_variable=>false,
|
803
|
+
:int_greater_than=>false,
|
804
|
+
:int_less_than=>false,
|
805
|
+
:int_not_equal=>false,
|
806
|
+
:int_value=>1,
|
807
|
+
:string_comparison_type=>0,
|
808
|
+
:string_equal=>true,
|
809
|
+
:variable=>{
|
810
|
+
:exclude_from_log=>false,
|
811
|
+
:is_local=>true,
|
812
|
+
:boolean_value=>false,
|
813
|
+
:decimal_value=>0.0,
|
814
|
+
:int_value=>2,
|
815
|
+
:name=>"torch",
|
816
|
+
:string_value=>"",
|
817
|
+
:type=>1
|
818
|
+
}
|
819
|
+
|
820
|
+
}
|
821
|
+
|
822
|
+
super(options.merge h)
|
823
|
+
|
824
|
+
end
|
825
|
+
|
826
|
+
def to_s(colour: false, indent: 0)
|
827
|
+
|
828
|
+
a = [:int_greater_than, :int_less_than, :int_not_equal,
|
829
|
+
:string_equal].zip(['>','<','!=', '='])
|
830
|
+
operator = a.find {|label,_| @h[label]}.last
|
831
|
+
|
832
|
+
@s = "%s %s %s" % [@h[:variable][:name], operator, @h[:int_value]]
|
833
|
+
super()
|
834
|
+
end
|
835
|
+
|
836
|
+
alias to_summary to_s
|
837
|
+
end
|
838
|
+
|
839
|
+
# Category: MacroDroid Specific
|
840
|
+
#
|
841
|
+
class MacroEnabledConstraint < Constraint
|
842
|
+
|
843
|
+
def initialize(h={})
|
844
|
+
|
845
|
+
options = {
|
846
|
+
enabled: true,
|
847
|
+
macro_ids: [-8016812002629322290],
|
848
|
+
macro_names: ["Intruder photo "]
|
849
|
+
}
|
850
|
+
|
851
|
+
super(options.merge h)
|
852
|
+
|
853
|
+
end
|
854
|
+
|
855
|
+
def to_s(colour: false, indent: 0)
|
856
|
+
'MacroEnabledConstraint ' + @h.inspect
|
857
|
+
end
|
858
|
+
|
859
|
+
alias to_summary to_s
|
860
|
+
end
|
861
|
+
|
862
|
+
# Category: MacroDroid Specific
|
863
|
+
#
|
864
|
+
class ModeConstraint < Constraint
|
865
|
+
|
866
|
+
def initialize(h={})
|
867
|
+
|
868
|
+
options = {
|
869
|
+
mode: 'Away',
|
870
|
+
mode_selected: true
|
871
|
+
}
|
872
|
+
|
873
|
+
super(options.merge h)
|
874
|
+
|
875
|
+
end
|
876
|
+
|
877
|
+
def to_s(colour: false, indent: 0)
|
878
|
+
'ModeConstraint ' + @h.inspect
|
879
|
+
end
|
880
|
+
|
881
|
+
alias to_summary to_s
|
882
|
+
end
|
883
|
+
|
884
|
+
# Category: MacroDroid Specific
|
885
|
+
#
|
886
|
+
class TriggerThatInvokedConstraint < Constraint
|
887
|
+
using ColouredText
|
888
|
+
|
889
|
+
def initialize(h={})
|
890
|
+
|
891
|
+
puts ('h: ' + h.inspect).green
|
892
|
+
@trigger = h[:macro].triggers.find {|x| x.siguid == h[:si_guid_that_invoked] }
|
893
|
+
|
894
|
+
options = {
|
895
|
+
not: false,
|
896
|
+
si_guid_that_invoked: -4951291100076165433,
|
897
|
+
trigger_name: 'Shake Device'
|
898
|
+
}
|
899
|
+
|
900
|
+
#super(options.merge filter(options,h))
|
901
|
+
super(options.merge h)
|
902
|
+
|
903
|
+
end
|
904
|
+
|
905
|
+
def to_s(colour: false, indent: 0)
|
906
|
+
'Trigger Fired: ' + @trigger.to_s(colour: colour)
|
907
|
+
end
|
908
|
+
|
909
|
+
def to_summary(colour: false)
|
910
|
+
#puts '@trigger' + @trigger.inspect
|
911
|
+
if @trigger then
|
912
|
+
'Trigger Fired: ' + @trigger.to_summary(colour: colour)
|
913
|
+
else
|
914
|
+
'Trigger Fired: Trigger not found; guid: ' + @h[:si_guid_that_invoked].inspect
|
915
|
+
end
|
916
|
+
end
|
917
|
+
|
918
|
+
end
|
919
|
+
|
920
|
+
# Category: MacroDroid Specific
|
921
|
+
#
|
922
|
+
class LastRunTimeConstraint < Constraint
|
923
|
+
|
924
|
+
def initialize(h={})
|
925
|
+
|
926
|
+
options = {
|
927
|
+
check_this_macro: false,
|
928
|
+
invoked: true,
|
929
|
+
macro_ids: [-6922688338672048267],
|
930
|
+
macro_names: ["Opendoor"],
|
931
|
+
time_period_seconds: 7260
|
932
|
+
}
|
933
|
+
|
934
|
+
super(options.merge h)
|
935
|
+
|
936
|
+
end
|
937
|
+
|
938
|
+
def to_s(colour: false, indent: 0)
|
939
|
+
|
940
|
+
macro = if @h[:check_this_macro] then
|
941
|
+
'[This Macro]'
|
942
|
+
end
|
943
|
+
|
944
|
+
invoked = @h[:invoked] ? ' Invoked' : 'Not Invoked'
|
945
|
+
|
946
|
+
duration = Subunit.seconds(@h[:time_period_seconds]).strfunit("%x")
|
947
|
+
"Macro(s) %s\n %s: %s for %s" % [invoked, macro, invoked, duration]
|
948
|
+
|
949
|
+
end
|
950
|
+
|
951
|
+
alias to_summary to_s
|
952
|
+
end
|
953
|
+
|
954
|
+
# Category: Media
|
955
|
+
#
|
956
|
+
class HeadphonesConnectionConstraint < Constraint
|
957
|
+
|
958
|
+
def initialize(h={})
|
959
|
+
|
960
|
+
options = {
|
961
|
+
connected: true
|
962
|
+
}
|
963
|
+
|
964
|
+
super(options.merge h)
|
965
|
+
|
966
|
+
end
|
967
|
+
|
968
|
+
def to_s(colour: false, indent: 0)
|
969
|
+
connection = @h[:connected] ? 'Connected' : 'Disconnected'
|
970
|
+
'Headphones ' + connection
|
971
|
+
end
|
972
|
+
|
973
|
+
alias to_summary to_s
|
974
|
+
|
975
|
+
end
|
976
|
+
|
977
|
+
# Category: Media
|
978
|
+
#
|
979
|
+
class MusicActiveConstraint < Constraint
|
980
|
+
|
981
|
+
def initialize(h={})
|
982
|
+
|
983
|
+
options = {
|
984
|
+
music_active: true
|
985
|
+
}
|
986
|
+
|
987
|
+
super(options.merge h)
|
988
|
+
|
989
|
+
end
|
990
|
+
|
991
|
+
def to_s(colour: false, indent: 0)
|
992
|
+
active = @h[:music_active] ? 'Playing' : 'Stopped'
|
993
|
+
'Music ' + active #+ @h.inspect
|
994
|
+
end
|
995
|
+
|
996
|
+
alias to_summary to_s
|
997
|
+
end
|
998
|
+
|
999
|
+
# Category: Notification
|
1000
|
+
#
|
1001
|
+
class NotificationPresentConstraint < Constraint
|
1002
|
+
|
1003
|
+
def initialize(h={})
|
1004
|
+
|
1005
|
+
options = {
|
1006
|
+
enable_regex: false,
|
1007
|
+
application_name_list: ["All applications"],
|
1008
|
+
exact_match: false,
|
1009
|
+
excludes: false,
|
1010
|
+
excludes_apps: -1,
|
1011
|
+
option: 0,
|
1012
|
+
package_name_list: ["allApplications"],
|
1013
|
+
text_content: ''
|
1014
|
+
}
|
1015
|
+
|
1016
|
+
super(options.merge h)
|
1017
|
+
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
def to_s(colour: false, indent: 0)
|
1021
|
+
'NotificationPresentConstraint ' + @h.inspect
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
alias to_summary to_s
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
# Category: Notification
|
1028
|
+
#
|
1029
|
+
class PriorityModeConstraint < Constraint
|
1030
|
+
|
1031
|
+
def initialize(h={})
|
1032
|
+
|
1033
|
+
options = {
|
1034
|
+
in_mode: true,
|
1035
|
+
selected_index: 0
|
1036
|
+
}
|
1037
|
+
|
1038
|
+
super(options.merge h)
|
1039
|
+
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
def to_s(colour: false, indent: 0)
|
1043
|
+
'PriorityModeConstraint ' + @h.inspect
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
alias to_summary to_s
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
# Category: Notification
|
1050
|
+
#
|
1051
|
+
class NotificationVolumeConstraint < Constraint
|
1052
|
+
|
1053
|
+
def initialize(h={})
|
1054
|
+
|
1055
|
+
options = {
|
1056
|
+
option: 1
|
1057
|
+
}
|
1058
|
+
|
1059
|
+
super(options.merge h)
|
1060
|
+
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
def to_s(colour: false, indent: 0)
|
1064
|
+
'NotificationVolumeConstraint ' + @h.inspect
|
1065
|
+
end
|
1066
|
+
|
1067
|
+
alias to_summary to_s
|
1068
|
+
end
|
1069
|
+
|
1070
|
+
# Category: Phone
|
1071
|
+
#
|
1072
|
+
class InCallConstraint < Constraint
|
1073
|
+
|
1074
|
+
def initialize(h={})
|
1075
|
+
|
1076
|
+
options = {
|
1077
|
+
in_call: true
|
1078
|
+
}
|
1079
|
+
|
1080
|
+
super(options.merge h)
|
1081
|
+
|
1082
|
+
end
|
1083
|
+
|
1084
|
+
def to_s(colour: false, indent: 0)
|
1085
|
+
'InCallConstraint ' + @h.inspect
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
alias to_summary to_s
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
# Category: Phone
|
1092
|
+
#
|
1093
|
+
class PhoneRingingConstraint < Constraint
|
1094
|
+
|
1095
|
+
def initialize(h={})
|
1096
|
+
|
1097
|
+
options = {
|
1098
|
+
ringing: true
|
1099
|
+
}
|
1100
|
+
|
1101
|
+
super(options.merge h)
|
1102
|
+
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
def to_s(colour: false, indent: 0)
|
1106
|
+
@s = @h[:ringing] ? 'Phone Ringing' : 'Not Ringing'
|
1107
|
+
super(colour: colour)
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
end
|
1111
|
+
|
1112
|
+
# Category: Screen and Speaker
|
1113
|
+
#
|
1114
|
+
class BrightnessConstraint < Constraint
|
1115
|
+
|
1116
|
+
def initialize(h={})
|
1117
|
+
|
1118
|
+
options = {
|
1119
|
+
brightness: 35,
|
1120
|
+
equals: false,
|
1121
|
+
force_pie_mode: false,
|
1122
|
+
greater_than: false,
|
1123
|
+
is_auto_brightness: false
|
1124
|
+
}
|
1125
|
+
|
1126
|
+
super(options.merge h)
|
1127
|
+
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
def to_s(colour: false, indent: 0)
|
1131
|
+
'BrightnessConstraint ' + @h.inspect
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
alias to_summary to_s
|
1135
|
+
end
|
1136
|
+
|
1137
|
+
# Category: Screen and Speaker
|
1138
|
+
#
|
1139
|
+
class VolumeConstraint < Constraint
|
1140
|
+
|
1141
|
+
def initialize(h={})
|
1142
|
+
|
1143
|
+
options = {
|
1144
|
+
option: 0
|
1145
|
+
}
|
1146
|
+
|
1147
|
+
super(options.merge filter(options, h))
|
1148
|
+
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
def to_s(colour: false, indent: 0)
|
1152
|
+
a = ['Volume On', 'Vibrate Only' 'Silent', 'Vibrate or Silent']
|
1153
|
+
|
1154
|
+
"Ringer Volume\n " + a[@h[:option]]
|
1155
|
+
end
|
1156
|
+
|
1157
|
+
end
|
1158
|
+
|
1159
|
+
# Category: Screen and Speaker
|
1160
|
+
#
|
1161
|
+
class SpeakerPhoneConstraint < Constraint
|
1162
|
+
|
1163
|
+
def initialize(h={})
|
1164
|
+
|
1165
|
+
options = {
|
1166
|
+
enabled: true
|
1167
|
+
}
|
1168
|
+
|
1169
|
+
super(options.merge h)
|
1170
|
+
|
1171
|
+
end
|
1172
|
+
|
1173
|
+
def to_s(colour: false, indent: 0)
|
1174
|
+
'SpeakerPhoneConstraint ' + @h.inspect
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
alias to_summary to_s
|
1178
|
+
end
|
1179
|
+
|
1180
|
+
# Category: Screen and Speaker
|
1181
|
+
#
|
1182
|
+
class DarkThemeConstraint < Constraint
|
1183
|
+
|
1184
|
+
def initialize(h={})
|
1185
|
+
|
1186
|
+
options = {
|
1187
|
+
option: 0
|
1188
|
+
}
|
1189
|
+
|
1190
|
+
super(options.merge h)
|
1191
|
+
|
1192
|
+
end
|
1193
|
+
|
1194
|
+
def to_s(colour: false, indent: 0)
|
1195
|
+
'DarkThemeConstraint ' + @h.inspect
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
alias to_summary to_s
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
# Category: Screen and Speaker
|
1202
|
+
#
|
1203
|
+
class ScreenOnOffConstraint < Constraint
|
1204
|
+
|
1205
|
+
def initialize(h={})
|
1206
|
+
|
1207
|
+
options = {
|
1208
|
+
a: true,
|
1209
|
+
screen_on: true
|
1210
|
+
}
|
1211
|
+
|
1212
|
+
super(options.merge h)
|
1213
|
+
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
def to_s(colour: false, indent: 0)
|
1217
|
+
'Screen ' + (@h[:screen_on] ? 'On' : 'Off')
|
1218
|
+
end
|
1219
|
+
|
1220
|
+
alias to_summary to_s
|
1221
|
+
|
1222
|
+
end
|
1223
|
+
|
1224
|
+
# Category: Screen and Speaker
|
1225
|
+
#
|
1226
|
+
class VolumeLevelConstraint < Constraint
|
1227
|
+
|
1228
|
+
def initialize(h={})
|
1229
|
+
|
1230
|
+
options = {
|
1231
|
+
comparison: 0,
|
1232
|
+
stream_index_array: [false, true, false, false, false, false, false],
|
1233
|
+
volume: 42
|
1234
|
+
}
|
1235
|
+
|
1236
|
+
super(options.merge h)
|
1237
|
+
|
1238
|
+
end
|
1239
|
+
|
1240
|
+
def to_s(colour: false, indent: 0)
|
1241
|
+
'VolumeLevelConstraint ' + @h.inspect
|
1242
|
+
end
|
1243
|
+
|
1244
|
+
alias to_summary to_s
|
1245
|
+
end
|
1246
|
+
|
1247
|
+
# Category: Sensors
|
1248
|
+
#
|
1249
|
+
class FaceUpDownConstraint < Constraint
|
1250
|
+
|
1251
|
+
def initialize(h={})
|
1252
|
+
|
1253
|
+
options = {
|
1254
|
+
option: -1,
|
1255
|
+
selected_options: [true, false, true, false, false, false]
|
1256
|
+
}
|
1257
|
+
|
1258
|
+
super(options.merge h)
|
1259
|
+
|
1260
|
+
end
|
1261
|
+
|
1262
|
+
def to_s(colour: false, indent: 0)
|
1263
|
+
a = ['Face Up', 'Face Down', 'Vertical Upright', 'Vertical Upside Down',
|
1264
|
+
'Sideways Left', 'Sideways Right']
|
1265
|
+
s = @h[:selected_options].zip(a).select(&:first).map(&:last).join(', ')
|
1266
|
+
|
1267
|
+
'Device Facing' + "\n " + s #+ @h.inspect
|
1268
|
+
end
|
1269
|
+
|
1270
|
+
alias to_summary to_s
|
1271
|
+
end
|
1272
|
+
|
1273
|
+
# Category: Sensors
|
1274
|
+
#
|
1275
|
+
class LightLevelConstraint < Constraint
|
1276
|
+
|
1277
|
+
def initialize(h={})
|
1278
|
+
|
1279
|
+
options = {
|
1280
|
+
light_level: -1,
|
1281
|
+
light_level_float: 5000.0,
|
1282
|
+
option: 1
|
1283
|
+
}
|
1284
|
+
|
1285
|
+
super(options.merge h)
|
1286
|
+
|
1287
|
+
end
|
1288
|
+
|
1289
|
+
def to_s(colour: false, indent: 0)
|
1290
|
+
|
1291
|
+
operator = @h[:light_level] == -1 ? 'Less than' : 'Greater than'
|
1292
|
+
condition = operator + ' ' + @h[:light_level_float].to_s + 'lx'
|
1293
|
+
'Light Sensor ' + condition
|
1294
|
+
|
1295
|
+
end
|
1296
|
+
|
1297
|
+
end
|
1298
|
+
|
1299
|
+
# Category: Sensors
|
1300
|
+
#
|
1301
|
+
class DeviceOrientationConstraint < Constraint
|
1302
|
+
|
1303
|
+
def initialize(h={})
|
1304
|
+
|
1305
|
+
options = {
|
1306
|
+
portrait: true
|
1307
|
+
}
|
1308
|
+
|
1309
|
+
super(options.merge h)
|
1310
|
+
|
1311
|
+
end
|
1312
|
+
|
1313
|
+
def to_s(colour: false, indent: 0)
|
1314
|
+
'DeviceOrientationConstraint ' + @h.inspect
|
1315
|
+
end
|
1316
|
+
|
1317
|
+
alias to_summary to_s
|
1318
|
+
end
|
1319
|
+
|
1320
|
+
# Category: Sensors
|
1321
|
+
#
|
1322
|
+
class ProximitySensorConstraint < Constraint
|
1323
|
+
|
1324
|
+
def initialize(h={})
|
1325
|
+
|
1326
|
+
options = {
|
1327
|
+
near: true
|
1328
|
+
}
|
1329
|
+
|
1330
|
+
super(options.merge h)
|
1331
|
+
|
1332
|
+
end
|
1333
|
+
|
1334
|
+
def to_s(colour: false, indent: 0)
|
1335
|
+
'Proximity Sensor: ' + (@h[:near] ? 'Near' : 'Far')
|
1336
|
+
end
|
1337
|
+
|
1338
|
+
end
|