replicant-adb 0.0.1 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ [![Gem Version](https://badge.fury.io/rb/replicant-adb.png)](http://badge.fury.io/rb/replicant-adb)
4
+ [![Build Status](https://travis-ci.org/mttkay/replicant.png)](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
- v1.0.0
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 !list to see a list of commands.
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
- -- com.soundcloud.android, No device set
43
- >> !list
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
- !list -- print a list of available commands
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
- 005de387d71505d6 [Nexus 4]
55
- emulator-5554 [Android SDK built for x86]
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
- -- com.soundcloud.android, No device set
59
- >> !device emu1
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
- Replicant hasn't seen a stable release yet, so for now, has to be installed as a local Ruby gem.
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
- [![Build Status](https://travis-ci.org/mttkay/replicant.png)](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
 
@@ -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