- 投稿日:2020-08-10T23:09:01+09:00
EMUIで最近のアプリ一覧の表示が消えるバグを対処する(非Root環境)
背景
サードパーティランチャー環境下でマルチウインドウで2つのアプリを表示させる
→マルチウィンドウを解除する
→最近のアプリ(バックグラウンドタスク)を表示しようとしてもアプリ一覧が表示されないバグに遭遇
(これ以外にもたまに表示されなくなることがある)どうやら公式のコミュニティにもEMUI10にて似たような報告がある模様(微妙に違うけど)
環境
・HUAWEI P20 lite (ANE-LX2J),非Root
→ EMUI自体のバグみたいなのでこの端末に限らないはず・EMUI 9.1.0.336 (C635E5R1P1)
→ EMUI 9.0.0以降のバージョンで発生するみたい
ただし自分の環境ではEMUI10以降の検証はできてないのであしからず・Nova Launcher 6.2.12
→ サードパーティランチャー全般で発生するのであまり関係ない対処法
XDA-devフォーラムのこことこことかRedditでも色々出てくる
サードパーティランチャーがデフォルトに設定されていることが前提1.PCと端末を接続して,以下のADBコマンドを実行する
adb shell pm uninstall -k --user 0 com.huawei.android.launcherこれにより,デフォルトランチャーを無効にできる&タスク一覧のUIがEMUI8.0.0のものに戻る(縦形のやつから横形のやつになる)
※PCにはADBをインストールして,端末は開発者向けオプションからUSBデバッグをオンにして,adb devicesでオンラインになってることを確認すること.Huwaei端末はここらへんの挙動が特に怪しい
2.(ジェスチャーナビゲーションを使っている場合)Fluid Navigation Gesturesを入れる
Google PlayよりFluid Navigation Gesturesを入れる
(権限とか設定方法とかはアプリに従ってやればいい)デフォルトランチャーを消すと,ジェスチャーナビゲーションでホーム画面&タスク一覧画面が出せなくなるので別のジェスチャーアプリで代用する必要があるため
デフォルトのジェスチャーもラップしてくれるみたいなので推奨『3つのキーによるナビゲーション』の場合そのまま動く
補足
adb shellで消したやつは,
adb shell cmd package install-existing com.huawei.android.launcherで戻すことができる
EMUI10以降ではHuwaeiホームがないと表示されなくなる報告(前述のフォーラム)もあるようなので一応
こっちのやり方でも戻せる考察とか(読まなくていい)
EMUI8.0.0⇒9.0.0更新時に,バックグラウンドタスク一覧のUIが変化&ジェスチャーナビゲーションに対応したが,Huwaeiホーム以外動作が不安定みたいで,マルチウィンドウ起動時にHuwaeiホームが呼び出される仕様
なので今回みたいにでデフォルトランチャーを変えるとバグるっぽいなおデフォルトランチャーでもタスク一覧画面を表示させようとしても反応しなくなるバグがあったので,そもそもちゃんとマルチウィンドウとかジェスチャーナビゲーションに対応してない気がする...
というより日本語の情報がなかった時点で日本市場のHuawei端末って絶滅危惧種なのでは...?
- 投稿日:2020-08-10T22:41:05+09:00
Status barを透明にする方法
直近の案件でStatus Bar部分にもコンテンツを表示したいという要件が合った際に使用したコードを、自分への備忘録も兼ねて記載します。
実装方法
XMLではなくコードから指定します。
// このコードでStatus Barを非表示にし、レイアウトを全画面に表示する window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN // レイアウトの上部にStatus Barの高さの分Paddingを入れることで、Status Barの領域を再確保 findViewById<View>(R.id.root).setPadding(0, getStatusBarHeight(), 0, 0) // Status Barの部分を透明に塗りつぶす window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) window.statusBarColor = Color.TRANSPARENT // Status Barに表示されるアイコンが見えるよう調節する var systemUiVisibility = window.decorView.systemUiVisibilitysystemUiVisibility = systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR window.decorView.systemUiVisibility = systemUiVisibility
- 投稿日:2020-08-10T15:14:47+09:00
Flutter + GitHub Actions + Firebase App Distributionで自動リリースサイクルを作る(Android編)
Flutterアプリ開発を進めるにあたり、masterにマージしたら、誰でもアプリが実機にインストールできるフローを作りたい。
たまにしかやらなくて忘れそうなので備忘。
やりたいこと
- ローカルでもCIでもリリースビルドを作れるようにする
- GitHubに署名ファイルはあげない
- 自動でバージョンをインクリメントして、Firebase App Distributionにuploadする
ローカルでリリースビルド作れるようにする
オフィシャルドキュメントのこの辺にも書いてある。
1. (無ければ)keystoreファイル作る
サンプルはたくさん転がってるので割愛。
git管理からは外しておく。2. ローカルでのビルド用に
key.properties
作るstorePassword=<password from previous step> keyPassword=<password from previous step> keyAlias=key storeFile=<location of the key store file, such as /Users/<user name>/key.jks>これもgit管理からは外しておく。
3. アプリの
build.gradle
に下記を追加。def keystoreProperties = new Properties() def keystorePropertiesFile = rootProject.file('key.properties') if (keystorePropertiesFile.exists()) { // For Local keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) } else { // For CI keystoreProperties.setProperty('storePassword', System.getenv('KEY_STORE_PASSWORD')) keystoreProperties.setProperty('keyPassword', System.getenv('KEY_PASSWORD')) keystoreProperties.setProperty('keyAlias', System.getenv('ALIAS')) keystoreProperties.setProperty('storeFile', System.getenv('KEY_PATH')) }
android
ブロックに下記を追加(&書換え)signingConfigs { release { keyAlias keystoreProperties['keyAlias'] keyPassword keystoreProperties['keyPassword'] storeFile file(keystoreProperties['storeFile']) storePassword keystoreProperties['storePassword'] } } buildTypes { release { signingConfig signingConfigs.release } }4. 確認
とりあえずAPK作ってみる
flutter build apk --release
GitHub Actionsでリリースビルド作れるようにする
参照:Signing Flutter Android apps for release in GitHub Actions
1. (無ければ)workflow作る
サンプルはたくさん転がってるので割愛。
2. GitHub Secretsにもろもろ入れとく
key.properties
の代わり。
この後、これをGitHub Actionsで読み取る。
ANDROID_KEY_STORE_PASSWORD
storePassword
ANDROID_KEY_PASSWORD
keyPassword
ANDROID_KEY_ALIAS
keyAlias
ANDROID_KEY_JKS
keystoreファイルをbase64エンコードしたもの。
openssl base64 -A -in key.jks
使わなかったけど、こんなactionもあったりする。
https://github.com/marketplace/actions/sign-android-release3. workflowにリリースビルドを作るステップを追加
前のステップでGitHub Secretsに保存した値を環境変数に保存。
- name: set up private files run: | echo $SIGNING_KEY | base64 -di > android/app/key.jks env: SIGNING_KEY: ${{ secrets.ANDROID_KEY_JKS }} - name: build release apk run: | flutter build apk --release --build-name=0.0.$GITHUB_RUN_NUMBER --build-number=$GITHUB_RUN_NUMBER env: KEY_STORE_PASSWORD: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }} KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} KEY_PATH: key.jks
- keystoreを復元してるところは、
i
オプションつけないと自分の環境ではこけた。android/app
のbuild.gradle
でビルドするので、android/app/
にファイル置いてる。$GITHUB_RUN_NUMBER
使って、ビルドごとにパッチバージョンとバージョン番号があがるようにするFirebase App Distributionへリリース
既存のGitHub Actionの恩恵にあずかって楽する
(App Distributionのセットアップも忘れずに)
- name: upload artifact to Firebase App Distribution uses: wzieba/Firebase-Distribution-Github-Action@v1 with: appId: ${{secrets.FIREBASE_ANDROID_APP_ID}} token: ${{secrets.FIREBASE_TOKEN}} groups: Developer file: build/app/outputs/flutter-apk/app-release.apkもう一声
- メジャー・マイナーバージョン名は、workflowの外で定義したい
- やっぱり署名も既存のactionを使った方が楽かもしれん
- 投稿日:2020-08-10T14:56:11+09:00
FlutterでAndroidビルドのみ失敗するようになる場合の対処法
現象
Androidでデバッグビルドしてデバッグ実行していたのに、突然ビルドが出来なくなることがある。
エラーは私の場合、firebase_analytics:webの依存関係が解決できない的なメッセージ。FAILURE: Build failed with an exception. * What went wrong: Could not determine the dependencies of task ':firebase_analytics:compileDebugAidl'. > Could not resolve all task dependencies for configuration ':firebase_analytics:debugCompileClasspath'. > Could not resolve project :firebase_analytics_web. Required by: project :firebase_analytics > Unable to find a matching configuration of project :firebase_analytics_web: - None of the consumable configurations have attributes. * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. * Get more help at https://help.gradle.org BUILD FAILED in 25s Finished with error: Gradle task assembleDebug failed with exit code 1環境など
ツールなど バージョンなど MacBook Air Early2015 macOS Mojave 10.14.6 Android Studio 3.6.1 Java 1.8.0_131 Flutter 1.12.13+hotfix.9 Dart 2.7.2 Xcode 11.3.1 対処法
https://github.com/FirebaseExtended/flutterfire/issues/1615
こちらにある、flutter pub cache repair
を実行して解消しました。必ず解決できるとは限らないようですが、今のところ私は現象が発生した2回中2回とも、この方法で解消しました。
- 投稿日:2020-08-10T07:00:11+09:00
Ghidra ARM32 全関数がARMモードかThumbモードかを出力する
ARM32のアプリの解析を行っているとき、正確に逆アセンブルした状態で各関数がARMモードかThumbモードなのかを判定したいという状況はあると思います。
そんな時のための備忘録的メモ。設定
コンソールの出力文字制限を50000->1000000程度にします。
Edit->Tool Options...->Console->Character Limitの値を1000000に変更。
Script
thumb_or_arm.pydef isThumbFunction(func): r = currentProgram.getRegister("TMode") value = currentProgram.programContext.getRegisterValue(r, func.entryPoint) return value.unsignedValueIgnoreMask == 1 func = getFirstFunction() while func is not None: if(isThumbFunction(func)): print("Function: {} @ 0x{}".format(func.getName(), func.getEntryPoint()),"Thumb") func = getFunctionAfter(func) else: print("Function: {} @ 0x{}".format(func.getName(), func.getEntryPoint()),"ARM") func = getFunctionAfter(func)出力
- 投稿日:2020-08-10T03:40:49+09:00
ConstraintLayout入門その5 - 「制約に合致」 (0dp) とは
ConstraintLayoutを使用するための設定については、ConstraintLayout入門その1をご覧ください。
制約に合致 (MATCH_CONSTRAINT)
以下の2つのレイアウトXML記述例では、
<ChildView>
の垂直方向の長さはともに「親Viewと同じ」になります。linear_layout_1.xml<ParentLayout ... > <ChildView ... android:layout_height="match_parent" ... /> </ParentLayout>constraint_layout_1.xml<androidx.constraintlayout.widget.ConstraintLayout ... > <ChildView ... android:layout_height="0dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" ... /> </androidx.constraintlayout.widget.ConstraintLayout>
<ChildView>
の記述は、2つとも「上端と下端が親Viewに一致する」ことを表現していますが、ConstraintLayoutの子Viewではapp:layout_constraintTop_toTopOf
などの値に"parent"
以外に他の子ViewのView IDを指定することができ、他のViewよりも多様な設定を可能にしています。
ConstraintLayoutの子Viewのandroid:layout_width
およびandroid:layout_height
の値に"0dp"
が設定されている場合、それは文字通りの「長さゼロ」の意味にはならず、「制約に合致する」という意味になります。余白を埋める
以下の2つのレイアウトXML記述例では(親Viewの垂直方向の長さが300dp以上ある場合)ともに、親Viewの上部に
<TopView>
, 下部に<BottomView>
, 上下に挟まれる形で<MidView>
が上30dpと下50dpを除いた部分に表示されます。linear_layout_2.xml<LinearLayout ... android:orientation="vertical" ... > <TopView ... android:layout_height="30dp" ... /> <MidView ... android:layout_height="0dp" android:layout_weight="1" ... /> <BottomView ... android:layout_height="50dp" ... /> </LinearLayout>constraint_layout_2.xml<androidx.constraintlayout.widget.ConstraintLayout ... > <TopView android:id="@+id/topView" ... android:layout_height="30dp" app:layout_constraintTop_toTopOf="parent" ... /> <MidView ... android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@id/topView" app:layout_constraintBottom_toTopOf="@id/bottomView" ... /> <BottomView android:id="@+id/bottomView" ... android:layout_height="50dp" app:layout_constraintBottom_toBottomOf="parent" ... /> </androidx.constraintlayout.widget.ConstraintLayout>LinearLayoutにおける
android:layout_weight
は、ConstraintLayout入門その3で紹介したような、子View間の長さの比を決める用途だけでなく、単純に「長さが決まっている子Viewで埋めた後に残った余白を埋める」という目的で単独でもよく用いられてきました。linear_layout_2.xmlではandroid:layout_weight
が設定されている子Viewは1つだけなので、weightの値が"2"
でも"3"
でも表示結果は同じになります。weightに正の数が設定されていれば、その子Viewは「伸びようとする」性質を帯びます(<TopView>
と<BottomView>
のweightにはデフォルト値として0が与えられていて、伸びようとはしません)。
ConstraintLayoutの場合は、<MidView>
にandroid:layout_height="0dp"
を設定して「制約に合致」とし、app:layout_constraintTop_toBottomOf
とapp:layout_constraintBottom_toTopOf
を設定して上下の位置を明示することで「余白を埋め」ます。とはいえ、これだけならRelativeLayoutでも従来から似た記述で同じレイアウトを表現できました。ConstraintLayoutの「制約に合致」はこの他にもいくつかレイアウト表現を提供します。
片端+長さの制約
上端の位置、下端の位置、垂直方向の長さ、の3者の間には、
(上端の座標)+(垂直方向の長さ)=(下端の座標)
という関係が成り立ちます。
水平方向の場合は、
(左端の座標)+(水平方向の長さ)=(右端の座標)
が成り立ちます。
したがって、子Viewの長さが制約で決まれば、子Viewの位置の制約は片端だけあれば足ります。
以下の2つのレイアウトXML記述例はともに、垂直方向に1:2:3の比で3つの子Viewを縦に並べています。linear_layout_3.xml<LinearLayout ... android:orientation="vertical" ... > <TopView ... android:layout_height="0dp" android:layout_weight="1" /> <MidView ... android:layout_height="0dp" android:layout_weight="2" /> <BottomView ... android:layout_height="0dp" android:layout_weight="3" /> </LinearLayout>constraint_layout_3.xml<androidx.constraintlayout.widget.ConstraintLayout ... > <TopView android:id="@+id/view1" ... android:layout_height="0dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintHeight_default="percent" app:layout_constraintHeight_percent="0.167" ... /> <MidView android:id="@+id/view2" ... android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@id/view1" app:layout_constraintHeight_default="percent" app:layout_constraintHeight_percent="0.333" ... /> <BottomView ... android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@id/view2" app:layout_constraintHeight_default="percent" app:layout_constraintHeight_percent="0.5" ... /> </androidx.constraintlayout.widget.ConstraintLayout>constraint_layout_3.xmlでは、
<TopView>
,<MidView>
の垂直方向の位置制約は上端のみ、<BottomView>
の垂直方向の位置制約は下端のみです。しかし、いずれもapp:layout_constraintHeight_percent
の指定によって垂直方向の長さが決まっているので、もう一方の端の位置も決まってレイアウトのすべての情報が決まります。このような使い方は、RelativeLayoutではサポートされていませんでした。ConstraintLayout入門その3で紹介した通り、percentは親View(すなわちConstraintLayout)に対する比を指定します。値は0〜1の小数です。
両端+長さの制約
上記の通り、子Viewの両端の位置と長さの間には等式の関係が成り立つので、両端の位置と長さの3つの制約のうち2つが与えられていればもう1つの制約が自動的に決まり、したがって3つの制約を与える必要はなく2つで足ります。
では、2つの制約で足りるところへあえて3つの制約が与えられ、かつ、それらが「(上端の座標)+(垂直方向の長さ)=(下端の座標)」という関係を満たしていなかったらどうなるでしょうか?
このようなとき、ConstraintLayoutでは3つの制約がバランスするようにレイアウトの微調整が行われます(相矛盾するパラメータのうちいずれかを無視する、といった消極的な調整ではなく、3つの制約のすべてが意味をもつような積極的な調整が行われます)。この性質が、他のレイアウトクラスには見られないConstraintLayoutならではの主要な特徴です。linear_layout_4.xml<LinearLayout ... android:orientation="vertical" ... > <TopView ... android:layout_height="0dp" android:layout_weight="10" ... /> <Space ... android:layout_height="0dp" android:layout_weight="16" /> <MidView ... android:layout_height="0dp" android:layout_weight="20" ... /> <Space ... android:layout_height="0dp" android:layout_weight="24" /> <BottomView ... android:layout_height="0dp" android:layout_weight="30" ... /> </LinearLayout>constraint_layout_4.xml<androidx.constraintlayout.widget.ConstraintLayout ... > <TextView android:id="@+id/view421" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintHeight_default="percent" app:layout_constraintHeight_percent="0.1" ... /> <MidView android:id="@+id/view422" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@id/view421" app:layout_constraintBottom_toTopOf="@id/view423" app:layout_constraintHeight_default="percent" app:layout_constraintHeight_percent="0.2" app:layout_constraintVertical_bias="0.4" ... /> <BottomView android:id="@+id/view423" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHeight_default="percent" app:layout_constraintHeight_percent="0.3" ... /> </androidx.constraintlayout.widget.ConstraintLayout>具体的には、制約が3つだったとき、子Viewの長さを制約の長さに合致させつつ、子Viewの位置が両端の中間に位置するように調整されます。
このとき、app:layout_constraintVertical_bias
の値を設定することで、垂直方向の「中間」の位置を比率で設定することができます。
上のConstraintLayout内の<MidView>
には3つの制約とbiasが与えられており、この調整によって表示が決定されます。
app:layout_constraintVertical_bias
が0のときは上端の制約に張りつきます(下端の制約を省略したのと同じ結果になります)。app:layout_constraintVertical_bias
が1のときは下端の制約に張りつきます(上端の制約を省略したのと同じ結果になります)。水平方向の場合は
app:layout_constraintHorizontal_bias
で同様の設定ができます。
app:layout_constraintHorizontal_bias
が0のときは 'Left' もしくは 'Start' に設定した制約に張りつき、1のときは 'Right' もしくは 'End' に設定した制約に張りつきます。
'Start' 〜 'End' で制約を設定したとき、right to leftレイアウトか否かでapp:layout_constraintHorizontal_bias
の偏りの方向が反転します。
app:layout_constraintVertical_bias
,app:layout_constraintHorizontal_bias
のどちらも、0と1の間の値を設定することで中間の位置取りを行えます。biasの値を省略したときのデフォルト値は0.5、すなわち子Viewの位置は両端の真ん中になります。
上記の2つのレイアウトXML記述例では、ConstraintLayoutがbiasの設定で<MidView>
の位置を調整しているのに対し、LinearLayoutでは<Space>
を挟んで空白を作り出して調整しています。サンプルコード
今回のサンプルコードは以下のリポジトリにあります。
https://github.com/csayamada/ConstraintLayout5画面例
LinearLayoutとConstraintLayoutの結果を並べた表示例は下のようになります。
最下段(biasを使って表現した部分)をよく見ると、LinearLayoutの表示とConstraintLayoutの表示との間にわずかなズレがありますが、これはLinearLayoutとConstraintLayoutの実装の違いによる誤差のよう(Android OSのバージョンによって変化するかもしれません)で、計算原理は両者同じのようです。
- 投稿日:2020-08-10T00:09:12+09:00
ImageViewにDataBindingをする方法
はじめに
今回はImageViewにDataBindingをする方法を解説していきます。
やりたかったこと
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@{iconId}"/>こんな感じでImageViewにリソースIDをDataBindingをしたかったのですが、なぜか上手くいきませんでした。
改善
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" app:imageResource="@{iconId}"/>これでできました!
なんでできないんだろうって考えていたんですが、公式のBindingAdapterを見て、そりゃできないなって思いました。
ちなみにこちらが既に用意されているBindingAdapterです。Java@BindingAdapter("android:src") public static void setImageUri(ImageView view, String imageUri) { if (imageUri == null) { view.setImageURI(null); } else { view.setImageURI(Uri.parse(imageUri)); | } } @BindingAdapter("android:src") public static void setImageUri(ImageView view, Uri imageUri) { view.setImageURI(imageUri); } @BindingAdapter("android:src") public static void setImageDrawable(ImageView view, Drawable drawable) { | view.setImageDrawable(drawable); }setImageResourceをしてくれるBindingAdapterはなかったんですね(笑)
そりゃできないわけだ、、、ちなみに自作でBindingAdapterを作っても良さそうですね
作るとしたらこんな感じですkotlin@BindingAdapter("android:src") fun ImageView.setImageResource(resourceId: Int) { setImageResource(resourceId) }というかResourceIdをセットすることを最初から想定しておいてほしいです、Googleさん、、、、
最後に
ImageViewがバグって表示されていて、なんでだろうなぁと小1時間くらい消費してしまいました。
想定通りの挙動をしない場合は、裏側で何をやっているかちゃんと確認した方がいいということを学べたのでよかったです(笑)