古いアプリをAndroid10に対応する必要があるのですが、ついでにAdmobも最新のルールにそって実装しておこうかなと思ったのですが、ちょっと躓いてしまいました。
GDPR(一般データ保護規則)です。
EU圏向けに対応する必要があるらしく、結構面倒そう。
ひとまず勉強中です。
古いアプリをAndroid10に対応する必要があるのですが、ついでにAdmobも最新のルールにそって実装しておこうかなと思ったのですが、ちょっと躓いてしまいました。
GDPR(一般データ保護規則)です。
EU圏向けに対応する必要があるらしく、結構面倒そう。
ひとまず勉強中です。
//import android.support.v7.app.ActionBarActivity; import androidx.appcompat.app.AppCompatActivity; ~途中省略~ //public class MainActivity extends ActionBarActivity implements SensorEventListener { public class MainActivity extends AppCompatActivity implements SensorEventListener {
SystemHealthManager
によって電池使用量の統計情報がリセットされます。主要な充電イベントとはつまり、デバイスが完全に充電されること、またはほとんど電池残量がない状態からほぼ完全に充電された状態になることです。package test.test; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // ボタンのイベント findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // サービスを呼ぶ } }); } }
package test.test; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.os.Build; import android.os.IBinder; import androidx.core.app.NotificationCompat; //(1) Serviceの派生クラスを作成 public class MyService extends Service { //(2) 必須のメソッド。 バインドしない場合はnullを返す。 @Override public IBinder onBind(Intent intent){ return null; } //(3.3)の一部 startforegroudnに渡すIDを定義する。 0以外の数字 。 private static final int ONGOING_NOTIFICATION_ID = 1; //(3) 呼び出し側でstartService()かstartForegroundService() をすると呼び出されます。 @Override public int onStartCommand(Intent intent, int flags, int startId) { //(3.1) チャンネルIDの登録 String channelID = "MY_CHANNEL_ID"; // 通知チャンネル用のID。適当な名前を定義 createNotificationChannel(channelID); // 通知をタップしたときにアプリを呼び出す準備 Intent notificationIntent = new Intent(this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); // 通知の準備 NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelID) .setContentTitle("通知のタイトル") .setContentText("通知の内容") .setSmallIcon(R.drawable.ic_service_notification) .setContentIntent(pendingIntent) .setPriority(NotificationCompat.PRIORITY_DEFAULT); // (3.3) このAPIを呼ぶと、フォアグラウンドサービスとなる。 startForeground(ONGOING_NOTIFICATION_ID, builder.build()); // (3.4) 戻り値を指定。 return START_STICKY; } // 通知のチャンネルを作成する。この関数はほぼもってきたそのまま。 // https://developer.android.com/training/notify-user/build-notification?hl=ja private void createNotificationChannel(String channelID) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //アプリ - 通知 に表示される情報 CharSequence name = "サービス起動中の通知"; // なんでもOK。 String description = "サービス起動中の通知は、XXXのための通知です。"; // なんでもOK。 // 通知のレベルとか名称とか設定 int importance = NotificationManager.IMPORTANCE_DEFAULT; NotificationChannel channel = new NotificationChannel(channelID, name, importance); channel.setDescription(description); // 通知の登録 NotificationManager notificationManager = getSystemService(NotificationManager.class); notificationManager.createNotificationChannel(channel); } } }
package test.test; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // ボタンのイベント findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // サービスを呼ぶ Intent serviceIntent = new Intent(MainActivity.this, MyService.class); startService(serviceIntent); } }); } }
「PowerConnectionReceiver」はイベントをキャッチするクラスの名前です。<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/> <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/> <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
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で動作することが確認できました。
BroadcastReceiver br = new PowerConnectionReceiver(); IntentFilter filter = new IntentFilter(Intent.ACTION_POWER_CONNECTED); filter.addAction(Intent.ACTION_POWER_DISCONNECTED); this.registerReceiver(br, filter);
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のサイトの用語でいうと「コンテキスト登録されたレシーバー」というものになります。
3-8. versionの数字が赤い場合は、下図の更新ボタンを押すか、intelljを再起動すると 読み込まれるようです。(この辺はちょっと怪しいです。)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
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 AndroidDriverdriver; 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(); } }
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」に入れる情報を取得します。
dumpsys window windows | grep -E 'mCurrentFocus|mFocusedApp'表示された文字列の赤線の部分がappActivityに入れるものとなります。
// 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();
// 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();
// javascript const wdio = require("webdriverio"); const assert = require("assert"); const opts = { path: '/wd/hub', port: 4723, capabilities: { platformName: "Android", platformVersion: "8", deviceName: "Android Emulator", app: "C:/XXXXXXXXXXXX/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();※内容はまだ理解していないです。
using (System.IO.Stream gfs = m_typeface.GetFontStream()) { XXXX }これでよいという確実な根拠はないのですが、使ったものは解放したほうが良いのだろうと思い、usingを使ってます。
System.IO.Stream gfs = m_typeface.GetFontStream(); gfs.Close(); gfs.Dispose(); gfs = m_typeface.GetFontStream();
string sfontfile = フォントファイルのパス FileStream fs = new FileStream(sfontfile, FileMode.Open, FileAccess.Read);
System.IO.Stream gfs = mytypeface.GetFontStream(); MemoryStream ms = new MemoryStream(); gfs.CopyTo(ms);memorystreamにコピーしているのは、使い方がこれしか分からなかったためとなります。