<commit_before>'use strict';

const path = require('path');
const EOL = require('os').EOL;
const ADB = require('macaca-adb');
const UnlockApk = require('unlock-apk');
const DriverBase = require('driver-base');
const UIAutomatorWD = require('uiautomatorwd');
const ChromeDriver = require('macaca-chromedriver');

const _ = require('./helper');
const logger = require('./logger');
const controllers = require('./controllers');

const reuseStatus = {};

reuseStatus.noReuse = 0;
reuseStatus.reuseEmu = 1;
reuseStatus.reuseEmuApp = 2;
reuseStatus.reuseEmuAppState = 3;

class Android extends DriverBase {
  constructor() {
    super();
    this.adb = null;
    this.apkInfo = null;
    this.args = null;
    this.chromedriver = null;
    this.chromeDriverPort = null;
    this.proxy = null;
    this.udid = null;
    this.uiautomator = null;
    this.isChrome = null;
    this.isVirtual = true;
    this.contexts = [];
    this.context = null;
    this.isWaitActivity = false;
    this.isActivityReady = false;
  }
}

Android.prototype.startDevice = function * (caps) {
  this.args = _.clone(caps);
  this.isChrome = this.args.browserName && this.args.browserName.toLowerCase() === 'chrome';
  this.initReuse();
  this.initAdb();
  yield this.initDevice();
  yield this.initUiautomator();
  yield this.getApkInfo();
  yield this.unlock();
  yield this.launchApk();

  this.autoAcceptAlerts = Boolean(caps.autoAcceptAlerts);
  this.autoDismissAlerts = Boolean(caps.autoDismissAlerts);
  this.isWaitActivity = Boolean(caps.isWaitActivity);

  if (this.isWaitActivity) {
    yield this.waitActivityReady();
  }

  if (this.isChrome) {
    yield this.getWebviews();
  }
};

Android.prototype.stopDevice = function * () {
  this.chromedriver && this.chromedriver.stop();
  if (this.isVirtual && this.args.reuse === reuseStatus.noReuse) {
    return ADB
      .emuKill()
      .catch(e => {
        logger.warn(e);
      });
  }

  return Promise.resolve();
};

Android.prototype.isProxy = function() {
  return !!this.proxy;
};

Android.prototype.whiteList = function(context) {
  var basename = path.basename(context.url);
  const whiteList = [
    'context',
    'contexts',
    'screenshot',
    'back',
    'tap',
    'source',
    'keys',
    'url',
    'title'
  ];
  return !!~whiteList.indexOf(basename);
};

Android.prototype.proxyCommand = function * (url, method, body) {

  if (this.autoAcceptAlerts) {
    const acceptUrl = '/wd/hub/session/:sessionId/accept_alert';
    yield this.proxy.sendCommand(acceptUrl, 'POST', {});
  } else if (this.autoDismissAlerts) {
    const dismissUrl = '/wd/hub/session/:sessionId/dismiss_alert';
    yield this.proxy.sendCommand(dismissUrl, 'POST', {});
  }

  url = url.replace('property', 'attribute');
  return yield this.proxy.sendCommand(url, method, body);
};

Android.prototype.waitActivityReady = function * () {

  yield _.sleep(1000);

  try {
    this.isActivityReady = yield this.adb.isActivityReady(this.apkInfo.package, this.apkInfo.activity);
  } catch (e) {
    logger.info(`waiting for activity: \`${this.apkInfo.activity}\` ready`);
  }

  if (!this.isActivityReady) {
    yield this.waitActivityReady();
  }
};

Android.prototype.initAdb = function() {
  this.adb = new ADB();
};

Android.prototype.initReuse = function() {
  let reuse = parseInt(this.args.reuse, 10);
  if (!reuse && reuse !== reuseStatus.noReuse) {
    reuse = reuseStatus.reuseEmu;
  }
  this.args.reuse = reuse;
};

Android.prototype.initUiautomator = function * () {
  this.uiautomator = new UIAutomatorWD();
  this.proxy = this.uiautomator;<commit_msg>add permission scan phase(async)<commit_after>'use strict';

const path = require('path');
const EOL = require('os').EOL;
const ADB = require('macaca-adb');
const UnlockApk = require('unlock-apk');
const DriverBase = require('driver-base');
const UIAutomatorWD = require('uiautomatorwd');
const ChromeDriver = require('macaca-chromedriver');

const _ = require('./helper');
const logger = require('./logger');
const controllers = require('./controllers');

const reuseStatus = {};

reuseStatus.noReuse = 0;
reuseStatus.reuseEmu = 1;
reuseStatus.reuseEmuApp = 2;
reuseStatus.reuseEmuAppState = 3;

class Android extends DriverBase {
  constructor() {
    super();
    this.adb = null;
    this.apkInfo = null;
    this.args = null;
    this.chromedriver = null;
    this.chromeDriverPort = null;
    this.proxy = null;
    this.udid = null;
    this.uiautomator = null;
    this.isChrome = null;
    this.isVirtual = true;
    this.contexts = [];
    this.context = null;
    this.isWaitActivity = false;
    this.isActivityReady = false;
  }
}

Android.prototype.startDevice = function * (caps) {
  this.args = _.clone(caps);
  this.isChrome = this.args.browserName && this.args.browserName.toLowerCase() === 'chrome';
  this.initReuse();
  this.initAdb();
  yield this.initDevice();
  yield this.initUiautomator();
  yield this.getApkInfo();
  yield this.unlock();
  yield this.skipPermission();
  yield this.launchApk();

  this.autoAcceptAlerts = Boolean(caps.autoAcceptAlerts);
  this.autoDismissAlerts = Boolean(caps.autoDismissAlerts);
  this.isWaitActivity = Boolean(caps.isWaitActivity);

  if (this.isWaitActivity) {
    yield this.waitActivityReady();
  }

  if (this.isChrome) {
    yield this.getWebviews();
  }
};

Android.prototype.stopDevice = function * () {
  this.chromedriver && this.chromedriver.stop();
  if (this.isVirtual && this.args.reuse === reuseStatus.noReuse) {
    return ADB
      .emuKill()
      .catch(e => {
        logger.warn(e);
      });
  }

  return Promise.resolve();
};

Android.prototype.isProxy = function() {
  return !!this.proxy;
};

Android.prototype.whiteList = function(context) {
  var basename = path.basename(context.url);
  const whiteList = [
    'context',
    'contexts',
    'screenshot',
    'back',
    'tap',
    'source',
    'keys',
    'url',
    'title'
  ];
  return !!~whiteList.indexOf(basename);
};

Android.prototype.proxyCommand = function * (url, method, body) {

  if (this.autoAcceptAlerts) {
    const acceptUrl = '/wd/hub/session/:sessionId/accept_alert';
    yield this.proxy.sendCommand(acceptUrl, 'POST', {});
  } else if (this.autoDismissAlerts) {
    const dismissUrl = '/wd/hub/session/:sessionId/dismiss_alert';
    yield this.proxy.sendCommand(dismissUrl, 'POST', {});
  }

  url = url.replace('property', 'attribute');
  return yield this.proxy.sendCommand(url, method, body);
};

Android.prototype.skipPermission = function *() {
  // execute and let continue install target app, consider make this an external file
  const skipPermissionUrl = `/wd/hub/permission/skip`;
  return yield this.proxy.sendCommand(skipPermissionUrl, 'POST', {
    'value':['继续安装', '下一步', '好', '允许', '确定', '我知道了'],
    'repeat':5});
};

Android.prototype.waitActivityReady = function *() {

  yield _.sleep(1000);

  try {
    this.isActivityReady = yield this.adb.isActivityReady(this.apkInfo.package, this.apkInfo.activity);
  } catch (e) {
    logger.info(`waiting for activity: \`${this.apkInfo.activity}\` ready`);
  }

  if (!this.isActivityReady) {
    yield this.waitActivityReady();
  }
};

Android.prototype.initAdb = function() {
  this.adb = new ADB();
};

Android.prototype.initReuse = function() {
  let reuse = parseInt(this.args.reuse, 10);
  if (!reuse && reuse !== reuseStatus.noReuse) {
    reuse = reuseStatus.reuseEmu;
  }
  this.args.reuse = reuse;
};

Android.prototype.initUiautomator = function * () {
  this.uiautomator = new UIAutomatorWD();
  this.proxy = this.uiautomator;
{'fraction_non_alphanumeric': 0.1548959851596661, 'fraction_numerical': 0.0027825626076586722, 'mean_word_length': 3.8107074569789674, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '620578', 'n_tokens_mistral': 2594, 'n_tokens_neox': 2538, 'n_words': 477}