roku_builder 4.25.5 → 4.25.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/intergration/roku_builder/test_helper.rb +2 -3
- data/intergration/roku_builder/test_profiler.rb +1 -1
- data/lib/roku_builder/config_parser.rb +4 -5
- data/lib/roku_builder/device_manager.rb +100 -0
- data/lib/roku_builder/plugins/core.rb +3 -1
- data/lib/roku_builder/plugins/inspector.rb +36 -26
- data/lib/roku_builder/plugins/linker.rb +29 -22
- data/lib/roku_builder/plugins/loader.rb +30 -11
- data/lib/roku_builder/plugins/monitor.rb +19 -17
- data/lib/roku_builder/plugins/navigator.rb +40 -34
- data/lib/roku_builder/plugins/packager.rb +109 -92
- data/lib/roku_builder/plugins/profiler.rb +24 -15
- data/lib/roku_builder/plugins/tester.rb +15 -13
- data/lib/roku_builder/util.rb +39 -17
- data/lib/roku_builder/version.rb +1 -1
- data/lib/roku_builder.rb +3 -19
- data/roku_builder.gemspec +3 -2
- data/test/roku_builder/plugins/test_core.rb +2 -3
- data/test/roku_builder/plugins/test_inspector.rb +48 -8
- data/test/roku_builder/plugins/test_linker.rb +66 -10
- data/test/roku_builder/plugins/test_loader.rb +44 -15
- data/test/roku_builder/plugins/test_monitor.rb +33 -16
- data/test/roku_builder/plugins/test_navigator.rb +55 -14
- data/test/roku_builder/plugins/test_packager.rb +118 -34
- data/test/roku_builder/plugins/test_profiler.rb +153 -78
- data/test/roku_builder/plugins/test_tester.rb +20 -5
- data/test/roku_builder/test_device_manager.rb +187 -0
- data/test/roku_builder/test_helper.rb +6 -0
- data/test/roku_builder/test_roku_builder.rb +5 -42
- metadata +23 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b926c96062054e546bb423d2031bc48446a3c716c2488c6756882a6218d22e2
|
4
|
+
data.tar.gz: 6748df137a4374f34b2c91aebac0a4e1c77129753c835f86d1ac61c9b3999f9b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42ff1860aded5097863dd4bd6b721d78f24e1e99888b365cb2a84dd98878c74599efc4be91c56d6b6f50b706d2eb3ce73bc75ab25ded7829a4a3325ece71a023
|
7
|
+
data.tar.gz: 7b531741d6cab2f47739892edb54aeae709fe53fa44b18dcaa97a22dc112767bd323644289be3041eac1de56b12f002b562e06714a5d116b6c45510cdd1a62ce
|
@@ -15,7 +15,7 @@ require "minitest/autorun"
|
|
15
15
|
require "minitest/utils"
|
16
16
|
require "securerandom"
|
17
17
|
|
18
|
-
ROKU_IP = "192.168.
|
18
|
+
ROKU_IP = "192.168.11.114"
|
19
19
|
|
20
20
|
module Minitest
|
21
21
|
module Assertions
|
@@ -114,8 +114,7 @@ module Helpers
|
|
114
114
|
default: :project1,
|
115
115
|
project1: {
|
116
116
|
directory: root_dir,
|
117
|
-
|
118
|
-
files: ["manifest","file.tmp"],
|
117
|
+
source_files: ["images","source","components", "manifest","file.tmp"],
|
119
118
|
app_name: "App Name",
|
120
119
|
stage_method: :script,
|
121
120
|
stages:{
|
@@ -48,7 +48,7 @@ module RokuBuilder
|
|
48
48
|
`#{roku} --sideload --working`
|
49
49
|
assert_log @uuid
|
50
50
|
output = `#{roku} --profile images`
|
51
|
-
assert_match(/Available memory/, output)
|
51
|
+
assert_match(/Available texture memory/, output)
|
52
52
|
end
|
53
53
|
def test_profile_textures
|
54
54
|
`#{roku} --sideload --working`
|
@@ -21,7 +21,7 @@ module RokuBuilder
|
|
21
21
|
|
22
22
|
def parse_config
|
23
23
|
process_in_argument
|
24
|
-
|
24
|
+
setup_devices
|
25
25
|
setup_project
|
26
26
|
setup_in_out_file
|
27
27
|
setup_project_config
|
@@ -35,10 +35,9 @@ module RokuBuilder
|
|
35
35
|
@options[:in] = File.expand_path(@options[:in]) if @options[:in]
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
39
|
-
@
|
40
|
-
@parsed[:
|
41
|
-
raise ArgumentError, "Unknown device: #{@options[:device]}" unless @parsed[:device_config]
|
38
|
+
def setup_devices
|
39
|
+
@parsed[:device_default] = @config[:devices][:default]
|
40
|
+
@parsed[:devices] = @config[:devices].select{|key, value| :default != key}
|
42
41
|
end
|
43
42
|
|
44
43
|
def setup_project
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# ********** Copyright Viacom, Inc. Apache 2.0 **********
|
2
|
+
|
3
|
+
module RokuBuilder
|
4
|
+
|
5
|
+
class DeviceManager
|
6
|
+
|
7
|
+
def initialize(options:, config:)
|
8
|
+
@config = config
|
9
|
+
@options = options
|
10
|
+
@timeout_duration = 3600
|
11
|
+
end
|
12
|
+
|
13
|
+
def reserve_device(no_lock: false)
|
14
|
+
message = "No Devices Found"
|
15
|
+
if @options[:device]
|
16
|
+
device = Device.new(@options[:device], @config.raw[:devices][@options[:device].to_sym])
|
17
|
+
return device if device_available!(device: device, no_lock: no_lock)
|
18
|
+
message = "Device #{@options[:device]} not found"
|
19
|
+
else
|
20
|
+
begin
|
21
|
+
Timeout::timeout(@timeout_duration) do
|
22
|
+
loop do
|
23
|
+
device = reserve_any(no_lock: no_lock)
|
24
|
+
if device
|
25
|
+
Logger.instance.info "Using Device: #{device.to_s}"
|
26
|
+
return device
|
27
|
+
end
|
28
|
+
break unless @options[:device_blocking]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
rescue Timeout::Error
|
32
|
+
end
|
33
|
+
end
|
34
|
+
raise DeviceError, message
|
35
|
+
end
|
36
|
+
|
37
|
+
def release_device(device)
|
38
|
+
lock = lock_file(device)
|
39
|
+
File.delete(lock) if File.exist?(lock)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def reserve_any(no_lock: false)
|
45
|
+
default = @config.device_default
|
46
|
+
all_devices = @config.devices.keys.reject{|key, value| default == key}
|
47
|
+
all_devices.unshift(default)
|
48
|
+
all_devices.each do |device_name|
|
49
|
+
device = Device.new(device_name, @config.devices[device_name])
|
50
|
+
if device_available!(device: device, no_lock: no_lock)
|
51
|
+
return device
|
52
|
+
end
|
53
|
+
end
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def device_available!(device:, no_lock: false)
|
58
|
+
return false unless device_ping?(device)
|
59
|
+
return true if no_lock
|
60
|
+
file = lock_file(device)
|
61
|
+
lock = file.flock(File::LOCK_EX|File::LOCK_NB)
|
62
|
+
return false if lock == false
|
63
|
+
text = file.readlines
|
64
|
+
return false if text.count > 0
|
65
|
+
file.write($$)
|
66
|
+
file.pos = 0
|
67
|
+
text = file.readlines
|
68
|
+
if text[0] != $$.to_s
|
69
|
+
file.flock(File::LOCK_UN)
|
70
|
+
return false
|
71
|
+
end
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
def device_ping?(device)
|
76
|
+
ping = Net::Ping::External.new
|
77
|
+
ping.ping? device.ip, 1, 0.2, 1
|
78
|
+
end
|
79
|
+
|
80
|
+
def lock_file(device)
|
81
|
+
File.open(File.join(Dir.tmpdir, device.name), File::RDWR | File::APPEND | File::CREAT | File::BINARY | File::SHARE_DELETE) #"a+")
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
class Device
|
87
|
+
attr_accessor :name, :ip, :user, :password
|
88
|
+
|
89
|
+
def initialize(name, device_config)
|
90
|
+
@name = name.to_s
|
91
|
+
@ip = device_config[:ip]
|
92
|
+
@user = device_config[:user]
|
93
|
+
@password = device_config[:password]
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_s
|
97
|
+
"#{name}:#{ip}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -67,7 +67,9 @@ module RokuBuilder
|
|
67
67
|
end
|
68
68
|
parser.on("-D", "--device ID", "Use a different device corresponding to the given ID") do |d|
|
69
69
|
options[:device] = d
|
70
|
-
|
70
|
+
end
|
71
|
+
parser.on("--device-blocking", "Block and wait for a device if none is ready. Does not work with --device") do
|
72
|
+
options[:device_blocking] = true
|
71
73
|
end
|
72
74
|
parser.on("-V", "--verbose", "Print Info message") do
|
73
75
|
options[:verbose] = true
|
@@ -33,13 +33,15 @@ module RokuBuilder
|
|
33
33
|
pkg = pkg+".pkg" unless pkg.end_with?(".pkg")
|
34
34
|
# upload new key with password
|
35
35
|
path = "/plugin_inspect"
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
36
|
+
response = nil
|
37
|
+
multipart_connection do |conn|
|
38
|
+
payload = {
|
39
|
+
mysubmit: "Inspect",
|
40
|
+
passwd: options[:password],
|
41
|
+
archive: Faraday::UploadIO.new(pkg, 'application/octet-stream')
|
42
|
+
}
|
43
|
+
response = conn.post path, payload
|
44
|
+
end
|
43
45
|
|
44
46
|
app_name = /App Name:\s*<\/td>\s*<td>\s*<font[^>]*>([^<]*)<\/font>\s*<\/td>/.match(response.body)
|
45
47
|
dev_id = nil
|
@@ -75,30 +77,38 @@ module RokuBuilder
|
|
75
77
|
# Capture a screencapture for the currently sideloaded app
|
76
78
|
# @return [Boolean] Success
|
77
79
|
def screencapture(options:)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
80
|
+
get_device do |device|
|
81
|
+
out = @config.out
|
82
|
+
payload = {
|
83
|
+
mysubmit: "Screenshot",
|
84
|
+
passwd: @dev_password,
|
85
|
+
archive: Faraday::UploadIO.new(File::NULL, 'application/octet-stream')
|
86
|
+
}
|
87
|
+
response = nil
|
88
|
+
multipart_connection(device: device) do |conn|
|
89
|
+
response = conn.post "/plugin_inspect", payload
|
90
|
+
end
|
85
91
|
|
86
|
-
|
87
|
-
|
88
|
-
|
92
|
+
path = /<img src="([^"]*)">/.match(response.body)
|
93
|
+
raise ExecutionError, "Failed to capture screen" unless path
|
94
|
+
path = path[1]
|
89
95
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
96
|
+
unless out[:file]
|
97
|
+
out[:file] = /time=([^"]*)">/.match(response.body)
|
98
|
+
out_ext = /dev.([^"]*)\?/.match(response.body)
|
99
|
+
out[:file] = "dev_#{out[:file][1]}.#{out_ext[1]}" if out[:file]
|
100
|
+
end
|
95
101
|
|
96
|
-
|
102
|
+
response = nil
|
103
|
+
simple_connection(device: device) do |conn|
|
104
|
+
response = conn.get path
|
105
|
+
end
|
97
106
|
|
98
|
-
|
99
|
-
|
107
|
+
File.open(File.join(out[:folder], out[:file]), "wb") do |io|
|
108
|
+
io.write(response.body)
|
109
|
+
end
|
110
|
+
@logger.info "Screen captured to #{File.join(out[:folder], out[:file])}"
|
100
111
|
end
|
101
|
-
@logger.info "Screen captured to #{File.join(out[:folder], out[:file])}"
|
102
112
|
end
|
103
113
|
end
|
104
114
|
RokuBuilder.register_plugin(Inspector)
|
@@ -36,14 +36,16 @@ module RokuBuilder
|
|
36
36
|
end
|
37
37
|
|
38
38
|
# Deeplink to an app
|
39
|
-
def deeplink(options:)
|
40
|
-
|
41
|
-
|
39
|
+
def deeplink(options:, device: nil)
|
40
|
+
get_device(device: device) do |device|
|
41
|
+
if options.has_source?
|
42
|
+
Loader.new(config: @config).sideload(options: options, device: device)
|
43
|
+
end
|
44
|
+
app_id = options[:app_id]
|
45
|
+
app_id ||= "dev"
|
46
|
+
path = "/launch/#{app_id}"
|
47
|
+
send_options(path: path, options: options[:deeplink], device: device)
|
42
48
|
end
|
43
|
-
app_id = options[:app_id]
|
44
|
-
app_id ||= "dev"
|
45
|
-
path = "/launch/#{app_id}"
|
46
|
-
send_options(path: path, options: options[:deeplink])
|
47
49
|
end
|
48
50
|
|
49
51
|
def input(options:)
|
@@ -54,8 +56,10 @@ module RokuBuilder
|
|
54
56
|
# @param logger [Logger] System Logger
|
55
57
|
def applist(options:)
|
56
58
|
path = "/query/apps"
|
57
|
-
|
58
|
-
|
59
|
+
response = nil
|
60
|
+
multipart_connection(port: 8060) do |conn|
|
61
|
+
response = conn.get path
|
62
|
+
end
|
59
63
|
|
60
64
|
if response.success?
|
61
65
|
regexp = /id="([^"]*)"\stype="([^"]*)"\sversion="([^"]*)">([^<]*)</
|
@@ -70,22 +74,25 @@ module RokuBuilder
|
|
70
74
|
|
71
75
|
private
|
72
76
|
|
73
|
-
def send_options(path:, options:)
|
77
|
+
def send_options(path:, options:, device: nil)
|
74
78
|
payload = RokuBuilder.options_parse(options: options)
|
79
|
+
get_device(device: device) do |device|
|
80
|
+
unless payload.keys.count > 0
|
81
|
+
@logger.warn "No options sent to launched app"
|
82
|
+
else
|
83
|
+
payload = parameterize(payload)
|
84
|
+
path = "#{path}?#{payload}"
|
85
|
+
@logger.info "Deeplink:"
|
86
|
+
@logger.info payload
|
87
|
+
@logger.info "CURL:"
|
88
|
+
@logger.info "curl -d '' 'http://#{device.ip}:8060#{path}'"
|
89
|
+
end
|
75
90
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
path = "#{path}?#{payload}"
|
81
|
-
@logger.info "Deeplink:"
|
82
|
-
@logger.info payload
|
83
|
-
@logger.info "CURL:"
|
84
|
-
@logger.info "curl -d '' '#{@url}:8060#{path}'"
|
91
|
+
multipart_connection(port: 8060, device: device) do |conn|
|
92
|
+
response = conn.post path
|
93
|
+
@logger.fatal("Failed Deeplinking") unless response.success?
|
94
|
+
end
|
85
95
|
end
|
86
|
-
|
87
|
-
response = multipart_connection(port: 8060).post path
|
88
|
-
@logger.fatal("Failed Deeplinking") unless response.success?
|
89
96
|
end
|
90
97
|
|
91
98
|
# Parameterize options to be sent to the app
|
@@ -50,16 +50,20 @@ module RokuBuilder
|
|
50
50
|
end
|
51
51
|
|
52
52
|
# Sideload an app onto a roku device
|
53
|
-
def sideload(options:)
|
53
|
+
def sideload(options:, device: nil)
|
54
54
|
did_build = false
|
55
55
|
unless options[:in]
|
56
56
|
did_build = true
|
57
57
|
build(options: options)
|
58
58
|
end
|
59
59
|
keep_build_file = is_build_command(options) and options[:out]
|
60
|
-
upload(options)
|
60
|
+
upload(options: options, device: device)
|
61
61
|
# Cleanup
|
62
|
-
|
62
|
+
begin
|
63
|
+
File.delete(file_path(:in)) if did_build and not keep_build_file
|
64
|
+
rescue Errno::EACCES
|
65
|
+
@logger.warn "Unable to delete: " + file_path(:in)
|
66
|
+
end
|
63
67
|
end
|
64
68
|
|
65
69
|
|
@@ -76,17 +80,29 @@ module RokuBuilder
|
|
76
80
|
|
77
81
|
# Remove the currently sideloaded app
|
78
82
|
def delete(options:, ignoreFailure: false)
|
79
|
-
payload = {
|
80
|
-
|
83
|
+
payload = {
|
84
|
+
mysubmit: make_param("Delete"),
|
85
|
+
archive: make_param("", "application/octet-stream")
|
86
|
+
}
|
87
|
+
response = nil
|
88
|
+
multipart_connection do |conn|
|
89
|
+
response = conn.post "/plugin_install", payload
|
90
|
+
end
|
81
91
|
unless response.status == 200 and response.body =~ /Delete Succeeded/ or ignoreFailure
|
82
92
|
raise ExecutionError, "Failed Unloading"
|
83
93
|
end
|
84
94
|
end
|
85
95
|
|
86
96
|
# Convert sideloaded app to squashfs
|
87
|
-
def squash(options:, ignoreFailure: false)
|
88
|
-
payload = {
|
89
|
-
|
97
|
+
def squash(options:, device: nil, ignoreFailure: false)
|
98
|
+
payload = {
|
99
|
+
mysubmit: "Convert to squashfs",
|
100
|
+
archive: make_param("", "application/octet-stream")
|
101
|
+
}
|
102
|
+
response = nil
|
103
|
+
multipart_connection(device: device) do |conn|
|
104
|
+
response = conn.post "/plugin_install", payload
|
105
|
+
end
|
90
106
|
unless response.status == 200 and response.body =~ /Conversion succeeded/ or ignoreFailure
|
91
107
|
raise ExecutionError, "Failed Converting to Squashfs"
|
92
108
|
end
|
@@ -104,16 +120,19 @@ module RokuBuilder
|
|
104
120
|
[:sideload, :build].include? options.command
|
105
121
|
end
|
106
122
|
|
107
|
-
def upload(options)
|
123
|
+
def upload(options:, device: nil)
|
108
124
|
payload = {
|
109
125
|
mysubmit: "Replace",
|
110
126
|
archive: Faraday::UploadIO.new(file_path(:in), 'application/zip'),
|
111
127
|
}
|
112
128
|
payload["remotedebug"] = "1" if options[:remoteDebug]
|
113
|
-
response =
|
129
|
+
response = nil
|
130
|
+
multipart_connection(device: device) do |conn|
|
131
|
+
response = conn.post "/plugin_install", payload
|
132
|
+
end
|
114
133
|
@logger.debug("Status: #{response.status}, Body: #{response.body}")
|
115
134
|
if response.status==200 and response.body=~/Identical to previous version/
|
116
|
-
@logger.warn("Sideload
|
135
|
+
@logger.warn("Sideload identical to previous version")
|
117
136
|
elsif not (response.status==200 and response.body=~/Install Success/)
|
118
137
|
raise ExecutionError, "Failed Sideloading"
|
119
138
|
end
|
@@ -32,26 +32,28 @@ module RokuBuilder
|
|
32
32
|
|
33
33
|
# Monitor a development log on the Roku device
|
34
34
|
def monitor(options:)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
35
|
+
get_device(no_lock: true) do |device|
|
36
|
+
type = options[:monitor].to_sym
|
37
|
+
telnet_config = { 'Host' => device.ip, 'Port' => @ports[type] }
|
38
|
+
waitfor_config = { 'Match' => /./, 'Timeout' => false }
|
39
|
+
|
40
|
+
thread = Thread.new(telnet_config, waitfor_config) {|telnet,waitfor|
|
41
|
+
@logger.info "Monitoring #{type} console(#{telnet['Port']}) on #{telnet['Host'] }"
|
42
|
+
connection = Net::Telnet.new(telnet)
|
43
|
+
Thread.current[:connection] = connection
|
44
|
+
all_text = ""
|
45
|
+
while true
|
46
|
+
connection.waitfor(waitfor) do |txt|
|
47
|
+
all_text = manage_text(all_text: all_text, txt: txt, regexp: options[:regexp])
|
48
|
+
end
|
47
49
|
end
|
48
|
-
|
49
|
-
|
50
|
-
thread.abort_on_exception = true
|
50
|
+
}
|
51
|
+
thread.abort_on_exception = true
|
51
52
|
|
52
|
-
|
53
|
+
init_readline()
|
53
54
|
|
54
|
-
|
55
|
+
run_prompt(thread: thread)
|
56
|
+
end
|
55
57
|
end
|
56
58
|
|
57
59
|
private
|
@@ -70,48 +70,54 @@ module RokuBuilder
|
|
70
70
|
# Send a navigation command to the roku device
|
71
71
|
# @param command [Symbol] The smbol of the command to send
|
72
72
|
# @return [Boolean] Success
|
73
|
-
def nav(options:)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
73
|
+
def nav(options:, device: nil)
|
74
|
+
get_device(device: device, no_lock: true) do |device|
|
75
|
+
commands = options[:nav].split(/, */).map{|c| c.to_sym}
|
76
|
+
commands.each do |command|
|
77
|
+
unless @commands.has_key?(command)
|
78
|
+
raise ExecutionError, "Unknown Navigation Command"
|
79
|
+
end
|
80
|
+
multipart_connection(port: 8060, device: device) do |conn|
|
81
|
+
path = "/keypress/#{@commands[command]}"
|
82
|
+
@logger.debug("Send Command: "+path)
|
83
|
+
response = conn.post path
|
84
|
+
raise ExecutionError, "Navigation Failed" unless response.success?
|
85
|
+
end
|
78
86
|
end
|
79
|
-
conn = multipart_connection(port: 8060)
|
80
|
-
path = "/keypress/#{@commands[command]}"
|
81
|
-
@logger.debug("Send Command: "+path)
|
82
|
-
response = conn.post path
|
83
|
-
raise ExecutionError, "Navigation Failed" unless response.success?
|
84
87
|
end
|
85
88
|
end
|
86
89
|
|
87
90
|
# Type text on the roku device
|
88
91
|
# @param text [String] The text to type on the device
|
89
92
|
# @return [Boolean] Success
|
90
|
-
def type(options:)
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
93
|
+
def type(options:, device: nil)
|
94
|
+
multipart_connection(port: 8060, device: device, no_lock: true) do |conn|
|
95
|
+
options[:type].split(//).each do |c|
|
96
|
+
path = "/keypress/LIT_#{CGI::escape(c)}"
|
97
|
+
@logger.debug("Send Letter: "+path)
|
98
|
+
response = conn.post path
|
99
|
+
return false unless response.success?
|
100
|
+
end
|
101
|
+
return true
|
97
102
|
end
|
98
|
-
return true
|
99
103
|
end
|
100
104
|
|
101
105
|
def navigate(options:)
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
@
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
106
|
+
get_device(no_lock: true) do |device|
|
107
|
+
running = true
|
108
|
+
@logger.info("Key Mappings:")
|
109
|
+
@mappings.each_value {|key|
|
110
|
+
@logger.info(sprintf("%13s -> %s", key[1], @commands[key[0].to_sym]))
|
111
|
+
}
|
112
|
+
@logger.info(sprintf("%13s -> %s", "Ctrl + c", "Exit"))
|
113
|
+
while running
|
114
|
+
char = read_char
|
115
|
+
@logger.debug("Char: #{char.inspect}")
|
116
|
+
if char == "\u0003"
|
117
|
+
running = false
|
118
|
+
else
|
119
|
+
Thread.new(char, device) {|character,device| handle_navigate_input(character, device)}
|
120
|
+
end
|
115
121
|
end
|
116
122
|
end
|
117
123
|
end
|
@@ -208,11 +214,11 @@ module RokuBuilder
|
|
208
214
|
end
|
209
215
|
end
|
210
216
|
|
211
|
-
def handle_navigate_input(char)
|
217
|
+
def handle_navigate_input(char, device)
|
212
218
|
if @mappings[char.to_sym] != nil
|
213
|
-
nav(options: {nav: @mappings[char.to_sym][0]})
|
219
|
+
nav(options: {nav: @mappings[char.to_sym][0]}, device: device)
|
214
220
|
elsif char.inspect.force_encoding("UTF-8").ascii_only?
|
215
|
-
type(options: {type: char})
|
221
|
+
type(options: {type: char}, device: device)
|
216
222
|
end
|
217
223
|
end
|
218
224
|
|