# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
#     https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
#     https://docs.fastlane.tools/plugins/available-plugins
#

opt_out_usage

# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane

default_platform(:android)

def retry_adb_command(adb_command, step_name: nil)
    if !step_name
      step_name = "adb: " + adb_command
    end
    adb_success = false
    iter = 0
    pause = 4
    max_duration = 240
    while adb_success != true && iter < max_duration/pause do
      begin
        iter = iter + 1
        res = adb(
          command: adb_command,
          step_name: step_name
        )
      rescue => ex
        adb_success = false
        UI.error(ex)
        puts "Retrying in " + pause.to_s + " seconds..."
        sleep pause
      else
        adb_success = true
      end
    end
    if adb_success == false
      UI.shell_error! "Encountered a problem with adb command. It was retried " + (max_duration/pause).to_s + " times."
    end
    return res
end

platform :android do
  desc "Runs all the tests"
  lane :test do
    gradle(task: "test")
  end

  desc "Build all the tests without running them"
  lane :build_test do
    gradle(
      tasks: [
        "compileStandardDebugUnitTestSources",
        "compileStandardReleaseUnitTestSources",
        "compileDebugUnitTestSources",
        "compileReleaseUnitTestSources",
        "bundleLibRuntimeToJarDebug",
        "bundleLibRuntimeToJarRelease",
              ],
    )
  end

  desc "Build all the instrumented tests without running them"
  lane :build_androidTest do
    gradle(
      task: "assembleAndroidTest",
    )
  end

  desc "Build Release APK & AAB"
  lane :build_release do
    gradle(
      tasks: ["clean", "assembleStandardRelease", "bundleStandardRelease"]
    )
  end

  desc "Submit a new Beta Build to Crashlytics Beta"
  lane :beta do
    gradle(task: "clean assembleRelease bundleRelease")
    crashlytics
  
    # sh "your_script.sh"
    # You can also use other beta testing services here
  end

  desc "Deploy a new version to the Google Play"
  lane :deploy do
    postpare_metadata_for_googleplay
    prepare_metadata_for_googleplay
    version_code = get_version_code
    version_name = get_version_name
    upload_to_play_store(
        aab: 'app/build/outputs/bundle/standardRelease/exifthumbnailadder-' + version_name + '-standard-release.aab',
        changes_not_sent_for_review: true,
        skip_upload_apk: true,
        skip_upload_changelogs: false,
        #version_code: version_code,
        skip_upload_metadata: false,
        skip_upload_images: false,
        skip_upload_screenshots: false,
    )
    postpare_metadata_for_googleplay
  end

  desc "get version_code"
  lane :get_version_code do
    lines = File.open("../version_last_tag.txt")
    lines.first.split("+")[1].strip
  end

  desc "get version_name"
  lane :get_version_name do
    lines = File.open("../version_last_tag.txt")
    tmp = lines.first.split("+")[0].strip
    tmp.split(" ")[1].strip
  end

  desc "Prepare matadata so that it is not rejected by google play"
  lane :prepare_metadata_for_googleplay do
    sh "../scripts/prepare_metadata_for_googleplay.sh"
  end

  desc "Postpare matadata - This restores to the state before the 'prepare'"
  lane :postpare_metadata_for_googleplay do
    sh "../scripts/post_metadata_for_googleplay.sh"
  end

  desc "Build debug and test APK for screenshots"
  lane :build_for_screengrab do
    #gradle(
    #  task: 'clean'
    #)
    build_android_app(
      task: 'assemble',
      flavor: 'Standard',
      build_type: 'Debug',
      properties: {
        "noVersionInArchivesBaseName" => "true",
      }
    )
    build_android_app(
      task: 'assemble',
      build_type: 'StandardDebugAndroidTest',
      properties: {
        "noVersionInArchivesBaseName" => "true",
      }
    )
  end

  desc "Do screenshots"
  lane :screenshots do
    build_for_screengrab
    prepare_device_for_tests
    capture_android_screenshots(
      use_timestamp_suffix: false,
      clear_previous_screenshots: true,
      locales: ["en-US", "de-DE", "es-ES", "fr-FR", "pt-BR", "tr-TR", "vi", "zh-CN"],
      ending_locale: "en-US",
      reinstall_app: true,
      app_package_name: "com.exifthumbnailadder.app.debug",
      tests_package_name: "com.exifthumbnailadder.app.debug.test",
      use_tests_in_classes: "com.exifthumbnailadder.app.TakeScreenshots",
      app_apk_path: "app/build/outputs/apk/standard/debug/exifthumbnailadder-standard-debug.apk",
      tests_apk_path: "app/build/outputs/apk/androidTest/standard/debug/exifthumbnailadder-standard-debug-androidTest.apk",
      #specific_device: "emulator-5554"
    )
  end

  desc "Do screenshots (sevenInch tablet)"
  lane :screenshots_sevenInch do
    build_for_screengrab
    prepare_device_for_tests
    capture_android_screenshots(
      use_timestamp_suffix: false,
      clear_previous_screenshots: true,
      locales: ["en-US", "de-DE", "es-ES", "fr-FR", "pt-BR", "tr-TR", "vi", "zh-CN"],
      ending_locale: "en-US",
      reinstall_app: true,
      app_package_name: "com.exifthumbnailadder.app.debug",
      tests_package_name: "com.exifthumbnailadder.app.debug.test",
      use_tests_in_classes: "com.exifthumbnailadder.app.TakeScreenshots",
      app_apk_path: "app/build/outputs/apk/standard/debug/exifthumbnailadder-standard-debug.apk",
      tests_apk_path: "app/build/outputs/apk/androidTest/standard/debug/exifthumbnailadder-standard-debug-androidTest.apk",
      device_type: "sevenInch",
      #specific_device: "emulator-5554"
    )
  end

  desc "disable animations on device"
  lane :disable_animation do
    adb(
      #serial: 'emulator-5554',
      command: 'shell settings put global window_animation_scale 0.0',
      step_name: 'disable animation window_animation_scale'
    )
    adb(
      #serial: 'emulator-5554',
      command: 'shell settings put global transition_animation_scale 0.0',
      step_name: 'disable animation transition_animation_scale'
    )
    adb(
      #serial: 'emulator-5554',
      command: 'shell settings put global animator_duration_scale 0.0',
      step_name: 'disable animation animator_duration_scale'
    )
  end

  desc "Prepare device for tests"
  lane :prepare_device_for_tests do

    # print adb version
    adb(
      command: 'version',
      step_name: 'print adb version'
    )

    disable_animation
    disable_virtual_keyboards

    # Sometimes, the '/storage/emulated/0' mountpoint is not ready. Leading to:
    #   rm: /storage/emulated/0/DCIM/test_pics: Transport endpoint is not connected
    # This would make the github job fail. Catch the failure and retry.
    retry_adb_command('shell rm -fr /storage/emulated/0/DCIM/test_pics')

    # Sometimes, the command returns:
    #   mkdir: '/storage/emulated/0/DCIM': Permission denied
    # This would make the github job fail. Catch the failure and retry.
    retry_adb_command('shell mkdir -p /storage/emulated/0/DCIM/test_pics')

    retry_adb_command('push tests/data/exif-samples/jpg/* /storage/emulated/0/DCIM/test_pics', step_name: "push test_pics/ part1")

    retry_adb_command('push tests/data/empty_file tests/data/*.jpg tests/data/text_file.txt /storage/emulated/0/DCIM/test_pics', step_name: "push test_pics/ part2")

    # Uninstall the app so as to reset the MANAGE_EXTERNAL_STORAGE permission.
    # I haven't found another way to remove this permission once it's granted
    adb(
      command: 'shell pm uninstall com.exifthumbnailadder.app.debug || :',
      step_name: "uninstall <app>.debug"
    )

    # Uninstall the previously installed test package. On github actions, don't removing it may give
    # Failed to install APK(s): exifthumbnailadder-*-standard-debug-androidTest.apk
    # INSTALL_FAILED_UPDATE_INCOMPATIBLE: Package com.exifthumbnailadder.app.debug.test signatures do not match the previously installed version; ignoring!
    adb(
      command: 'shell pm uninstall com.exifthumbnailadder.app.debug.test || :',
      step_name: "uninstall <app>.debug.test"
    )
  end

  desc "Get test output from device"
  lane :get_output_of_tests do

    require 'fileutils'
    FileUtils.mkdir_p('../tests/output')
    FileUtils.touch('../tests/output/get_output_of_tests-started-not-completed')

    retry_adb_command('shell mkdir -p /data/local/tmp/screenrecords/')
    retry_adb_command('shell mkdir -p /data/local/tmp/test_output/')
    retry_adb_command('shell mkdir -p /storage/emulated/0/DCIM/test_pics/')

    retry_adb_command('pull -a /data/local/tmp/screenrecords/ tests/output/', step_name: "pull screenrecords/")
    retry_adb_command('pull -a /data/local/tmp/test_output/ tests/output/', step_name: "pull test_output/")
    retry_adb_command('pull -a /storage/emulated/0/DCIM/test_pics/ tests/output/', step_name: "pull test_pics/")

    FileUtils.rm('../tests/output/get_output_of_tests-started-not-completed')
  end

  desc "Disable Virtual Keyboards"
  lane :disable_virtual_keyboards do
    # Disable virtual keyboards
    #retry_adb_command('shell pm disable-user com.google.android.googlequicksearchbox || :')
    #retry_adb_command('shell pm disable-user com.google.android.inputmethod.latin || :')
    retry_adb_command('shell settings put secure show_ime_with_hard_keyboard 0 || :')
  end

  desc "Enable Virtual Keyboards"
  lane :enable_virtual_keyboards do
    # Re-enable virtual keyboards
    #retry_adb_command('shell pm enable com.google.android.googlequicksearchbox || :')
    #retry_adb_command('shell pm enable com.google.android.inputmethod.latin || :')
    retry_adb_command('shell settings put secure show_ime_with_hard_keyboard 1 || :')
  end

  desc "Run all the instrumented tests except screenshots"
  lane :connectedCheck do |options|
    adb(
      # Get rid of `Viewing full screen, To exit, swipe down from the top` message
      # https://stackoverflow.com/a/62475026
      command: 'shell settings put secure immersive_mode_confirmations confirmed',
      step_name: 'shell settings put secure immersive_mode_confirmations confirmed'
    )

    if ENV["CI"] == "true" && ENV["API_LEVEL"].to_i >= 30
      adb(
        # Turn off WiFi (to avoid using system resources for updates during tests)
        command: 'shell cmd -w wifi set-wifi-enabled disabled',
        step_name: 'turn off WiFi'
      )
    end

    if ENV["DELAY"] && ENV["CI"] == "true"
      # Pause (in seconds) to let the system image in the emulator finish booting.
      puts "Waiting " + ENV["DELAY"] + "seconds."
      sleep ENV["DELAY"].to_i
    end

    if ENV["CI"] == "true"
      wait_for_idle
    end

    prepare_device_for_tests

    gradle(
      task: options[:task],
      properties: {
        "excludeScreenshots" => "NULL",
      }
    )
  end

  # To run only for a single flavor, run:
  # bundle exec fastlane connectedCheck_with_screenrecord task:connectedStandardDebugAndroidTest
  desc "Run all the instrumented tests except screenshots and save test_output & screenrecords"
  lane :connectedCheck_with_screenrecord do |options|

    adb(
      command: 'shell rm -rf /data/local/tmp/screenrecords/',
      step_name: 'remove /data/local/tmp/screenrecords/'
    )
    adb(
      command: 'shell rm -rf /data/local/tmp/test_output/',
      step_name: 'remove /data/local/tmp/test_output/'
    )

    if options[:task]
      begin
        connectedCheck(task: options[:task])
      rescue => ex
        test_failed = true
        UI.error(ex)
      end
    else
      # We split by flavor because with connectedCheck, as soon a one flavor has a failing test,
      # then the subsequent flavors are skipped.
      begin
        connectedCheck(task: "connectedStandardDebugAndroidTest")
      rescue => ex
        test_failed = true
        UI.error(ex)
      end
    end

    enable_virtual_keyboards
    if ENV["CI"] == "true"
      # There are problems in gitlab actions with API 35 where get_output_of_tests continuously crashes (adb pull -a ...)
      # This tries to manage this.
      if ENV["API_LEVEL"].to_i >= 35
        puts "Detected API_LEVEL >= 35. Current API_LEVEL value: " + ENV["API_LEVEL"].to_s
        retry_adb_command('reboot')
      end
      wait_for_idle
    end
    get_output_of_tests

    if test_failed == true
      UI.shell_error! "connectedCheck lane failed."
    end
  end

  desc "Wait until device is considered idle"
  lane :wait_for_idle do
    retry_adb_command('wait-for-device')

    shell_command = "while true; do getprop sys.boot_completed | grep -m 1 '1' && exit; done"
    adb(
      command: "shell '" + shell_command + "'",
      step_name: 'adb "loop until boot_completed"'
    )

    start_time = Time.now
    load_threshold = 0.9
    puts "Start waiting until device is idle (" + Time.now.to_s + ")"
    command = 'shell uptime'
    output = retry_adb_command(command)
    load = `#{"echo '" + output + "' | cut -d , -f 3 | cut -f 2 -d :"}`
    load = load.strip.to_f
    puts "load: " + load.to_s
    end_time = start_time + 1800
    while load > load_threshold && Time.now < end_time do
      if load < 4
        adb_output = adb(
          command: 'shell dumpsys window | grep -E "mCurrentFocus.*Application Not Responding" || :',
          step_name: 'get anr_package if exists'
        )
        anr_package = `#{"echo '" + adb_output + "' | cut -f 2 -d : | sed -e 's/}//' -e 's/^ *//' "}`.strip
        if anr_package != ""
            puts "ANR on screen for: " + anr_package + ". Restarting it."
            # Some suggest that restarting the service with 'am startservice' should restart it,
            # but it doesn't seem to work. So using killall.
            # We additionally restart it, but with killall, system UI is restarted automatically

            begin
              # Killing
              adb(
                command: 'shell su 0 killall ' + anr_package,
                step_name: 'Kill arn_package ' + anr_package
              )
            rescue => ex
              UI.error(ex)
              # Fallback to click if kill didn't work. This location is for a 1080x1920 screen
              adb(
                command: 'shell input tap 540 935 || :',
                step_name: 'Click/tap on screen'
              )
            end

            # Starting Service
            if anr_package == "com.android.systemui"
              adb(
                command: 'shell am start-service -n com.android.systemui/.SystemUIService || :',
                step_name: 'Restart service com.android.systemui'
              )
            end
            #cmd = 'shell pm dump ' + anr_package + ' | grep Services: -A1 | tail -n 1 | sed -e "s/.*{.* .* \(.*\)}.*/\1/"'
            #anr_package_service1 = adb(command: cmd).strip
            #adb(
            #  command: 'shell am start-service -a ' + anr_package_service1 + ' || :',
            #  step_name: 'Restart service ' + anr_package_service1
            #)
        end
      end
      sleep 15
      output = retry_adb_command(command)
      load = `#{"echo '" + output + "' | cut -d , -f 3 | cut -f 2 -d :"}`
      load = load.strip.to_f
      puts "load: " + load.to_s
    end
    if load > load_threshold
      puts "Reached timeout before device is idle."  
    end
    puts "Waited until device is idle for " + (Time.now - start_time).to_i.to_s + " seconds."
  end
end
