2020年6月23日火曜日

Android 充電ケーブル挿す、抜くのイベントを拾う

やりたいこと
Android で充電ケーブルを挿したときと、抜いた時のイベントを拾う方法を勉強したので、手順をまとめておきます。

環境
Windows 10
Android Studio : 4.0

テスト環境
Nexus S API 22(emulator) : Android5.1
Pixel 3 API 26(emulator) : Android8.0

参考にしたサイト

[(1)ブロードキャストレシーバーについて]
https://developer.android.com/guide/components/broadcasts
[(2)電源管理について]
https://developer.android.com/training/monitoring-device-state/battery-monitoring?hl=ja

ざっくりと
ざっくり説明すると以下の手順となります。
(1)manifestにひろいたいイベントと関数をかく。(android7まで用)
(2)イベントを拾う用のクラスを作成する。そのクラスはBroadcastReceiverを継承させる。
(3)イベントを拾う用のクラスをnewして、レシーバーとして登録する。

Android7までなら(1)と(2)だけでイベントを拾うことができます。 アプリがいなくてもイベントが拾えます。

Android8以降は(1)と(2)だけではイベントを拾えなくなってるので、(3)の手順が必要になります。 しかもAndroid8以降はアプリが起きていないと拾えなくなってます。 この辺のことは参考にしたサイト(1)に詳しく記載されております。

以降は手順を細かく記載します。

Step1.Manifestの登録
AndroidManifest.xmlに下記の6行を追加します。
        
            
                <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
                <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
            
        
「PowerConnectionReceiver」はイベントをキャッチするクラスの名前です。
 任意の名前でOKです。
「ACTION_POWER_CONNECTED」は接続したときのイベントを拾うときのIntent、
「ACTION_POWER_DISCONNECTED」は抜いた時のイベントを拾うときのIntentとなります。

manifest全体ではこうなります。
 



    
        
            
                <action android:name="android.intent.action.MAIN" /&gt

                <category android:name="android.intent.category.LAUNCHER" /&gt
            
        
        
    
        
            
                <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
                <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
            
        
       
       
    


Step2.イベントをキャッチするクラスを追加
2-1.下図のようにクラスを追加します。クラス名はStep1で定義したものと同じ名前としてください。


2-2.BroadcastReceiverを継承します。
 
import android.content.BroadcastReceiver;

public class PowerConnectionReceiver extends BroadcastReceiver {
}
2-3.onReceiveメソッドを追加します。
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class PowerConnectionReceiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
        
    }
}
2-4.イベントを拾ったときに文字列を画面に表示するようにしておきました。
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class PowerConnectionReceiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {

        if (intent.getAction() == Intent.ACTION_POWER_CONNECTED)  {
            Toast.makeText(context, "ケーブルを接続した", Toast.LENGTH_LONG ).show();
        }
        else if (intent.getAction() == Intent.ACTION_POWER_DISCONNECTED) {
            Toast.makeText(context, "ケーブルを抜いた", Toast.LENGTH_LONG ).show();
        }
    }
}

Android 7以前が対象の場合は、Step1, Step2までで完了となります。Android5.1で動作することが確認できました。
さらにAndroid 7以前の場合はアプリが起動中でなくても、このイベントが発生するとアプリが起動します。
Step3.レシーバーを明示的に登録する
Android8以降はStep1のmanifestの方法でイベントがひろえなくなってます。
そこで、明示的にPowerConnectionReceiverをnewしてレシーバーとして登録しておきます。
以下のようなコードとなります。
 
        BroadcastReceiver br = new PowerConnectionReceiver();
        IntentFilter filter = new IntentFilter(Intent.ACTION_POWER_CONNECTED);
        filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
        this.registerReceiver(br, filter);

これをMainActivityのonCreateメソッドに追加しました。
 
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        // ここから追加
        BroadcastReceiver br = new PowerConnectionReceiver();
        IntentFilter filter = new IntentFilter(Intent.ACTION_POWER_CONNECTED);
        filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
        this.registerReceiver(br, filter);

    }
※Googleのサイトの用語でいうと「コンテキスト登録されたレシーバー」というものになります。

Step4.テスト
emulatorで電源挿抜のテストをします。
emulatorの右の操作パネルの一番したのボタンをクリックします。

「Extended controls」ダイアログが表示されるので、Batteryを選択し、Charger connectionを操作します。

ここを操作すると電源ケーブルの挿抜の操作が仮想的にできます。
emulatorの下側に文字列が表示されたら成功です。

Android8でも確認。できました。

 
Step5.今後の課題
Android8以降は、アプリが生存していないとイベントを拾えないので、そこを解決したいです。 ヒントとなるのが、JobScheduler とserviceかなと考えてるので、この辺を勉強していきたいと思ってます。

2020年6月22日月曜日

Appium 入門6 ( 電卓のボタン押す)

はじめに
Appiumを使って、電卓のボタンを押すところまでやってみました。
今までjavascriptでやってきたのですが、うまくできなかったために、javaに乗り換えました。

環境
OS : Windows 10
スマホ : emulator (Pixel3 API 26)
Android Studio : 3.6.3
Appium : 1.17.1
言語 : java

参考にしたサイト

https://www.swtestacademy.com/appium-tutorial/
↑ 細かく書かれており、とても参考になりました。

1.準備
appium等のインストールは完了した状態で実施しております。
まだの場合はAppium入門1 を参考に、 javascriptでサンプルコードが動作するところまで実施できるようにしてみてください。
2.intellij IDEA をインストール
下記のサイトから無料版(コミュニティ)の方をダウンロードします。  https://www.jetbrains.com/ja-jp/idea/download/#section=windows
3.dependencies.の設定
3-1. IntelliJ IDEAを起動する。
3-2.+ Create New Projectを押す。
3-3.Mavenを選択して、Nextボタンを押す

3-4.Nameに適当な名前を付けてFinishボタンを押す。
3-5.https://mvnrepository.com/ にアクセスする。
3-6.「io.appium」 で検索する。
java-client - 最新のバージョンを選択し、mavenというタブに表示されたxmlをコピーする。
 3-7.コピーしたものをintellj IDEAのpom.xmlにペーストする。
dependenciesというタグで囲む必要があります。
同じようにselenium-javaと, testngもコピペします。
最終的にはこんな感じになります。
 
	
    4.0.0

    org.example
    untitled
    1.0-SNAPSHOT

  
    
        
        
            io.appium
            java-client
            7.3.0
        

        
        
            org.seleniumhq.selenium
            selenium-java
            3.141.59
        

        
        
            org.testng
            testng
            7.1.0
            test
        

    
  
  
	
3-8. versionの数字が赤い場合は、下図の更新ボタンを押すか、intelljを再起動すると     読み込まれるようです。(この辺はちょっと怪しいです。) 
 
4.inspectorで情報取得
4-1. emulator を起動
4-2. 電卓アプリを起動
4-3. appiumを起動
4-4. uiautomatorviewerを起動
4-5. 電卓の1のボタンのresource-idを後で使います。
※uiautomatorviewerの使い方はこちらを参考にしてください。

5.emulatorの情報取得
5-1.emulatorを起動しておく
5-2.PCでコマンドプロンプトを起動する。
5-3.adb devices とコマンドプロンプトに打ち込んで実行する。
※adbコマンドが実行できない場合は、環境変数のpathに%ANDROID_HOME%\platform-toolsを追加するか、platform-toolsにcdで移動して、実行してください
5-4.表示された文字列を後で使います。

6.javaのコードを作成
6-1. intellJ IDEAで src - test - javaを右クリック
New - Java Class を追加 - 任意の名前でクラスを追加する。

6-2. ここまでで集めた情報を使って次のコードを書きます。
①~④のところに今まで集めた情報を入れます。 後は特に変更しませんでした。
参考にしたサイトのコードとは少し異なります。不要と思うところを削除したためです。

      import io.appium.java_client.MobileElement;
      import io.appium.java_client.android.AndroidDriver;
      import org.openqa.selenium.By;
      import org.openqa.selenium.remote.DesiredCapabilities;
      import org.openqa.selenium.support.ui.ExpectedConditions;
      import org.openqa.selenium.support.ui.WebDriverWait;
      import org.testng.annotations.AfterMethod;
      import org.testng.annotations.BeforeMethod;
      import org.testng.annotations.Test;

      import java.net.MalformedURLException;
      import java.net.URL;

      public class calctest {
          public AndroidDriver driver;
          public WebDriverWait wait;

          @BeforeMethod
          public void setup () throws MalformedURLException {
              DesiredCapabilities caps = new DesiredCapabilities();
              caps.setCapability("deviceName", "Pixel 3 API 26"); // ①emulatorの名前
              caps.setCapability("udid", "emulator-5554");        // ②adb devicesコマンドの結果
              caps.setCapability("platformName", "Android");      // ③androidなのでandroid

              driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"),caps);
              wait = new WebDriverWait(driver, 10);
          }

          @Test
          public void basicTest () throws InterruptedException {
              //Click and pass Splash
              wait.until(ExpectedConditions.visibilityOfElementLocated
                      (By.id("com.android.calculator2:id/digit_1"))).click();    // ④uiautomatorviewer.exeで取得した情報を
          }

          @AfterMethod
          public void teardown(){
              driver.quit();
          }
      }

      
	  

7.実行
7-1. emulatorを起動(「サーバーVXXXXXを起動する」ボタンも押しておく)
7-2. emulator上で電卓アプリを起動
7-3. intellj IDEAで、Runメニュー Runを押す。
7-4. 電卓の1が押せたら成功です。

8.できた
こんな感じで押せました。

2020年6月17日水曜日

Appium 入門5 ( UIの情報を取得する方法を整理)

はじめに
AppiumでUIの情報を取得する方法を整理します。
いろいろあるようなのですが、試してみてできたものを記載します。
使用した環境はこちらです。

環境

OS : Windows 10
スマホ : emulator (Pixel3 API 26)
Android Studio : 3.6.3
Appium : 1.17.1

準備
emulatorを起動させておきます。
1.android studio 起動
2.Toolsメニュー - AVD Manager
3.再生ボタンを押して起動する
4.情報を取得したいアプリを起動しておく。(電卓で試しました。)

(方法1)Appium Inspector を使う方法
1.コマンドプロンプトで、「adb devices」と打ち込みます。表示される情報を覚えておきます。


※adbコマンドが実行できない場合は、環境変数のpathに%ANDROID_HOME%\platform-toolsを追加するか、platform-toolsにcdで移動して、実行してください

2.Appiumを起動する。
3.サーバvXXXを起動するボタンを押す。
4.右上の虫眼鏡のボタンを押す。

5.しばらく待つと下の画面が表示されます。

6.左下のテキストボックスに次の値を入れます。
udid, テキスト, emulator-5554 ← 手順1で表示された文字列
platformName, テキスト, Android

7.セッションを開始するボタンを押す。
そうすると、emulatorの画面が表示され、マウスで要素をクリックすると右のペインに情報が表示されます。

できた。
ネットの情報や、「実践 Appium」ではWindowsではInspectorは使えないという記載があるため、 だめなパターンがあるのかもしれないです。電卓ではできました。


(方法2)uiautomatorviewer を使う方法

1 uiautomatorviewerを起動

%ANDROID_HOME%\tools\binにある「uiautomatorviewer.bat」をダブルクリックします。
※uiautomatorviewer.batが起動しない場合は、こちらが参考になるかもしれません。

2 uiautomatorviewerで情報を取得

起動したら、左上のボタンを押します。
ちょっと待つと画像が表示されるので、押したいボタンをマウスでクリックします。
右のペインにクリックしたボタンの情報が表示されます。

できた。
まとめ
AppiumでUIの情報を取得する方法を2通り、やってみました。
①Appium Inspector
②uiautomatorviewer
②は設定も特に不要で、起動するだけでよいので、こっちのほうが良いのかもしれません。
「実践 Appium」という書籍にはほかの方法も記載されていたので、試してたら追記します。

2020年6月14日日曜日

Appium 入門4 (javascript から javaに乗り換える)

前回までで、サンプルアプリのボタンが押せるところまでできたので、
別のアプリの操作をやってみようと思いました。

そこで、電卓のアプリのボタン操作にトライしてみたのですが、
javascrpt (webdriverio)だとやりかたがよくわからず、断念しました。

電卓のボタンの押し方がわからない...

ということで、javaに乗り換えることにしました。

こちらのwebページを参考に進めていったら、電卓のボタンが押せるところまでできました。


手順はこんど整理しようと思います。





2020年6月9日火曜日

Appium 入門 3 ( サンプルアプリのボタンを押す)

前回の記事で、サンプルアプリでHello World!を表示されるまでをやってみました。
やったというか、ただ公式サイトのサンプルコードをコピペしただけですが...

今回はこのサンプルアプリについているボタンを押すところをAppiumでやってみます。
サンプルアプリ(API Demos)を起動して、Views → Buttons と遷移させると表示されるボタンです。

初心者の自分にとってはとても大変でした。
環境
Windows 10
Android 5.1 (実機)
appium 1.15.1

1.概要
大まかには、この画面やボタンの情報を取得して、それを元に前回のjavascriptのコードに反映させるという流れとなります。

2.画面の情報取得( appActivity )
const opts = {
  path: '/wd/hub',
  port: 4723,
  capabilities: {
    platformName: "Android",
    platformVersion: "5.1",
    deviceName: "002628105537",
    app: "C:/Users/XXXXX/Desktop/appium_test/ApiDemos-debug.apk", 
    appPackage: "io.appium.android.apis",
    appActivity: ".view.TextFields",   // ← ここに入れる情報
    automationName: "UiAutomator2"
  }
};
javascriptの 「appActivity」に入れる情報を取得します。
実機の方でサンプルアプリを起動して、押下したいボタンの画面を表示しておき、
コマンドプロンプトで「adb shell」と打ち込みます。

※adbコマンドが実行できない場合は、環境変数のpathに%ANDROID_HOME%\platform-toolsを追加するか、platform-toolsにcdで移動して、実行してください。

そのままコマンドプロンプトで下記のコマンドを打ち込みます。
dumpsys window windows | grep -E 'mCurrentFocus|mFocusedApp'
表示された文字列の赤線の部分がappActivityに入れるものとなります。


参考にしたサイトはこちらです。

3.画面の情報取得( ボタンの情報)

3.1 uiautomatorviewerを起動

%ANDROID_HOME%\tools\binにある「uiautomatorviewer.bat」をダブルクリックします。
※uiautomatorviewer.batが起動しない場合は、こちらが参考になるかもしれません。

3.2 uiautomatorviewerで情報を取得

起動したら、左上のボタンを押します。
ちょっと待つと画像が表示されるので、押したいボタンをマウスでクリックします。
右のペインにクリックしたボタンの情報が表示されるので、class名をコピーしておきます。


4.クリックの表現方法を調べる
Appiumのサイトでコマンドのページがあるので、そちらを参考にしました。
「$('#SomeId').click();」


5.javascriptに反映
手順の2~4で調べた情報を元にjavascriptに反映します。
// javascript

const wdio = require("webdriverio");
const assert = require("assert");

const opts = {
  path: '/wd/hub',
  port: 4723,
  capabilities: {
    platformName: "Android",
    platformVersion: "5.1",
    deviceName: "002628105537",
    app: "C:/Users/XXXXX/Desktop/appium_test/ApiDemos-debug.apk",  // ここは環境に合わせてください。
    appPackage: "io.appium.android.apis",
    appActivity: ".view.Buttons1",     					// 手順2で調べたものに書き換える
    automationName: "UiAutomator2"
  }
};

async function main () {
  const client = await wdio.remote(opts);

  const field = await client.$("android.widget.Button");  // 手順3で調べたものに書き換える
  await field.click();									  // 手順4で調べたものに書き換える


  await client.deleteSession();
}

main();
5.結果
これで実行すると、ボタンが押されました。
できた。

実行方法がわからない場合は、前回前々回の記事を参考にしてください。

2020年6月5日金曜日

uiautomatorviewer.bat は jdk 8でないと動かない。

uiautomatorviewer.bat を起動したら、エラーがでて起動しませんでした。
-Djava.ext.dirs=..\lib\x86_64;.......  

ぐぐってみたところ、jdk8でないとだめということでした。
jdk14を入れていたので、こちらをアンインストールして、jdk8に入れなおしました。

直後だとまた別のエラーがでたので、再起動したら、uiautomatorviewerが起動しました。

よかった。

2020年6月3日水曜日

Appium 入門2 ( 実機で Hello World! )


emulator上でHelloworldを表示することに前回やってみました。
今回は実機でHello worldを表示する方法を試してみました。

環境
Windows 10
Android 5.1 (実機)
appium 1.15.1

インストール
インストールの手順は省略します。前回の記事を参考にしてください。

手順

1.Android端末側の準備

開発向けオプション : ON
USBデバッグ : ON
にしました。

2.ADBコマンドで確認

コマンドプロンプトで「adb devices」を実行して、実機がリストされることを確認しました。
adbコマンドが使えない場合は、android sdkのフォルダのplatform-toolsを環境変数のパスに登録するか、そこに移動して、adbコマンドを実行する必要があります。
 

3.スクリプトの準備

前回のスクリプトのplatformversionを実機のAndroidのバージョンに変更
deviceNameを手順2のadb devicesの結果に変更しました。
※devicenameは変更しなくても動いたので、不要なのかもしれません。
// javascript

const wdio = require("webdriverio");
const assert = require("assert");

const opts = {
  path: '/wd/hub',
  port: 4723,
  capabilities: {
    platformName: "Android",
    platformVersion: "5.1",     // ここ
    deviceName: "002628105537", // ここ
    app: "C:/Users/xxxxxxx/Desktop/appium_test/ApiDemos-debug.apk",  
    appPackage: "io.appium.android.apis",
    appActivity: ".view.TextFields",
    automationName: "UiAutomator2"
  }
};

async function main () {
  const client = await wdio.remote(opts);

  const field = await client.$("android.widget.EditText");
  await field.setValue("Hello World!");
  const value = await field.getText();
  assert.equal(value,"Hello World!");

  await client.deleteSession();
}

main();

4.Appiumを起動

スタートメニュー → Appiumをクリック
特に設定等変更せずに、「サーバーVXXXXXを起動する」 ボタンを押す。

5.実行

コマンドプロンプトを開く
index.jsがおいてあるパスにcdコマンドで移動する。
「node index.js」コマンドを実行

6.結果

実機側でHello World!が表示されたら、成功。 できた。