【AndroidTV／Java】スマホ→TV化＋Guided

## やりたいこと
スマホ向けアプリをAndroidTV向けに移行したい
AndroidTVでAlertDialogがつかえないので、それに代わるもので代用したい
（Android11環境で確認）

## Step1．スマホ→AndroidTVへの移行
AndroidTV向けの[お作法](https://developer.android.com/training/tv/start/start)が色々と記載されている
要点をピックアップすると以下の変更が必要っぽい

#### build.gradle(app)
~~~gradle
dependencies {
    implementation 'androidx.leanback:leanback:1.0.0'
}
~~~
#### AndroidManifest.xml
~~~xml
    <!-- *** AndroidTV用設定 *** -->
    <!-- タッチスクリーン不要 -->
    <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
    <!-- AndroidTV以外の拒否(AndroidOS搭載のスマホ) -->
    <uses-feature android:name="android.software.leanback" android:required="false" />

    <application
        android:banner="@drawable/xxxx[320x180pxのPNG画像 アイコンになる]"
        >
        <activity
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
~~~
#### res/value/themes.xml
~~~xml
<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- <style name="Theme.GuideSample" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> -->
    <style name="Theme.GuideSample" parent="@style/Theme.Leanback"/>
</resources>
~~~
#### MainActivity.java
~~~java
//public class MainActivity extends AppCompatActivity {
public class MainActivity extends Activity {
}
~~~

### 解説
1. build.gradle：dependenciesに定義を追記。Leanback対応
1. AndroidManifest.xml：intent-filterをLAUNCHER(スマホ向け)→LEANBACK_LAUNCHER(TV向け)に変更。AndroidTVの標準的な権限を付与
1. themes.xml：Theme.Leanbackに変更
`ポイント：AndroidTVにはアクションバーがない（DialogFragmentなど一部サポートしない Step2にて記載）`
1. MainActivity.java：前述のため、継承クラスをActivityに変更

## Step2．alertDialog→GuidedStepSupportFragmentへの移行
AlertDialogでダイアログを表示すると以下のエラーが発生する
~~~java
java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
~~~
このエラーの理由は、[開発ガイド](https://developer.android.com/training/tv/start/layouts?hl=ja#leanback-theme)に明記されている
`ポイント：Android TV アプリにはアクションバーがないため、Leanback テーマにはアクションバーが含まれない`

上記のため、AlertDialogからAndroidTVの標準的な（？）GuidedStepSupportFragmentへ切り替える

#### res/layout/guide_tv_frame.xml(新規追加)
~~~xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/guide_tv_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
~~~
#### GuideTvFragmentListener.java(新規追加)
~~~java
package jp.co.miyaken3381.guideSample;

public interface GuideTvFragmentListener {
    public void onClickButton(long action);
}
~~~
#### GuideTvFragment.java(新規追加)
~~~java
public class GuideTvFragment extends GuidedStepSupportFragment {
    public final long ACTION_CASE1 = 1000;
    private GuideTvFragmentListener mListener;
    private String mTitle;
    private String mMessage;

    public GuideTvFragment(String title, String message) {
        mTitle = title;
        mMessage = message;
    }

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        // contextクラスがMyListenerを実装しているかをチェック
        if (context instanceof GuideTvFragmentListener) {
            mListener = (GuideTvFragmentListener) context;
        }
    }

    @Override
    public void onDetach() {
        mListener = null;   // Fragmentを離れた後の再実行防止
        super.onDetach();
    }

    @Override
    public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
        String breadcrumb = "Guideサンプル";
        String title = mTitle;
        String description = mMessage;
        Drawable icon = getActivity().getDrawable(R.drawable.ic_info);
        return new GuidanceStylist.Guidance(title, description, breadcrumb, icon);
    }

    @Override
    public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
        super.onCreateActions(actions, savedInstanceState);
        actions.add(new GuidedAction.Builder()
                .id(ACTION_CASE1)
                .title("ボタン１")
                .description("ボタン説明１")
                .hasNext(false)
                .build());
   }

    @Override
    public void onGuidedActionClicked(GuidedAction action) {
        if (mListener != null) {
            mListener.onClickButton(action.getId());
        }
        // Fragment終了
        getParentFragmentManager().beginTransaction().remove(this).commit();
    }
}
~~~
#### MainActivity.java
~~~java
//public class MainActivity extends Activity {
public class MainActivity extends FragmentActivity implements GuideTvFragmentListener {
    // エラー表示ガイド
    private GuidedStepSupportFragment mGuidFragment;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mFragmentActivity = this;

        Button btn = (Button) findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mGuidFragment = new GuideTvFragment("タイトルサンプル", "ほげほげ\nふがふが");
                GuidedStepSupportFragment.addAsRoot(mFragmentActivity, mGuidFragment, android.R.id.content);
            }
        });
    }

    public void onClickButton(long action) {
        Toast.makeText(getApplicationContext(), "表示しました。ボタンID[" + action + "]", Toast.LENGTH_SHORT).show();
    }
~~~

### 解説
1. guide_tv_frame.xml：Guideを表示する用のFrameLayoutを追加
1. GuideTvFragmentListener.java：GuideからのボタンイベントのListenerを追加
1. GuideTvFragment.java：GuidedStepSupportFragment継承したFragmentクラスを追加。
   onAtach/onDetach：FragmentのAtache/Detachを行う（そのまんまか）
   onCreateGuidance：Guideの生成を行う。この記載がテンプレートっぽい
   onCreateActions：Guideに表示するボタンを追加する。GuidedAction.Builder()が非推奨になっているのが気になる。　actions.add()を追加することで、ボタンの数を増やせる
   onGuidedActionClicked：ボタンがクリックされたときのイベント。Listenerに返却
1. MainActivity.java：Activity→FragmentActivityに変更。Listenerを登録
   GuidedStepSupportFragment.addAsRoot:Guideを表示。第一期比数がFragmentActivityを継承したクラスを指定しなければならない
   onClickButton：GuideTvFragmentからの返却
　　　　
## 表示イメージ
編集前のスマホ向けの表示と、編集後のAndroitTV向けの表示で以下のようになる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3468893/d17c4c61-0b8c-3c16-de26-50aa86479f8c.png)


## サンプルソース
[Githubを参照](https://github.com/miyaken3381/androidSample/tree/main/01_GuidedStepSupportFragment)