liberate 0.1.pre.alpha
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 +7 -0
- data/bin/liberate +5 -0
- data/lib/liberate.rb +301 -0
- data/lib/liberate/version.rb +4 -0
- metadata +61 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 44260d07f0d85b17fd875b0a0d03d5c0328217c8
|
|
4
|
+
data.tar.gz: c68a969a6dd249244b7b364bcf4f4f7bdc9564b2
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 85ad2b1a4ed9ff95d61c6bbac1db3447ba2337d6fe82ad73d1138919246733c83dc424d74f2c426b7c45327e786cac53773f15105175d00b0362549810ff51e0
|
|
7
|
+
data.tar.gz: a45338bea2fa2a4c144a9a7491f3541c01ce591fcc2009678c50a387fba23380a84adf41460f730cc79226fe280347947e7e2d42e3af7b6ef429aed92ea672ca
|
data/bin/liberate
ADDED
data/lib/liberate.rb
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
require 'liberate/version'
|
|
2
|
+
require 'optparse'
|
|
3
|
+
require 'colorize'
|
|
4
|
+
require 'open3'
|
|
5
|
+
|
|
6
|
+
module Liberate
|
|
7
|
+
|
|
8
|
+
### This is where all the action happens!
|
|
9
|
+
class App
|
|
10
|
+
|
|
11
|
+
# Regex
|
|
12
|
+
DEVICE_ID_REGEX = "[\\w\\d\\.\\:]+"
|
|
13
|
+
VALUE_SUFFIX_REGEX = "\:([a-zA-Z0-9_])+"
|
|
14
|
+
IP_V4_REGEX = "([0-9])+\\.([0-9])+\\.([0-9])+\\.([0-9])+" # TODO Make a tighter regex
|
|
15
|
+
|
|
16
|
+
# Other constants
|
|
17
|
+
PORT_NUMBER = 5555
|
|
18
|
+
ROW_FORMAT = '%-20s %-12s %-12s %s'
|
|
19
|
+
|
|
20
|
+
### Good ol' constructor
|
|
21
|
+
def initialize(args)
|
|
22
|
+
# args.push "-h" if args.size == 0
|
|
23
|
+
# Command-line options
|
|
24
|
+
create_options_parser(args)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
### Checks if 'adb' is present in the system path
|
|
28
|
+
def has_adb_in_path
|
|
29
|
+
unless which('adb')
|
|
30
|
+
puts "'adb' (Android Debug Bridge) not found in path.".red
|
|
31
|
+
exit 1
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
### Checks if a given command is found in the system path
|
|
36
|
+
# see http://stackoverflow.com/a/5471032/421372
|
|
37
|
+
def which(cmd)
|
|
38
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
|
39
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
|
40
|
+
exts.each { |ext|
|
|
41
|
+
exe = File.join(path, "#{cmd}#{ext}")
|
|
42
|
+
return exe if File.executable?(exe) && !File.directory?(exe)
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
return nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
### Creates an options parser
|
|
49
|
+
def create_options_parser(args)
|
|
50
|
+
args.options do |opts|
|
|
51
|
+
opts.banner = "Usage: liberate <options>"
|
|
52
|
+
opts.separator ''
|
|
53
|
+
opts.separator "where possible options are:"
|
|
54
|
+
|
|
55
|
+
# List connected devices
|
|
56
|
+
opts.on('-l', '--list', 'list connected devices') do
|
|
57
|
+
list_devices
|
|
58
|
+
exit
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Liberate a specific device
|
|
62
|
+
opts.on('-d', '--device', 'liberate a specific device') do
|
|
63
|
+
if args.size == 0
|
|
64
|
+
puts "You must specify a device when using the -d option.".yellow
|
|
65
|
+
exit 0
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
liberate_device(args[0])
|
|
69
|
+
exit
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Version
|
|
73
|
+
opts.on('-v', '--version', 'show version number') do
|
|
74
|
+
puts Liberate::NAME.concat(' ').concat(Liberate::VERSION)
|
|
75
|
+
exit
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Help
|
|
79
|
+
opts.on('-h', '--help', 'show help') do
|
|
80
|
+
puts opts.help
|
|
81
|
+
exit
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
opts.parse!
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
### Handles the -l option
|
|
89
|
+
def list_devices
|
|
90
|
+
devices = get_devices
|
|
91
|
+
print_devices_table(devices)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
### Handles the -d option
|
|
95
|
+
def liberate_device(key)
|
|
96
|
+
devices = get_devices
|
|
97
|
+
|
|
98
|
+
matching_devices = Array.new
|
|
99
|
+
devices.each do |device|
|
|
100
|
+
matching_devices << device if device.matches(key)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
found = matching_devices.size
|
|
104
|
+
if found == 0
|
|
105
|
+
message = "Oops... no device matched '%s'." % [key]
|
|
106
|
+
puts message.yellow
|
|
107
|
+
elsif found > 1
|
|
108
|
+
puts "Multiple devices found.".yellow
|
|
109
|
+
puts matching_devices
|
|
110
|
+
else
|
|
111
|
+
device = matching_devices[0]
|
|
112
|
+
liberate(device)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
### Gets the list of connected devices (sorted by '@model')
|
|
117
|
+
def get_devices()
|
|
118
|
+
command = 'adb devices -l'
|
|
119
|
+
console_output, console_error, exit_code = execute_shell_command(command)
|
|
120
|
+
|
|
121
|
+
if exit_code != 0
|
|
122
|
+
puts "Trouble starting 'adb', try restarting it manually.".red
|
|
123
|
+
puts "Details...".yellow
|
|
124
|
+
puts console_output
|
|
125
|
+
puts console_error
|
|
126
|
+
exit 1
|
|
127
|
+
else
|
|
128
|
+
console_output = console_output.split("\n")
|
|
129
|
+
console_output.delete_at(0) # DELETE this line => List of devices attached
|
|
130
|
+
|
|
131
|
+
if console_output.size == 0
|
|
132
|
+
puts "No connected devices found.".yellow
|
|
133
|
+
exit
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Collect and print device information
|
|
137
|
+
return parse_devices(console_output)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
### Gets the list of devices from console output
|
|
142
|
+
def parse_devices(console_output)
|
|
143
|
+
devices = Array.new
|
|
144
|
+
console_output.each do |line|
|
|
145
|
+
devices << extract_device(line)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
return devices.sort! { |a,b| a.model.downcase <=> b.model.downcase }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
### Get a device from the console output
|
|
152
|
+
def extract_device(line)
|
|
153
|
+
# Sample line => [51b64dcb device usb:1-12 product:A6020a40 model:Lenovo_A6020a40 device:A6020a40]
|
|
154
|
+
id = line.match(DEVICE_ID_REGEX)[0]
|
|
155
|
+
device = extract_value("device", line)
|
|
156
|
+
product = extract_value("product", line)
|
|
157
|
+
model = extract_value("model", line)
|
|
158
|
+
|
|
159
|
+
return Device.new(id, device, product, model)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
### Extracts value for the 'key' from a given console output line
|
|
163
|
+
def extract_value(key, line)
|
|
164
|
+
return line.match(key.concat(VALUE_SUFFIX_REGEX))[0].split(':').last
|
|
165
|
+
.gsub('_', ' ')
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
### Formats and prints a device table
|
|
169
|
+
def print_devices_table(devices)
|
|
170
|
+
# Table Header
|
|
171
|
+
header = ROW_FORMAT % ['Model', 'Device', 'Product', 'ID']
|
|
172
|
+
puts header.yellow
|
|
173
|
+
|
|
174
|
+
# Table Rows
|
|
175
|
+
liberated_devices = 0
|
|
176
|
+
devices.each do |d|
|
|
177
|
+
row = ROW_FORMAT % [d.model, d.device, d.product, d.id]
|
|
178
|
+
if d.is_connected
|
|
179
|
+
liberated_devices += 1
|
|
180
|
+
puts row.light_blue
|
|
181
|
+
else
|
|
182
|
+
puts row
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Message
|
|
187
|
+
message = "%d/%d device(s) liberated." % [liberated_devices, devices.size]
|
|
188
|
+
puts message.green
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
### Liberates the specified device
|
|
192
|
+
def liberate(device)
|
|
193
|
+
command = "adb -s %s shell ip -f inet addr show wlan0" % [device.id]
|
|
194
|
+
console_output, console_error, exit_code = execute_shell_command(command)
|
|
195
|
+
|
|
196
|
+
if exit_code == 0
|
|
197
|
+
if console_output != nil
|
|
198
|
+
ip_address = extract_ip_address(console_output)
|
|
199
|
+
adb_connect_tcpip(device, ip_address)
|
|
200
|
+
else
|
|
201
|
+
message = "WiFi is turned off on '%s', turn it on from your device's settings." % [device.model]
|
|
202
|
+
puts message.yellow
|
|
203
|
+
end
|
|
204
|
+
else
|
|
205
|
+
# TODO Display adb error message
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
### Extracts the IPv4 address from the console output
|
|
210
|
+
def extract_ip_address(console_output)
|
|
211
|
+
console_output = console_output.split("\n")
|
|
212
|
+
console_output.each do |line|
|
|
213
|
+
ip_address = line.match(IP_V4_REGEX)
|
|
214
|
+
return ip_address if ip_address != nil
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
return nil
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
### Connect to the device via its IP address and port number
|
|
221
|
+
def adb_connect_tcpip(device, ip_address)
|
|
222
|
+
open_tcpip(device)
|
|
223
|
+
|
|
224
|
+
command = "adb -s %s connect %s:%d" % [device.id, ip_address, PORT_NUMBER]
|
|
225
|
+
console_output, console_error, exit_code = execute_shell_command(command)
|
|
226
|
+
|
|
227
|
+
if exit_code == 0
|
|
228
|
+
message = "%s liberated!" % [device.model]
|
|
229
|
+
puts message.green
|
|
230
|
+
else
|
|
231
|
+
message = "Unable to connect to '%s' via %s. Are we on the same network?" % [device.model, ip_address]
|
|
232
|
+
puts message.red
|
|
233
|
+
puts "Details...".yellow
|
|
234
|
+
puts console_output
|
|
235
|
+
puts console_error
|
|
236
|
+
exit 1
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
### Opens a TCPIP port on the device for a remote connection
|
|
241
|
+
def open_tcpip(device)
|
|
242
|
+
command = "adb -s %s tcpip %d" % [device.id, PORT_NUMBER]
|
|
243
|
+
console_output, console_error, exit_code = execute_shell_command(command)
|
|
244
|
+
|
|
245
|
+
if exit_code != 0
|
|
246
|
+
message = "Unable to open port on '%s'." % [device.model]
|
|
247
|
+
puts message.red
|
|
248
|
+
puts "Details...".yellow
|
|
249
|
+
puts console_output
|
|
250
|
+
puts console_error
|
|
251
|
+
exit 1
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
### This is an elegant method that abstracts the "Open3.popen3" call
|
|
256
|
+
def execute_shell_command(command)
|
|
257
|
+
stdin, stdout, stderr, wait_thr = Open3.popen3(command)
|
|
258
|
+
|
|
259
|
+
console_output = stdout.gets(nil)
|
|
260
|
+
console_error = stderr.gets(nil)
|
|
261
|
+
exit_code = wait_thr.value
|
|
262
|
+
|
|
263
|
+
# Free resources
|
|
264
|
+
stdin.close
|
|
265
|
+
stdout.close
|
|
266
|
+
stderr.close
|
|
267
|
+
|
|
268
|
+
return console_output, console_error, exit_code
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
### Class that holds a device information
|
|
272
|
+
class Device
|
|
273
|
+
attr_accessor :id, :device, :product, :model
|
|
274
|
+
|
|
275
|
+
def initialize(id, device, product, model)
|
|
276
|
+
@id = id
|
|
277
|
+
@device = device
|
|
278
|
+
@product = product
|
|
279
|
+
@model = model
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def matches(key)
|
|
283
|
+
key = key.downcase
|
|
284
|
+
@id.downcase.include?(key) || @device.downcase.include?(key) ||
|
|
285
|
+
@product.downcase.include?(key) || @model.downcase.include?(key)
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def is_connected()
|
|
289
|
+
@id.end_with? ":%d" % [PORT_NUMBER]
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def to_s
|
|
293
|
+
"ID: ".concat(@id)
|
|
294
|
+
.concat(" | Device: ").concat(@device)
|
|
295
|
+
.concat(" | Product: ").concat(@product)
|
|
296
|
+
.concat(" | Model: ").concat(@model)
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
end
|
|
301
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: liberate
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.pre.alpha
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ragunath Jawahar
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2016-07-27 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: colorize
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 0.8.1
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 0.8.1
|
|
27
|
+
description: A gem that liberates your Android devices from USB cables during development.
|
|
28
|
+
email: rj@mobsandgeeks.com
|
|
29
|
+
executables:
|
|
30
|
+
- liberate
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- bin/liberate
|
|
35
|
+
- lib/liberate.rb
|
|
36
|
+
- lib/liberate/version.rb
|
|
37
|
+
homepage: https://github.com/ragunathjawahar/liberate
|
|
38
|
+
licenses:
|
|
39
|
+
- Apache-2.0
|
|
40
|
+
metadata: {}
|
|
41
|
+
post_install_message:
|
|
42
|
+
rdoc_options: []
|
|
43
|
+
require_paths:
|
|
44
|
+
- lib
|
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: 2.0.0
|
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 1.3.1
|
|
55
|
+
requirements: []
|
|
56
|
+
rubyforge_project:
|
|
57
|
+
rubygems_version: 2.6.4
|
|
58
|
+
signing_key:
|
|
59
|
+
specification_version: 4
|
|
60
|
+
summary: Liberate!
|
|
61
|
+
test_files: []
|