replicant-adb 0.0.1 → 1.0.1
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/README.md +32 -34
- data/lib/replicant.rb +3 -1
- data/lib/replicant/commands.rb +10 -0
- data/lib/replicant/commands/adb_command.rb +47 -0
- data/lib/replicant/commands/clear_command.rb +23 -0
- data/lib/replicant/commands/command.rb +85 -0
- data/lib/replicant/commands/device_command.rb +87 -0
- data/lib/replicant/commands/devices_command.rb +57 -0
- data/lib/replicant/commands/env_command.rb +18 -0
- data/lib/replicant/commands/list_command.rb +18 -0
- data/lib/replicant/commands/package_command.rb +19 -0
- data/lib/replicant/commands/reset_command.rb +16 -0
- data/lib/replicant/commands/restart_command.rb +13 -0
- data/lib/replicant/device.rb +7 -2
- data/lib/replicant/log_muncher.rb +83 -0
- data/lib/replicant/process_muncher.rb +47 -0
- data/lib/replicant/repl.rb +24 -9
- data/lib/replicant/styles.rb +24 -10
- data/lib/replicant/version.rb +1 -1
- metadata +30 -43
- data/.travis.yml +0 -4
- data/Gemfile +0 -19
- data/Rakefile +0 -40
- data/lib/replicant/command.rb +0 -352
- data/test/commands/adb_command_spec.rb +0 -58
- data/test/commands/command_spec.rb +0 -90
- data/test/commands/command_spec_base.rb +0 -30
- data/test/commands/device_command_spec.rb +0 -40
- data/test/commands/devices_command_spec.rb +0 -65
- data/test/commands/env_command_spec.rb +0 -30
- data/test/commands/list_command_spec.rb +0 -13
- data/test/commands/package_command_spec.rb +0 -34
- data/test/helper.rb +0 -16
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 25ff78db9805a61866c8ba3be4c9d404ca666858
|
4
|
+
data.tar.gz: 44c65677a410f9bf4b78c3b8d44ccf97ce6c00bd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 636cc5f837bb35dc349c5af94d908368ee9ea8a23172d2ab525ec32e5495000ca190305d864fa83df12f770afaa0a5c57ff9a58be4a2f1389e53de7089fe25fe
|
7
|
+
data.tar.gz: 9acc12a4f28448766545489786faf6ed5b0dc527560dd347bd4b616125fc2ebe723ba84e866aed4993dd81758f6482bfc2d44d756baf293e12e7bd8959ac3101
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
replicant - a repl for adb
|
2
2
|
==========================
|
3
|
+
[](http://badge.fury.io/rb/replicant-adb)
|
4
|
+
[](https://travis-ci.org/mttkay/replicant)
|
3
5
|
|
4
6
|
`replicant` is an interactive shell (a [REPL][2]) for `adb`, the Android Debug Bridge.
|
5
7
|
It is partially based on Chris Wanstrath's excellent [repl][0] command line wrapper.
|
@@ -23,7 +25,7 @@ From here on we list the available commands, fix the device to the first listed
|
|
23
25
|
emulator, uninstall the app, then reset the session.
|
24
26
|
|
25
27
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
26
|
-
|
28
|
+
v0.0.1
|
27
29
|
dP oo dP
|
28
30
|
88 88
|
29
31
|
88d888b. .d8888b. 88d888b. 88 dP .d8888b. .d8888b. 88d888b. d8888P
|
@@ -32,45 +34,34 @@ emulator, uninstall the app, then reset the session.
|
|
32
34
|
dP `88888P' 88Y888P' dP dP `88888P' `88888P8 dP dP dP
|
33
35
|
88
|
34
36
|
dP (c) 2013 Matthias Kaeppler
|
35
|
-
|
36
|
-
|
37
|
-
Type !
|
37
|
+
|
38
|
+
|
39
|
+
Type '!' to see a list of commands, '?' for environment info.
|
38
40
|
Commands not starting in '!' are sent to adb verbatim.
|
39
41
|
Use Ctrl-D (i.e. EOF) to exit.
|
40
42
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
41
43
|
Setting default package to "com.soundcloud.android"
|
42
|
-
|
43
|
-
|
44
|
+
>> !
|
45
|
+
!clear -- clear application data
|
44
46
|
!device -- set a default device to work with
|
45
47
|
!devices -- print a list of connected devices
|
46
|
-
!
|
48
|
+
!logcat -- access device logs
|
47
49
|
!package -- set a default package to work with
|
48
50
|
!reset -- clear current device and package
|
49
51
|
!restart -- restart ADB
|
50
52
|
OK.
|
51
|
-
-- com.soundcloud.android, No device set
|
52
53
|
>> !devices
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
|
55
|
+
[0] Nexus 4 | 005de387d71505d6
|
56
|
+
[1] Genymotion Nexus 4 API 18 768x1280 | 192.168.56.101:5555
|
57
|
+
|
57
58
|
OK.
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
005de387d71505d6 [Nexus 4]
|
62
|
-
emulator-5554 [Android SDK built for x86]
|
63
|
-
|
64
|
-
Setting default device to emulator-5554 [Android SDK built for x86]
|
59
|
+
>> !device 0
|
60
|
+
Setting default device to 005de387d71505d6 [Nexus 4]
|
65
61
|
OK.
|
66
|
-
-- com.soundcloud.android, emulator-5554 [Android SDK built for x86]
|
67
62
|
>> uninstall
|
68
63
|
Success
|
69
64
|
OK.
|
70
|
-
-- com.soundcloud.android, emulator-5554 [Android SDK built for x86]
|
71
|
-
>> !reset
|
72
|
-
OK.
|
73
|
-
-- No package set, No device set
|
74
65
|
>>
|
75
66
|
|
76
67
|
Install
|
@@ -81,22 +72,15 @@ command history and tab-completion, although it's not a requirement.
|
|
81
72
|
`replicant` integrates with `rlwrap` automatically;
|
82
73
|
it's sufficient for it to just be installed.
|
83
74
|
|
84
|
-
|
85
|
-
To proceed, make sure that [bundler][3] is installed on your system. (e.g. `$gem install bundler`)
|
86
|
-
|
87
|
-
Clone this repository, then:
|
88
|
-
|
89
|
-
$ cd replicant
|
90
|
-
$ bundle install
|
91
|
-
$ rake install
|
75
|
+
If all requirements are met, you can install `replicant` as a Ruby gem:
|
92
76
|
|
77
|
+
$ gem install replicant-adb
|
93
78
|
|
94
79
|
Contributing
|
95
80
|
------------
|
96
81
|
|
97
|
-
[](https://travis-ci.org/mttkay/replicant)
|
98
|
-
|
99
82
|
Please hack on replicant and make it better and more feature complete!
|
83
|
+
Here's a general list of guidelines you should follow:
|
100
84
|
|
101
85
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
102
86
|
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
@@ -105,6 +89,20 @@ Please hack on replicant and make it better and more feature complete!
|
|
105
89
|
* Write an executable spec. See existing specs in the test/ folder for examples.
|
106
90
|
* Commit and push until you are happy with your contribution.
|
107
91
|
|
92
|
+
After checking out the project, change into the project dir and do
|
93
|
+
|
94
|
+
$ bundle install
|
95
|
+
|
96
|
+
to make sure all dependencies are installed. After making local changes, you can
|
97
|
+
install locally using
|
98
|
+
|
99
|
+
$ rake install
|
100
|
+
|
101
|
+
And don't forget to regularly
|
102
|
+
|
103
|
+
$ rake test
|
104
|
+
|
105
|
+
|
108
106
|
Copyright
|
109
107
|
---------
|
110
108
|
|
data/lib/replicant.rb
CHANGED
@@ -3,7 +3,9 @@ require 'active_support/core_ext/string/filters.rb'
|
|
3
3
|
require 'active_support/core_ext/object/try.rb'
|
4
4
|
|
5
5
|
require 'replicant/version'
|
6
|
-
require 'replicant/command'
|
7
6
|
require 'replicant/styles'
|
7
|
+
require 'replicant/process_muncher'
|
8
|
+
require 'replicant/log_muncher'
|
9
|
+
require 'replicant/commands'
|
8
10
|
require 'replicant/device'
|
9
11
|
require 'replicant/repl'
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require_relative "commands/command"
|
2
|
+
require_relative "commands/adb_command"
|
3
|
+
require_relative "commands/clear_command"
|
4
|
+
require_relative "commands/device_command"
|
5
|
+
require_relative "commands/devices_command"
|
6
|
+
require_relative "commands/env_command"
|
7
|
+
require_relative "commands/list_command"
|
8
|
+
require_relative "commands/package_command"
|
9
|
+
require_relative "commands/reset_command"
|
10
|
+
require_relative "commands/restart_command"
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class AdbCommand < Command
|
2
|
+
|
3
|
+
class Result
|
4
|
+
attr_accessor :pid, :code, :output
|
5
|
+
|
6
|
+
def to_s
|
7
|
+
"result: pid=#{pid} code=#{code} output=#{output}"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
Result.new.tap do |result|
|
13
|
+
cmd = "#{command}"
|
14
|
+
|
15
|
+
putsd cmd
|
16
|
+
|
17
|
+
if interactive?
|
18
|
+
system cmd
|
19
|
+
else
|
20
|
+
result.output = `#{cmd}`
|
21
|
+
output result.output
|
22
|
+
end
|
23
|
+
result.pid = $?.pid
|
24
|
+
result.code = $?.exitstatus
|
25
|
+
putsd "Command returned with exit status #{result.code}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def command
|
30
|
+
adb = "adb"
|
31
|
+
adb << " -s #{@repl.default_device.id}" if @repl.default_device
|
32
|
+
adb << " #{args}"
|
33
|
+
adb << " #{@repl.default_package}" if @repl.default_package && package_dependent?
|
34
|
+
adb << " 2>&1" # redirect stderr to stdout so that we can silence it
|
35
|
+
adb
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def interactive?
|
41
|
+
args == "shell" || args.start_with?("logcat")
|
42
|
+
end
|
43
|
+
|
44
|
+
def package_dependent?
|
45
|
+
["uninstall"].include?(args)
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class ClearCommand < Command
|
2
|
+
|
3
|
+
def description
|
4
|
+
"clear application data"
|
5
|
+
end
|
6
|
+
|
7
|
+
# TODO: this is not a very good argument validator
|
8
|
+
def valid_args?
|
9
|
+
args.present? || @repl.default_package
|
10
|
+
end
|
11
|
+
|
12
|
+
def usage
|
13
|
+
"#{name} [com.example.package|<empty>(when default package is set)]"
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
package = args.present? ? args : @repl.default_package
|
18
|
+
# Clear app data - cache, SharedPreferences, Databases
|
19
|
+
AdbCommand.new(@repl, "shell su -c \"rm -r /data/data/#{package}/*\"").execute
|
20
|
+
# Force application stop to recreate shared preferences, databases with new launch
|
21
|
+
AdbCommand.new(@repl, "shell am force-stop #{package}").execute
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
class Command
|
4
|
+
|
5
|
+
include Styles
|
6
|
+
|
7
|
+
def self.inherited(subclass)
|
8
|
+
@@subclasses ||= []
|
9
|
+
@@subclasses << subclass
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.all
|
13
|
+
(@@subclasses - [AdbCommand, ListCommand, EnvCommand]).map do |clazz|
|
14
|
+
clazz.new(nil)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.load(repl, command_line)
|
19
|
+
if command_line == '!'
|
20
|
+
# load command that lists available commands
|
21
|
+
ListCommand.new(repl)
|
22
|
+
elsif command_line == '?'
|
23
|
+
EnvCommand.new(repl)
|
24
|
+
elsif command_line.start_with?('!')
|
25
|
+
# load custom command
|
26
|
+
command_parts = command_line[1..-1].split
|
27
|
+
command_name = command_parts.first
|
28
|
+
command_args = command_parts[1..-1].join(' ')
|
29
|
+
command_class = "#{command_name.capitalize}Command"
|
30
|
+
begin
|
31
|
+
clazz = Object.const_get(command_class)
|
32
|
+
clazz.new(repl, command_args)
|
33
|
+
rescue NameError => e
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
else
|
37
|
+
# forward command to ADB
|
38
|
+
AdbCommand.new(repl, command_line.strip)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_reader :args
|
43
|
+
|
44
|
+
def initialize(repl, args = nil, options = {})
|
45
|
+
@repl = repl
|
46
|
+
@args = args.strip if args
|
47
|
+
@options = options
|
48
|
+
end
|
49
|
+
|
50
|
+
def name
|
51
|
+
"!#{self.class.name.gsub("Command", "").downcase}"
|
52
|
+
end
|
53
|
+
|
54
|
+
# subclasses override this to provide a description of their functionality
|
55
|
+
def description
|
56
|
+
"TODO: description missing"
|
57
|
+
end
|
58
|
+
|
59
|
+
# subclasses override this to provide a usage example
|
60
|
+
def usage
|
61
|
+
end
|
62
|
+
|
63
|
+
def execute
|
64
|
+
if valid_args?
|
65
|
+
run
|
66
|
+
else
|
67
|
+
output "Invalid arguments. Ex.: #{usage}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def valid_args?
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
def output(message)
|
78
|
+
@repl.output(message) unless @options[:silent]
|
79
|
+
end
|
80
|
+
|
81
|
+
def putsd(message)
|
82
|
+
puts "[DEBUG] #{message}" if @repl.debug?
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
class DeviceCommand < Command
|
2
|
+
|
3
|
+
@@threads = []
|
4
|
+
|
5
|
+
def initialize(repl, args = nil, options = {})
|
6
|
+
super
|
7
|
+
@process_muncher = ProcessMuncher.new(repl)
|
8
|
+
@log_muncher = LogMuncher.new(repl)
|
9
|
+
end
|
10
|
+
|
11
|
+
def description
|
12
|
+
"set a default device to work with"
|
13
|
+
end
|
14
|
+
|
15
|
+
def usage
|
16
|
+
"#{name} [<index>|<device_id>]"
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid_args?
|
20
|
+
args.present? && /\S+/ =~ args
|
21
|
+
end
|
22
|
+
|
23
|
+
def run
|
24
|
+
default_device = if index? && (1..devices.size).include?(args.to_i)
|
25
|
+
# user selected by index
|
26
|
+
devices[args.to_i - 1]
|
27
|
+
else
|
28
|
+
# user selected by device ID
|
29
|
+
devices.detect { |d| d.id == args }
|
30
|
+
end
|
31
|
+
|
32
|
+
if default_device
|
33
|
+
@repl.default_device = default_device
|
34
|
+
output "Default device set to #{default_device.name}"
|
35
|
+
|
36
|
+
# kill any existing threads
|
37
|
+
putsd "Found #{@@threads.size} zombie threads, killing..." unless @@threads.empty?
|
38
|
+
@@threads.select! { |t| t.exit }
|
39
|
+
|
40
|
+
@@threads << @log_muncher.munch_logs do |logfile|
|
41
|
+
observe_pid_changes(logfile)
|
42
|
+
end
|
43
|
+
|
44
|
+
output "Logs are available at #{LogMuncher::LOGFILE}"
|
45
|
+
else
|
46
|
+
output "No such device"
|
47
|
+
end
|
48
|
+
|
49
|
+
default_device
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def index?
|
55
|
+
/^\d+$/ =~ args
|
56
|
+
end
|
57
|
+
|
58
|
+
def devices
|
59
|
+
@devices ||= DevicesCommand.new(@repl, nil, :silent => true).execute
|
60
|
+
end
|
61
|
+
|
62
|
+
def observe_pid_changes(logfile)
|
63
|
+
@@threads << @process_muncher.scan_pid do |new_pid|
|
64
|
+
@log_muncher.current_pid = new_pid
|
65
|
+
if new_pid
|
66
|
+
log_state_change!(logfile, "#{@repl.default_package} (pid = #{new_pid})")
|
67
|
+
else
|
68
|
+
log_state_change!(logfile, "<all>")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def log_message!(o, message)
|
74
|
+
o.puts "*" * Styles::CONSOLE_WIDTH
|
75
|
+
o.puts " #{message}"
|
76
|
+
o.puts "*" * Styles::CONSOLE_WIDTH
|
77
|
+
o.flush
|
78
|
+
end
|
79
|
+
|
80
|
+
def log_state_change!(o, change)
|
81
|
+
msg = "Detected change in device or target package\n"
|
82
|
+
msg << "-" * Styles::CONSOLE_WIDTH
|
83
|
+
msg << "\n --> device = #{@repl.default_device.name}"
|
84
|
+
msg << "\n --> process = #{change}"
|
85
|
+
log_message!(o, msg)
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class DevicesCommand < Command
|
2
|
+
def description
|
3
|
+
"print a list of connected devices"
|
4
|
+
end
|
5
|
+
|
6
|
+
def run
|
7
|
+
adb = AdbCommand.new(@repl, "devices -l", :silent => true)
|
8
|
+
device_lines = adb.execute.output.lines.to_a.reject do |line|
|
9
|
+
line.strip.empty? || line.include?("daemon") || line.include?("List of devices")
|
10
|
+
end
|
11
|
+
|
12
|
+
device_lines.reject! { |l| l =~ /offline/ }
|
13
|
+
device_ids = device_lines.map { |l| /([\S]+)\s+device/.match(l)[1] }
|
14
|
+
device_products = device_lines.map { |l| /product:([\S]+)/.match(l).try(:[], 1) }
|
15
|
+
|
16
|
+
device_names = device_lines.zip(device_ids).map do |l, id|
|
17
|
+
/model:([\S]+)/.match(l).try(:[], 1) || detect_device_name(id)
|
18
|
+
end
|
19
|
+
|
20
|
+
device_indices = (1..device_ids.size).to_a
|
21
|
+
devices = device_indices.zip(device_ids, device_names, device_products).map do |idx, id, name, product|
|
22
|
+
Device.new(idx, id, humanize_name(name, product))
|
23
|
+
end
|
24
|
+
|
25
|
+
output ""
|
26
|
+
output devices_string(devices)
|
27
|
+
output ""
|
28
|
+
devices
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def detect_device_name(id)
|
34
|
+
if id.start_with?("emulator-")
|
35
|
+
"Android emulator"
|
36
|
+
else
|
37
|
+
"Unknown device"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def humanize_name(name_string, product)
|
42
|
+
if product == "vbox86p"
|
43
|
+
"Genymotion " + name_string.gsub(/___[\d_]+___/, "_")
|
44
|
+
else
|
45
|
+
name_string
|
46
|
+
end.gsub('_', ' ').squish
|
47
|
+
end
|
48
|
+
|
49
|
+
def devices_string(devices)
|
50
|
+
device_string = if devices.any?
|
51
|
+
padding = devices.map { |d| d.name.length }.max
|
52
|
+
devices.map { |d| "[#{d.idx}] #{d.name}#{' ' * (padding - d.name.length)} | #{d.id}" }.join("\n")
|
53
|
+
else
|
54
|
+
"No devices found"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|