# frozen_string_literal: true
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require_relative 'device/emulator'
require_relative 'device/clipboard'
require_relative 'device/network'
require_relative 'device/performance'
require_relative 'device/screen'
require_relative 'device/auth_finger_print'
module Appium
module Core
module Android
module Device
extend Forwardable
# rubocop:disable Layout/LineLength
# @!method open_notifications
# Open Android notifications
#
# @example
#
# @driver.open_notifications
#
# @!method current_activity
# Get current activity name
# @return [String] An activity name
#
# @example
#
# @driver.current_activity # '.ApiDemos'
#
# @!method current_package
# Get current package name
# @return [String] A package name
#
# @example
#
# @driver.current_package # 'com.example.android.apis'
#
# @!method get_system_bars
# Get system bar's information
# @return [String]
#
# @example
#
# @driver.get_system_bars
# @driver.system_bars
#
# @!method get_display_density
# Get connected device's density.
# @return [Integer] The size of density
#
# @example
#
# @driver.get_display_density # 320
#
# @!method get_network_connection
# Get the device network connection current status
# See set_network_connection method for return value
# Same as #network_connection_type in selenium-webdriver.
#
# Returns a key of {:airplane_mode: 1, wifi: 2, data: 4, all: 6, none: 0}
in #network_connection_type
# Returns a number of the mode in +#get_network_connection+
#
# @example
#
# @driver.network_connection_type #=> :all
# @driver.get_network_connection #=> 6
#
# @!method toggle_wifi
# Switch the state of the wifi service only for Android
#
# @return [String]
#
# @example
#
# @driver.toggle_wifi
#
# @!method toggle_data
# Switch the state of data service only for Android, and the device should be rooted
#
# @return [String]
#
# @example
#
# @driver.toggle_data
#
# @!method location
# Get the location of the device.
#
# @return [::Appium::Location]
#
# @example
#
# driver.location #=> ::Appium::Location.new(10, 10, 10)
#
# @!method location=
# Set the location of the device.
#
# @param [::Appium::Location] location Set the location.
#
# @example
#
# driver.location = ::Appium::Location.new(10, 10, 10)
#
# @!method set_location
# Set the location of the device.
#
# @param [String, Number] latitude Set the latitude.
# @param [String, Number] longitude Set the longitude.
# @param [String, Number] altitude Set the altitude.
# @param [String, Number] speed Set the speed to apply the location on Android real devices
# in meters/second @since Appium 1.21.0 and in knots for emulators @since Appium 1.22.0.
# @param [String, Number] satellites Sets the count of geo satellites being tracked in range 1..12 @since Appium 1.22.0.
# This number is respected on Emulators.
# @param [::Appium::Location]
#
# @example
#
# driver.set_location 10, 10, 0
#
# @!method toggle_location_services
# Switch the state of the location service
#
# @return [String]
#
# @example
#
# @driver.toggle_location_services
#
# @!method toggle_airplane_mode
# Toggle flight mode on or off
#
# @example
#
# @driver.toggle_airplane_mode
#
# @!method hide_keyboard(close_key = nil, strategy = nil)
# Hide the onscreen keyboard
# @param [String] close_key The name of the key which closes the keyboard.
# Defaults to 'Done' for iOS(except for XCUITest).
# @param [Symbol] strategy The symbol of the strategy which closes the keyboard.
# XCUITest ignore this argument.
# Default for iOS is +:pressKey+. Default for Android is +:tapOutside+.
#
# @example
#
# @driver.hide_keyboard # Close a keyboard with the 'Done' button
# @driver.hide_keyboard('Finished') # Close a keyboard with the 'Finished' button
# @driver.hide_keyboard(nil, :tapOutside) # Close a keyboard with tapping out side of keyboard
#
# @!method end_coverage(path, intent)
# Android only; Ends the test coverage and writes the results to the given path on device.
# @param [String] path Path on the device to write too.
# @param [String] intent Intent to broadcast when ending coverage.
#
# @!method start_activity(opts)
# Android only. Start a new activity within the current app or launch a new app and start the target activity.
#
# Read https://developer.android.com/studio/command-line/adb#IntentSpec for each flags.
#
# @param opts [Hash] Options
# @option opts [String] :app_package The package owning the activity [required]
# @option opts [String] :app_activity The target activity [required]
# @option opts [String] :app_wait_package The package to start before the target package [optional]
# @option opts [String] :app_wait_activity The activity to start before the target activity [optional]
# @option opts [String] :intent_action The intent action to give it when start the target activity (+-a+) [optional]
# @option opts [String] :intent_category The intent category to give it when start the target activity (+-c+) [optional]
# @option opts [String] :intent_flags The intent flag to give it when start the target activity (+-f+) [optional]
# @option opts [String] :optional_intent_arguments The optional intent action to give it when start the target activity [optional]
# You can set arbitrary arguments with space as string.
# e.g. +'--ez your_extra_bool bool --ei your_extra_int 1'+
# @option opts [bool] :dont_stop_app_on_reset Do not stop the app when the reset is called in Appium create/delete session [optional]
#
# @example
#
# start_activity app_package: 'io.appium.android.apis',
# app_activity: '.accessibility.AccessibilityNodeProviderActivity'
#
# @!method set_network_connection(mode)
# Set the device network connection mode
# Same as +#network_connection_type+ in selenium-webdriver.
#
# @param [String] mode Bit mask that represent the network mode
# Or the key matched with {:airplane_mode: 1, wifi: 2, data: 4, all: 6, none: 0}
#
# Value (Alias) | Data | Wifi | Airplane Mode
# -------------------------------------------------
# 1 (Airplane Mode) | 0 | 0 | 1
# 6 (All network on) | 1 | 1 | 0
# 4 (Data only) | 1 | 0 | 0
# 2 (Wifi only) | 0 | 1 | 0
# 0 (None) | 0 | 0 | 0
#
# @example
#
# @driver.set_network_connection 1
# @driver.set_network_connection :airplane_mode
# @driver.network_connection_type = :airplane_mode # As selenium-webdriver
#
# @!method get_performance_data_types
# Get the information type of the system state which is supported to read such as
# cpu, memory, network, battery via adb commands.
# https://github.com/appium/appium-base-driver/blob/be29aec2318316d12b5c3295e924a5ba8f09b0fb/lib/mjsonwp/routes.js#L300
#
# @example
#
# @driver.get_performance_data_types #=> ["cpuinfo", "batteryinfo", "networkinfo", "memoryinfo"]
#
# @!method get_performance_data(package_name:, data_type:, data_read_timeout: 1000)
# Get the resource usage information of the application.
# https://github.com/appium/appium-base-driver/blob/be29aec2318316d12b5c3295e924a5ba8f09b0fb/lib/mjsonwp/routes.js#L303
# @param [String] package_name: Package name
# @param [String] data_type: Data type get with +get_performance_data_types+
# @param [String] data_read_timeout: Command timeout. Default is 2.
#
# @example
#
# @driver.get_performance_data package_name: package_name, data_type: data_type, data_read_timeout: 2
#
# @!method start_recording_screen(remote_path: nil, user: nil, pass: nil, method: 'PUT', file_field_name: nil, form_fields: nil, headers: nil, force_restart: nil, video_size: nil, time_limit: '180', bit_rate: '4000000', bug_report: nil)
# @param [String] remote_path The path to the remote location, where the resulting video should be uploaded.
# The following protocols are supported: http/https, ftp.
# Null or empty string value (the default setting) means the content of resulting
# file should be encoded as Base64 and passed as the endpoint response value.
# An exception will be thrown if the generated media file is too big to
# fit into the available process memory.
# This option only has an effect if there is screen recording process in progress
# and +forceRestart+ parameter is not set to +true+.
# @param [String] user The name of the user for the remote authentication.
# @param [String] pass The password for the remote authentication.
# @param [String] method The http multipart upload method name. The 'PUT' one is used by default.
# @param [String] file_field_name The name of the form field containing the binary payload in multipart/form-data
# requests since Appium 1.18.0. Defaults to 'file'.
# @param [Array>] form_fields The form fields mapping in multipart/form-data requests since Appium 1.18.0.
# If any entry has the same key in this mapping, then it is going to be ignored.
# @param [Hash] headers The additional headers in multipart/form-data requests since Appium 1.18.0.
# @param [Boolean] force_restart Whether to try to catch and upload/return the currently running screen recording
# (+false+, the default setting on server) or ignore the result of it
# and start a new recording immediately (+true+).
#
# @param [String] video_size The format is widthxheight.
# The default value is the device's native display resolution (if supported),
# 1280x720 if not. For best results,
# use a size supported by your device's Advanced Video Coding (AVC) encoder.
# For example, "1280x720"
# @param [String] time_limit Recording time. 180 seconds is by default.
# Since Appium 1.8.2 the time limit can be up to 1800 seconds (30 minutes).
# Appium will automatically try to merge the 3-minutes chunks recorded
# by the screenrecord utility, however, this requires FFMPEG utility
# to be installed and available in PATH on the server machine. If the utility is not
# present then the most recent screen recording chunk is going to be returned as the result.
# @param [String] bit_rate The video bit rate for the video, in megabits per second.
# 4 Mbp/s(4000000) is by default for Android API level below 27. 20 Mb/s(20000000) for API level 27 and above.
# @param [Boolean] bug_report Set it to +true+ in order to display additional information on the video overlay,
# such as a timestamp, that is helpful in videos captured to illustrate bugs.
# This option is only supported since API level 27 (Android P).
# @return [String] Base64 encoded content of the recorded media file or an empty string
# if the file has been successfully uploaded to a remote location (depends on the actual options)
#
# @example
#
# @driver.start_recording_screen
# @driver.start_recording_screen video_size: '1280x720', time_limit: '180', bit_rate: '5000000'
#
# @!method get_clipboard(content_type: :plaintext)
# Set the content of device's clipboard.
# @param [String] content_type: one of supported content types.
# @return [String]
#
# @example
#
# @driver.get_clipboard #=> "happy testing"
#
# @!method set_clipboard(content:, content_type: :plaintext, label: nil)
# Set the content of device's clipboard.
# @param [String] label: clipboard data label.
# @param [String] content_type: one of supported content types.
# @param [String] content: Contents to be set. (Will encode with base64-encoded inside this method)
#
# @example
#
# @driver.set_clipboard(content: 'happy testing') #=> {"protocol"=>"W3C"}
#
# @!method finger_print(finger_id)
# Authenticate users by using their finger print scans on supported emulators.
#
# @param [Integer] finger_id Finger prints stored in Android Keystore system (from 1 to 10)
#
# @example
#
# @driver.finger_print 1
#
# @!method execute_cdp(cmd, **params)
# Execute Chrome Devtools protocol commands
# https://chromedevtools.github.io/devtools-protocol
#
# @param [String] cmd The name of command
# @option params The parameter for the command as keyword options.
#
# @example
#
# @driver.execute_cdp 'Page.captureScreenshot', quality: 50, format: 'jpeg'
# @driver.execute_cdp 'Page.getResourceTree'
#
# # for Ruby 2,7 and 3+ compatibility
# params = {'timezoneId': 'Asia/Tokyo'}
# driver.execute_cdp 'Emulation.setTimezoneOverride', **params
#
####
## class << self
####
# rubocop:enable Layout/LineLength
class << self
def extended(_mod)
::Appium::Core::Device.extend_webdriver_with_forwardable
::Appium::Core::Device.add_endpoint_method(:open_notifications) do
def open_notifications
execute :open_notifications
end
end
::Appium::Core::Device.add_endpoint_method(:current_activity) do
def current_activity
execute :current_activity
end
end
::Appium::Core::Device.add_endpoint_method(:current_package) do
def current_package
execute :current_package
end
end
::Appium::Core::Device.add_endpoint_method(:get_system_bars) do
def get_system_bars
execute :get_system_bars
end
end
# as alias to get_system_bars
::Appium::Core::Device.add_endpoint_method(:system_bars) do
def system_bars
execute :get_system_bars
end
end
::Appium::Core::Device.add_endpoint_method(:toggle_location_services) do
def toggle_location_services
execute :toggle_location_services
end
end
::Appium::Core::Device.add_endpoint_method(:start_activity) do
def start_activity(opts)
raise 'opts must be a hash' unless opts.is_a? Hash
option = {}
app_package = opts[:app_package]
raise 'app_package is required' unless app_package
app_activity = opts[:app_activity]
raise 'app_activity is required' unless app_activity
option[:appPackage] = app_package
option[:appActivity] = app_activity
app_wait_package = opts.fetch(:app_wait_package, nil)
app_wait_activity = opts.fetch(:app_wait_activity, nil)
option[:appWaitPackage] = app_wait_package if app_wait_package
option[:appWaitActivity] = app_wait_activity if app_wait_activity
intent_action = opts.fetch(:intent_action, nil)
intent_category = opts.fetch(:intent_category, nil)
intent_flags = opts.fetch(:intent_flags, nil)
optional_intent_arguments = opts.fetch(:optional_intent_arguments, nil)
dont_stop_app_on_reset = opts.fetch(:dont_stop_app_on_reset, nil)
option[:intentAction] = intent_action if intent_action
option[:intentCategory] = intent_category if intent_category
option[:intentFlags] = intent_flags if intent_flags
option[:optionalIntentArguments] = optional_intent_arguments if optional_intent_arguments
option[:dontStopAppOnReset] = dont_stop_app_on_reset if dont_stop_app_on_reset
execute :start_activity, {}, option
end
end
# Android, Override included method in bridge
::Appium::Core::Device.add_endpoint_method(:hide_keyboard) do
def hide_keyboard(close_key = nil, strategy = nil)
option = {}
option[:key] = close_key if close_key
option[:strategy] = strategy if strategy
execute :hide_keyboard, {}, option
end
end
# Android, Override included method in bridge
::Appium::Core::Device.add_endpoint_method(:background_app) do
def background_app(duration = 0)
execute :background_app, {}, seconds: duration
end
end
# TODO: TEST ME
::Appium::Core::Device.add_endpoint_method(:end_coverage) do
def end_coverage(path, intent)
execute :end_coverage, {}, path: path, intent: intent
end
end
::Appium::Core::Device.add_endpoint_method(:execute_cdp) do
# SeleniumWebdriver could already define this method
return if method_defined? :execute_cdp
def execute_cdp(cmd, **params)
execute :chrome_send_command, {}, { cmd: cmd, params: params }
end
end
Screen.add_methods
Performance.add_methods
Network.add_methods
Clipboard.add_methods
Emulator.add_methods
Authentication.add_methods
end
end
end # module Device
end # module Android
end
end # module Appium