AndroidでOpenAi APIを呼び出す

# はじめに
近頃のトレンドのChatGpt。Androidの環境でOpenAi APIを叩いてみた。
既に同じような記事は存在するが、AnroidのJavaっていう点では少ないので記載。


## 準備
まずはAPIを呼び出せるように設定を行います。
https://platform.openai.com/overview
上記URLでアカウントを作成し、ログインしましょう。APIを利用するには、別途料金が必要になってきます。

# アプリ概要
テキストに入力した内容をボタンで送信。バックグランドでOpenAPIに要求し、アウトプット用テキストに出力する簡単な仕組み。

# 実装
### 依存関係
必要な依存関係をビルドシステム(gradle)に記載

依存関係を確認する際には下記サイトを参照。OpenAi API Javaの一覧が記載されている。
https://mvnrepository.com/artifact/com.theokanning.openai-gpt3-java
初心者のために行っておくが、依存関係でOpenAi APIを取り込まないとクラスなども呼べない。


```build.graled
dependencies {

    // https://mvnrepository.com/artifact/com.theokanning.openai-gpt3-java/api
    implementation group: 'com.theokanning.openai-gpt3-java', name: 'api', version: '0.2.0'
    // https://mvnrepository.com/artifact/com.theokanning.openai-gpt3-java/service
    implementation group: 'com.theokanning.openai-gpt3-java', name: 'service', version: '0.14.0'
    // https://mvnrepository.com/artifact/com.theokanning.openai-gpt3-java/client
    implementation group: 'com.theokanning.openai-gpt3-java', name: 'client', version: '0.16.0'
}
```
## ソースコード

```MainActivity.java
public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private EditText mInputText, mOutputText;
    private Button mRequestBtn;
    private ChatGptService mChatGptService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mInputText = findViewById(R.id.inputText);
        mOutputText = findViewById(R.id.outputText);
        mRequestBtn = findViewById(R.id.requestBtn);

        mChatGptService = new ChatGptService(getApplicationContext());
    }

    @Override
    protected void onResume() {
        super.onResume();

        // 回答内容を要求
        mRequestBtn.setOnClickListener(view -> {
            Log.i(TAG, "onClick()");
            String inputText = mInputText.getText().toString();
            mChatGptService.requestChatGpt(inputText, new ChatGptService.Callback() {
                @Override
                public void onRequest() {
                    mRequestBtn.setEnabled(false);
                }

                @Override
                public void onFinishedRequest(String result) {
                    mOutputText.setText(result);
                    mRequestBtn.setEnabled(true);
                }
            });
        });
    }
}
```

### ChatGpServiceクラス
OpenAi APIをバックランドで叩くクラスです。
バックランドとのやり取りには、handlerThreadを作成して、 Handlerでやり取りを行う。ここに関しては本題から外れるので詳しく話しません。OpenAi AIPを叩くには、ネットワーク通信が必要になってきます。バックグランドでの実行を行うようにしましょう。
それに伴いPermissionの設定も必要になってきます。

```AndroidManifest.xml
 <uses-permission android:name="android.permission.INTERNET" />
```

```ChatGptService.java
/**
 * バックグランドでOpenAi APIをコールするクラス
 */
public class ChatGptService {
    private static final String TAG = ChatGptService.class.getSimpleName();
    private final Object mObject = new Object();
    private HandlerThread mHandlerThread;
    private ChatGptHandler mChatGptHandler;
    private Callback mCallback;
    // TODO:key-codeの保存方法は変更する
    private static final String key_token = "自分のtoken";
    private OpenAiService mOpenAiService;
    private SharedPreferences mPhrasingSettingPrefs;
    private static final int DELAY_TIME = 60;
    private static final String DEFAULT_MODE ="通常通り";


    public ChatGptService(Context context) {
        // スレッドはrequestChatGptの時に作成

        mOpenAiService = new OpenAiService(key_token, Duration.ofSeconds(DELAY_TIME));
    }

    /**
     * リクエストを要求する
     * @param inputText
     */
    public void requestChatGpt(String inputText, Callback callback) {
        if (mCallback == null) {
            mCallback = callback;
        }

        mCallback.onRequest();

        if (mHandlerThread == null) {
            mHandlerThread = new HandlerThread("ChatGptService");
            mHandlerThread.start();
        }

        mChatGptHandler = new ChatGptHandler(mHandlerThread.getLooper() ,inputText, mPhrasingSettingPrefs.getString(PHRASING_MODE, DEFAULT_MODE));

        //バックグランドにMessage送信
        Message message = new Message();
        message.what = ChatGptHandler.REQUEST_MSG;
        mChatGptHandler.sendMessage(message);
    }

    class ChatGptHandler extends Handler {
        private static final int REQUEST_MSG = 0;
        private String mInputText;
        private String mContent;
        private ChatMessage mChatMessage;
        private List<ChatMessage> mMessages;
        private ChatMessage mUserMessage;

        ChatGptHandler(Looper looper, String inputText) {
            super(looper);
            mInputText = inputText;
            // TODO
            mContent = "";
            mMessages = new ArrayList<>();

            mChatMessage = new ChatMessage("system", mContent);
            mMessages.add(mChatMessage);

            mUserMessage = new  ChatMessage("user", mInputText);
            mMessages.add(mUserMessage);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case REQUEST_MSG:
                    Log.i(TAG, "handleMessage:" + "REQUEST_MSG");
                    requestChatGpt();
                    break;
                default:
                    Log.w(TAG, "Nothing message");
                    break;
            }
        }

        /**
         * inputされたchat内容を返答する
         */
        private void requestChatGpt() {
            ChatCompletionRequest chatCompletionRequest =
                    ChatCompletionRequest.builder()
                            .model("gpt-3.5-turbo-0613")
                            .messages(mMessages)
                            .maxTokens(2048)
                            .build();
            ChatCompletionResult chatCompletionResult = mOpenAiService.createChatCompletion(chatCompletionRequest);
            String resultText = chatCompletionResult.getChoices().get(0).getMessage().getContent();
            Log.i(TAG, "result:" + resultText);
            notifyFinishedRequest(mCallback, resultText);
        }

        private void notifyFinishedRequest(ChatGptService.Callback callback, String result) {
            final Handler handler = new Handler(Looper.getMainLooper());
            final ChatGptService.Callback callbackImpl = callback;
            if (callbackImpl == null) {
                Log.w(TAG, "Callback is null");
                return;
            }
            synchronized (mObject) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        callbackImpl.onFinishedRequest(result);
                    }
                });
            }
        }
    }



    interface Callback {
        /**
         * リクエストが始まった時のコールバック
         */
        void onRequest();

        /**
         * リクエスト結果を返すコールバック
         * @param result 結果内容
         */
        void onFinishedRequest(String result);
    }
}
```
### ActivtiyのLayout
```activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/phrasingSettingText"
            android:layout_width="0.0dp"
            android:layout_height="wrap_content"
            android:layout_weight="0.8"/>

        <Button
            android:id="@+id/phrasingSettingsBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/phrasingChange"/>

    </LinearLayout>

    <EditText
        android:id="@+id/inputText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/requestBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="30dp"
        android:text="@string/request" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <EditText
            android:id="@+id/outputText"
            android:layout_width="match_parent"
            android:layout_height="200dp"/>
    </ScrollView>
</LinearLayout>
```


# まとめ
簡単なOpenAI APIを利用する簡易アプリを作成してみました
最近では、AIと英会話をするようなアプリも作成されており、そのようなアプリもOpenAI APIを利用してるのでしょうかね。次回はそのようなアプリの作成をしてみたいと思います。