- 投稿日:2020-11-18T22:49:57+09:00
Flutter実装時にお世話になったページ 2020/11/18
なにか
毎回、同じ問題に、同じ検索して、同じページで解決してるので。。。
ContainerのBottomにだけ、枠線を入れたかった
Stack Overflow "How to add a border to a widget in Flutter?"
url_launcherでカスタムURLスキーマを使用したアプリ起動ができなかった(Android)
AndroidManifestまで意識が行かなかった。。。
Stack Overflow "[url_launcher] When targeting API 30, canLaunch always retrurns false"
flutter upgradeしたら、ビルド通らなくなった。。。
こんなエラーが出るようになりました。
Flutter Row() 'crossAxisAlignment != CrossAxisAlignment.baseline || textBaseline != null': is not true. Failed assertion:flutter upgradeしたら、Build iOS 失敗するようになった。。。
こんなエラーが出るようになりました。
ld: symbol(s) not found for architecture armv7 clang: error: linker command failed with exit code 1 (use -v to see invocation) note: Using new build system note: Planning build note: Constructing build descriptionStack Overflow "Undefined symbols for architecture armv7 - Failed to build iOS app"
これは、このコマンドで行けるようになったんだけど、Firebaseとかの設定してると色々面倒なので、根本的な原因を確認中、どうやらビルドのターゲットバージョンが問題っぽい。
なんか
Stack Overflowだらけでした。それにしてもStack Overflowのコピーサイトは検索結果を汚染しすぎ。
- 投稿日:2020-11-18T17:30:26+09:00
HMS Location Kit Android SDKの導入方法について
1. Location Kitの概要
Location KitはHMSの位置情報関連のサービスを提供するSDKです。
今回はLocation Kitの機能と使用方法について説明したいと思います。1.1 機能
Location Kitは以下の3つの機能を提供しています。
1. ハイブリッド測位による位置情報の取得
2. ジオフェンス機能
3. 運動状態の識別機能それぞれの機能の概要と使用シナリオについては以下で紹介します。
位置情報の取得
Location KitのAPIを使用することで、ハイブリッド測位(GNSS, WiFi, 基地局)による位置情報の取得ができます。
また、東京、大阪、名古屋などの主要都市でSuperGPS機能がサポートされ、位置情報を取得する際には、ロードサイドが分かるように精度よく位置情報を取得できます。利用シナリオ
・現在地の取得
・タクシー予約アプリの場合、道路のどの側にいるのかが分かるように精度よく現在地を取得したい場合ジオフェンス機能
機能
自分の決めた条件でジオフェンスを作成し、フェンスを出入りする際の処理を自分で定義できます。利用シナリオ:
店舗付近にジオフェンスを作成し、ユーザーがジオフェンスの範囲内に入ったらPush通知を送信して店舗の情報やプロモーション情報を知らせることができます。運動状態の識別
ユーザーの運動状態を識別して、それぞれの状態の場合の操作を定義できます。
利用シナリオ:
フィットネストラッキングアプリの場合、ユーザーの運動状態を自動で識別して記録を開始できます。機能に関してさらに詳しく知りたい方はコチラから確認できます。
1.2. HMS Location Kitの特徴
- 位置情報の請求の成功率が高い
- SuperGPSによる高精度の位置情報の取得
- 低いエネルギー消耗
2. 導入方法
全体の流れは以下のようになります。
- 開発者アカウントの登録
- 開発者コンソール(AppGallery Connect)でプロジェクトを作成
- コンソールでプロジェクトの詳細を設定
- Androidプロジェクトに必要なファイルの導入、依頼関係の追加
- コーディング
次に各ステップについて詳細に説明します。
3. 詳細ステップ
3.1 開発者アカウントの登録
HMSのSDKを使用するには、Huawei開発者アカウントを取得する必要があります。
アカウントの申請には主に以下の2つのステップがあります。
- Huawei IDの取得(すてにHuawei IDをもっている人はこのステップが不要となります)
- 開発者認証
詳しいガイドは以下のサイト(英語)を参照してください。
Huawei ID申請ガイド:https://developer.huawei.com/consumer/en/doc/10104
開発者認証(個人の場合):https://developer.huawei.com/consumer/en/doc/101223.2 Appgallery Connectにてアプリプロジェクトの作成とプロジェクトの設定
3.2.1 プロジェクトの作成
AppGallery Connect(リンク)はHuaweiの開発者サイトです。
開発者サイトにログインしたら、最初は以下のページが表示されます。
このページから、AppGallery Connectをクリックしてコンソールに入ります。
クリックしたら以下のページが表示されます。
次に、My appsをクリックしてアプリ管理画面に入ります。
My appsページの右上の「New app」をクリックして新しいプロジェクトを作成します。
「New apps」をクリックしたら次に下のページが表示されます。
このページでは、アプリの種類(APK:Androidアプリの場合、RPK:QuickAppの場合)、アプリ名やデフォルト言語を設定できます。
また、ここでは「Add to project」にチェックを入れる必要があります。入力が完了したら、「OK」をクリックして作成が完了となります。
「OK」をクリックしたらアプリの詳細ページが表示されます。次に、プロジェクトのAPI設定やFingerprint設定をする必要がありますので、左上のMy appsより、My projectsを選択してプロジェクトの詳細設定ページに入ります。
3.2.2 プロジェクトの設定
プロジェクト詳細ページでは、以下のようにアプリの詳細情報が表示されています。
まずは、Location APIを有効にする必要がありますので、Manage APIsをクリックします。
Manage APIsページで使用したいAPIを有効にします。今回はLocation Kitを有効にしました。
次に、fingerprintを設定し、コンフィグファイル(agconnect-services.json)をダウンロードします。
以上の操作で、Location Kitを使用する準備が完了となります。
詳しいガイドは以下のサイト(英語)を参照してください。
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides-V5/config-agc-0000001057629153-V53.3 Android Studioでの前準備(SDK導入)
3.3.1 コンフィグファイルの導入
先ほどダウンロードしたagconnect-services.jsonファイルをappフォルダに入れます。
このファイルは、アプリの認証情報が記載されたコンフィグファイルとなります。このファイルがないと、HMS SDKが機能しなくなる可能性が高いですのでかならずプロジェクトに導入してください
3.3.2 gradleファイルに依頼関係を追加
次に、gradleファイルに依頼関係を追加します。
まずはProjectのgradleに、以下のコメントの下の行を追加します。build.gradle(Project)buildscript { repositories { google() jcenter() //HMS Core SDKのMavenを追加します maven {url 'https://developer.huawei.com/repo/'} } dependencies { classpath "com.android.tools.build:gradle:4.1.0" //agcpのclasspathを追加します classpath 'com.huawei.agconnect:agcp:1.4.1.300' } } allprojects { repositories { google() jcenter() //HMS Core SDKのMavenを追加します maven {url 'https://developer.huawei.com/repo/'} } }追加したら一度Syncし、次にappのgradleに以下のように依頼関係を追加します。
build.gradle(app)plugins { id 'com.android.application' //'com.android.application'の後に以下の行を追加します id 'com.huawei.agconnect' } android { compileSdkVersion 30 buildToolsVersion "30.0.2" //signingConfigsを追加します signingConfigs { config { storeFile file("Your_KeyStore") storePassword "Your_Password" keyAlias "Your_KeyAlias" keyPassword "Your_Password" v2SigningEnabled true } } defaultConfig { applicationId "com.example.locationdemoqiita" //HMS Location Kitを使う場合の制限に関しては以下のリンクより確認できます。 //https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides-V5/emui-version-dependent-0000001050042515-V5 minSdkVersion 21 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' //signingConfigsを追加します signingConfig signingConfigs.config } debug{ //signingConfigsを追加します signingConfig signingConfigs.config } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { ... //HMS Location SDKを追加します。最新のバージョンは以下のリンクから確認できます。 //https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides-V5/version-change-history-0000001050986155-V5 implementation 'com.huawei.hms:location:5.0.4.300' }完了したらSyncをクリックしてSDKの導入が完了となります。
詳しいガイドは以下のサイト(英語)を参照してください。
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides-V5/integrate-as-sdk-0000001050168936-V53.4 コーディング
3.4.1 Permissionの追加
Location Kitを使用するにはManifestに以下のPermissionを追加する必要があります。
AndroidManifest.xml<!-- 位置情報関連のPermissionを追加します--> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <!-- Android Q以降、バックグランドで位置情報を継続的に取得したい場合は以下のPermissionを追加する必要があります --> <!--<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />-->3.4.2 Location Kitによって現在地を取得
以下では、Location Kitを使って現在地を取得する場合の例を示します。
Location Kitはコールバックによって位置情報を取得するようになっています。
具体的には以下のコードを参考してください。MainActivity.javapublic class MainActivity extends AppCompatActivity { private static final String TAG = "LocationDemoQiita"; private static final int PERMISSION_CODE = 0; private static final int LOCATION_SETTING_CHECK_CODE = 1; private LocationCallback mLocationCallback; private FusedLocationProviderClient mClient; private LocationRequest mLocationRequest; private SettingsClient mSettingsClient; private Location mLastLocation; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initiateLocationServices(); checkAndRequestLocationPermissions(); } /** * Location関連のクラスをインスタンス化します */ private void initiateLocationServices(){ mClient = LocationServices.getFusedLocationProviderClient(this); mSettingsClient = LocationServices.getSettingsClient(this); mLocationRequest = new LocationRequest(); //Locationを取得できた場合、このcallbackが実行されます if(mLocationCallback == null){ mLocationCallback = new LocationCallback(){ @Override public void onLocationResult(LocationResult locationResult) { if(locationResult!= null){ //得られた位置情報を処理します mLastLocation = locationResult.getLastLocation(); Log.d(TAG, "My Last Location is" + " ( " +mLastLocation.getLatitude() + ", " + mLastLocation.getLongitude()+ " )"); } } }; } } private void checkAndRequestLocationPermissions(){ if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { String[] strings = {android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_COARSE_LOCATION}; ActivityCompat.requestPermissions(this, strings, PERMISSION_CODE); } else { //Permissionがすでに許可された場合はCheckLocationSetting()を実行します checkLocationSetting(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if(requestCode == PERMISSION_CODE && grantResults.length > 0){ for(int i = 0;i < grantResults.length ;i ++){ if(grantResults[i] == PackageManager.PERMISSION_DENIED){ Toast.makeText(this, permissions[i] + " is denied", Toast.LENGTH_SHORT).show(); return; } } checkLocationSetting(); } } /** * デバイスの位置情報が有効になっているかどうかなどの設定を確認し、成功した場合はLocationのUpdateを請求します */ private void checkLocationSetting(){ LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder(); builder.addLocationRequest(mLocationRequest); mSettingsClient.checkLocationSettings(builder.build()) .addOnSuccessListener(new OnSuccessListener<LocationSettingsResponse>() { @Override public void onSuccess(LocationSettingsResponse locationSettingsResponse) { //設定に問題がない場合はLocationのUpdateを請求します requestLocationUpdate(); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { int statusCode = ((ApiException) e).getStatusCode(); if(statusCode == LocationSettingsStatusCodes.RESOLUTION_REQUIRED){ //この場合の失敗の理由はHMS Coreの位置情報を有効にしていないことですので、 //ポップアップでユーザーにHMS Coreの位置情報の有効化を請求します。実際のポップアップは下の図を参考してください。 try { ResolvableApiException rae = (ResolvableApiException) e; //ポップアップを表示します rae.startResolutionForResult(MainActivity.this, LOCATION_SETTING_CHECK_CODE); } catch (IntentSender.SendIntentException sie) { sie.printStackTrace(); } } } }); } /** *checkLocationSettingで表示したポップアップに対するユーザーの選択について処理します */ @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode){ case LOCATION_SETTING_CHECK_CODE: switch (resultCode){ case RESULT_OK: //「有効化」をクリックした場合、LocationのUpdateを請求します requestLocationUpdate(); break; case RESULT_CANCELED: //「キャンセル」を選択した場合 Toast.makeText(this, "位置情報を取得するにはHMS Coreの位置情報権限をオンにする必要があります", Toast.LENGTH_SHORT).show(); break; } } } /** * 位置情報を取得します。位置情報を正しく取得した場合、mLocationCallbackのonLocationResultが実行されます */ private void requestLocationUpdate(){ mClient.requestLocationUpdates(mLocationRequest,mLocationCallback, Looper.getMainLooper()) .addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { Toast.makeText(MainActivity.this, "Location Update Request Succeed", Toast.LENGTH_SHORT).show(); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { Toast.makeText(MainActivity.this, "Location Update Request Succeed Failed", Toast.LENGTH_SHORT).show(); } }); } }ユーザーが端末の位置情報機能をオフにした場合、checkLocationSetting()を使うことで下のようなポップアップが表示されます。
開発に関するさらに詳しいガイドは以下のリンクより確認できます。
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides-V5/location-develop-steps-0000001050746143-V54. トラブルシューティング
Location Kitが正しく動作しない場合には、以下の項目を確認したらいいと思います。
- Location Kitが有効になっているかどうか(コンソール→MyProjects)
- fingerprintは正しくコンソールに登録できているかどうか
- agconnect-services.jsonファイルはプロジェクトに導入しているかどうか
- Permissionは追加したかどうか
5. その他リソース
APIリファレンス:https://developer.huawei.com/consumer/en/doc/development/HMSCore-References-V5/overview-0000001051066102-V5
サンプルコード:https://developer.huawei.com/consumer/en/doc/development/HMSCore-Examples-V5/sample-code-0000001050986087-V5
- 投稿日:2020-11-18T10:55:38+09:00
AutoCompleteTextViewにフォーカスを当てたままキーボードを非表示にする
環境
Android Studio 4.1.1
Build #AI-201.8743.12.41.6953283, built on November 5, 2020
Runtime version: 1.8.0_242-release-1644-b01 amd64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
Windows 10 10.0
GC: ParNew, ConcurrentMarkSweep
Memory: 4029M
Cores: 8
Registry: ide.new.welcome.screen.force=true, external.system.auto.import.disabled=true
Non-Bundled Plugins: org.jetbrains.kotlin, com.developerphil.adbideaAutoCompleteTextViewとは
キーボードによるインプットに対して、
自動で文字を補完してくれる機能を備えもったEditTextのことです。通常、Googleのマテリアルデザインガイドラインに則った
ドロップダウンメニューを作成する場合は以下のような実装をします。res/layout/dropdown_menu.xml
<com.google.android.material.textfield.TextInputLayout android:id="@+id/lyt_dropdown" style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="項目名1"> <androidx.appcompat.widget.AppCompatAutoCompleteTextView android:id="@+id/dropdown" android:layout_width="match_parent" android:layout_height="wrap_content" /> </com.google.android.material.textfield.TextInputLayout>res/layout/item_dropdown_menu.xml
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="16dp" android:ellipsize="end" android:maxLines="1" android:textAppearance="?attr/textAppearanceSubtitle1"/>ちなみに
android:ellipsize="end"
を
android:ellipsize="start"
にするとテキストを全文表示できます。final AutoCompleteTextView view = root.findViewById(R.id.dropdown); ArrayAdapter<String> adapter = new ArrayAdapter<>( view.getContext(), R.layout.item_dropdown_menu, new String[]{ "選択値1", "選択値2", "選択値3", "選択値4", "選択値5", }); view.setAdapter(adapter);しかし、このままではテキストボックスを選択した時に、
ドロップダウンメニューが表示されているのにキーボードが表示され、
AutoCompleteTextView
はEditText
でもあるのでカーソルも表示されてしまいます。あくまでmaterial.ioはデザインのガイドラインなので、
デザインのことしか記載されていません。ここからhintの色変更等のリッチな動きはキープしつつ、
カーソルやキーボードが表示されない以下のようなイメージにしたいですね。カーソルの非表示
AutoCompleteTextViewのレイアウトファイルに
android:cursorVisible="false"
を追加してあげます。キーボードの非表示
フォーカスを当ててた際のリッチな動きはそのままに、
キーボードを非表示にする方法は以下のコードで実現できます。
AutoCompleteTextView
のandroid:focusable="true"
が大事です。
- レイアウトファイル
<com.google.android.material.textfield.TextInputLayout android:id="@+id/lyt_dropdown" style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:hint="項目名1"> <androidx.appcompat.widget.AppCompatAutoCompleteTextView android:id="@+id/dropdown" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" android:cursorVisible="false" android:focusable="true" android:maxLines="1" android:text="選択肢1" android:textSize="18sp" tools:ignore="LabelFor" /> </com.google.android.material.textfield.TextInputLayout>
- javaの場合
AutoCompleteTextView view = findViewById(R.id.dropdown); view.setShowSoftInputOnFocus(false);
- kotlinの場合
dropdown.setShowSoftInputOnFocus = false余談
ちなみに
setShowSoftInputOnFocus
メソッドの呼び出し元を見てみると、
TextView
クラスなのでこの方法はEditText
クラスでも使えます。
- 投稿日:2020-11-18T02:18:40+09:00
Github Actionsを使って自動テストをした話
はじめに
今回はGithub Actionsを使って自動テストをした話を紹介していきたいと思います。
自動テストのタイミング
PR出したタイミングとかPR出したブランチにpushしたタイミングでいいのでは?
と思う方もいるかもしれません。Github Actionsはpublicなレポジトリだと無料ですが、Organizationで使うと従量課金制になっています。
テストの量が多い場合、push毎にCIを走らせてしまうととんでもない実行時間になってしまいます。またPR出したタイミングで修正があることに気付き新しくpushした場合、その部分はグリーンになっている保証はありません。
なので今回はレビュワーがapproveしたタイミング(LGTMしたタイミング)で自動テストを回すようにしました。
PR出す→修正がある場合は新たにpush→レビュワーがapprove→テスト→成功でも失敗でもslackに通知
レビュー時にテストをする方法
自動テストのやり方などはググればたくさん出てくるんですが、approveされたらという条件が合致するものがなかったので苦労しました。
まずGithub Actionsのトリガーを見てみると、pull_request_reviewというのが出てきます。
これがレビューされた時のトリガーです。今回筆者はアクティビティタイプをsubmittedだけにしました。
レビュワーがapproveしてくれたけど、コメントを編集した場合や、取り消しした場合に毎回走るのは嫌だったので追加したタイミングだけCIが走るようにしました。次にプルリクエストをレビューした時のWebHookを見ていきます。
ここのpayloadを見ていくと、reviewの中にstateというのがあるのがわかります。
ここには
- commented
- approved
- change_requested(これ合ってるかわからない)
の3つの要素が入ってきます。
なのでこれがapprovedの時だけCIを走らせればいい感じですね。
一旦全体を見ていきましょう
name: UnitTest on: pull_request_review: types: [ submitted ] branches: [ master ] jobs: test: if: github.event.review.state == 'approved' runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: set up JDK 1.8 uses: actions/setup-java@v1 with: java-version: 1.8 - name: UnitTest run: ./gradlew testDebugUnitTest細かい解説
トリガー
on: pull_request_review: types: [ submitted ] branches: [ develop ]まずはトリガーから
レビューをした時に走らせたいのでpull_request_review
アクティビティタイプはsubmitted
ブランチは適当にmaster指定にしてますが、任意で設定してください条件
jobs: test: if: github.event.review.state == 'approved' runs-on: ubuntu-latest次にテストを走らせる条件
jobsをスタートしてジョブ名はtest(ここも任意で)
ifに条件を書いていきます。
github.eventでそのトリガーに合ったWebHookのペイロードを取得できます。
今回は上記でも説明したようにreviewの中のstateを使いたかったのでgithub.event.review.stateちなみに記法として2パターンあって
if: github.event.review.state == 'approve'でも
if: ${{ github.event.event.review.state == 'approve' }}でもOK
確かシングルクオーテーションじゃなきゃダメだったので気をつけてくださいあとはいつも通り
steps: - uses: actions/checkout@v2 - name: set up JDK 1.8 uses: actions/setup-java@v1 with: java-version: 1.8 - name: UnitTest run: ./gradlew testDebugUnitTestAndroidのビルド環境を作ってテストするだけ
テストコマンドは
./gradlew test{buildVariantName}UnitTest
です。
まとめ
以上がレビューでapproveもらった時だけ自動でテストを走らせる方法です。
次はテスト結果をslackに通知する方法でいいかな?