├── .gitea └── workflows │ └── android.yml ├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── gradle.xml ├── misc.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── release │ ├── app-release.apk │ └── output-metadata.json └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── lucario │ │ └── gpt4allandroid │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── lucario │ │ │ └── gpt4allandroid │ │ │ ├── Chat.java │ │ │ ├── ChatAdapter.java │ │ │ ├── MainActivity.java │ │ │ ├── Message.java │ │ │ ├── MessageAdapter.java │ │ │ ├── MessageView.java │ │ │ ├── SettingsDialogFragment.java │ │ │ └── SwipeToDeleteCallback.java │ ├── jniLibs │ │ └── arm64-v8a │ │ │ ├── libgptj-default.so │ │ │ └── libllmodel.so │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── baseline_lock_24.xml │ │ ├── baseline_logout_24.xml │ │ ├── baseline_mic_24.xml │ │ ├── baseline_person_24.xml │ │ ├── baseline_settings_24.xml │ │ ├── baseline_stop_24.xml │ │ ├── chatgpt_icon.xml │ │ ├── custom_edittext.xml │ │ ├── ic_baseline_chat_24.xml │ │ ├── ic_baseline_send_24.xml │ │ ├── ic_delete.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_password_visibility.xml │ │ ├── rounded_button.xml │ │ ├── rounded_corner.xml │ │ └── text_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── chat_heads.xml │ │ ├── chat_item.xml │ │ ├── dialog_settings.xml │ │ ├── message_view.xml │ │ └── progress_dialog.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-anydpi-v33 │ │ └── ic_launcher.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ ├── colors.xml │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── lucario │ └── gpt4allandroid │ └── ExampleUnitTest.java ├── build.gradle ├── demo.mp4 ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitea/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | ANDROID_HOME: ${{ github.workspace }}/android/sdk 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Download and unzip ARM64 Android CI files 20 | run: | 21 | curl -LO https://github.com/ronith256/ARM64-Android-CI-CD/releases/download/33/armBuild33.zip 22 | unzip -q armBuild33.zip 23 | 24 | - name: Set up Android SDK 25 | run: | 26 | echo -e "\nsdk.dir=$ANDROID_HOME" >> local.properties 27 | echo -e "\nandroid.aapt2FromMavenOverride=$ANDROID_HOME/build-tools/33.0.3/aapt2" >> gradle.properties 28 | 29 | - name: Set up JDK 11 30 | uses: actions/setup-java@v3 31 | with: 32 | java-version: '11' 33 | distribution: 'temurin' 34 | cache: gradle 35 | java-home: /usr/lib/jvm/java-11-openjdk-arm64 36 | 37 | - name: Grant execute permission for gradlew 38 | run: chmod +x gradlew 39 | 40 | - name: Accept Android SDK licenses 41 | run: echo y | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses --sdk_root=$ANDROID_HOME 42 | - name: Convert base64-encoded keystore to JKS 43 | run: | 44 | echo "${{ secrets.KEY_STORE }}" | base64 --decode > keystore.jks 45 | - name: Build and sign APK 46 | run: | 47 | ./gradlew assembleRelease 48 | chmod +x $ANDROID_HOME/build-tools/33.0.3/apksigner 49 | $ANDROID_HOME/build-tools/33.0.3/apksigner sign --ks keystore.jks --ks-key-alias ${{ secrets.ALIAS }} --ks-pass pass:${{ secrets.KEYSTOREPASSWORD }} --key-pass pass:${{ secrets.KEYPASSWORD }} --out app/build/outputs/apk/release/app-release-signed.apk app/build/outputs/apk/release/app-release-unsigned.apk 50 | - name: Upload APK as an artifact 51 | uses: actions/upload-artifact@v3 52 | with: 53 | name: app-release.apk 54 | path: app/build/outputs/apk/release/app-release-signed.apk -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | GPT4ALL Android -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LocalGPT-Android 2 | - The Local GPT Android is a mobile application that runs the GPT (Generative Pre-trained Transformer) model directly on your Android device. This app does not require an active internet connection, as it executes the GPT model locally. 3 | - (It still uses Internet to download the model, you can manually place the model in data directory and disable internet). 4 | 5 | ## Requirements 6 | - Android 11+ 7 | - At least 3-4 GB of Free RAM (Again "Free" RAM) 8 | - I tested it on iQoo 11 with 16GB RAM and Snapdragon 8 Gen 2 9 | 10 | ## Features 11 | - It works :) 12 | - Only Text Completions as of now 13 | - If you want to to chat, send messages like `### Human:\n prompt\n ### Assistant:` 14 | - The model used does not care about ethics afaik 15 | 16 | ## How to? 17 | - Download the latest release 18 | - Wait for the download to complete 19 | - The model is ggml-gpt4all-j-v1.3-groovy.bin (Downloaded from gpt4all.io) 20 | - The model will get loaded 21 | - You can start chatting 22 | 23 | ## Benchmarks 24 | | Device Name | SoC | RAM | Model Load Time | Average Response Initiation Time | 25 | |-----------------|------------|-------|-----------------|----------------------------------| 26 | | iQoo 11 | SD 8 Gen 2 | 16 GB | 4 seconds | 2 seconds | 27 | | Galaxy S21 Plus | SD 888 | 8 GB | 7 seconds | 6 seconds | 28 | | LG G8X | SD 855 | 6 GB | Did not run | NaN | 29 | | Xiaomi Poco F5 | SD 7+Gen2 | 8 GB | 7 seconds | 5 seconds | 30 | | Pixel 6 Pro | Tensor | 12 GB | 5 seconds | 5 seconds | 31 | 32 | ## Demo 33 | - [Demo Video](demo.mp4) or https://youtube.com/shorts/J7DJ-40Uy1k?feature=share 34 | 35 | ## TODO 36 | - [x] Add very basic chat support 37 | - [ ] Reduce the size of the model 38 | - [ ] Fix UI issues 39 | 40 | ## Notes 41 | - It is very much buggy. I haven't tested it much. 42 | - You'll need a good device to run this. 43 | - Response initiation time and RAM usage for Chat Completion increases with the number of messages. 44 | - This is because Chat Completion is using Text Completion, and with every message the prompt size increases. 45 | - This could be fixed by training the model with Chat model in mind. I'm looking into this. 46 | 47 | ## Credits 48 | - nomic-AI 49 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /jniLibs/arm64-v8a/a.bat -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | namespace 'com.lucario.gpt4allandroid' 7 | compileSdk 33 8 | 9 | defaultConfig { 10 | applicationId "com.lucario.gpt4allandroid" 11 | minSdk 30 12 | targetSdk 33 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | externalNativeBuild { 18 | cmake { 19 | cppFlags '' 20 | } 21 | } 22 | 23 | ndk { 24 | abiFilters 'arm64-v8a' 25 | } 26 | } 27 | 28 | buildTypes { 29 | release { 30 | minifyEnabled false 31 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 32 | } 33 | } 34 | compileOptions { 35 | sourceCompatibility JavaVersion.VERSION_11 36 | targetCompatibility JavaVersion.VERSION_11 37 | } 38 | 39 | splits { 40 | abi { 41 | include 'arm64-v8a' 42 | } 43 | } 44 | } 45 | 46 | dependencies { 47 | implementation fileTree(dir: "libs", include: ["*.jar"]) 48 | implementation 'androidx.appcompat:appcompat:1.6.1' 49 | implementation 'com.google.android.material:material:1.9.0' 50 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 51 | testImplementation 'junit:junit:4.13.2' 52 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 53 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 54 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/release/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ronith256/LocalGPT-Android/b59c8ba837a2eb3aaf6ae2a61e14889859143eb8/app/release/app-release.apk -------------------------------------------------------------------------------- /app/release/output-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "artifactType": { 4 | "type": "APK", 5 | "kind": "Directory" 6 | }, 7 | "applicationId": "com.lucario.gpt4allandroid", 8 | "variantName": "release", 9 | "elements": [ 10 | { 11 | "type": "SINGLE", 12 | "filters": [], 13 | "attributes": [], 14 | "versionCode": 1, 15 | "versionName": "1.0", 16 | "outputFile": "app-release.apk" 17 | } 18 | ], 19 | "elementType": "File" 20 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/lucario/gpt4allandroid/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.lucario.gpt4allandroid; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.lucario.gpt4allandroid", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/lucario/gpt4allandroid/Chat.java: -------------------------------------------------------------------------------- 1 | package com.lucario.gpt4allandroid; 2 | 3 | import java.io.File; 4 | import java.io.Serializable; 5 | 6 | public class Chat implements Serializable { 7 | private int mProfileLogo; 8 | private String chatName; 9 | private String mLatestChat; 10 | private int chatId; 11 | 12 | private boolean consent; 13 | private int recordNumber = 0; 14 | public boolean isFirstPrompt() { 15 | return isFirstPrompt; 16 | } 17 | 18 | public void setFirstPrompt(boolean firstPrompt) { 19 | isFirstPrompt = firstPrompt; 20 | } 21 | 22 | private boolean isFirstPrompt; 23 | 24 | public String getSessionKey() { 25 | return sessionKey; 26 | } 27 | 28 | public void setSessionKey(String sessionKey) { 29 | this.sessionKey = sessionKey; 30 | } 31 | 32 | private String sessionKey; 33 | File chatArray; 34 | 35 | public long getSessionStartTime() { 36 | return sessionStartTime; 37 | } 38 | 39 | public void setSessionStartTime(long sessionStartTime) { 40 | this.sessionStartTime = sessionStartTime; 41 | } 42 | 43 | long sessionStartTime; 44 | 45 | public void setSessionTimeOutVal(long sessionTimeOutVal) { 46 | this.sessionTimeOutVal = sessionTimeOutVal; 47 | } 48 | 49 | public long getSessionTimeOutVal() { 50 | return sessionTimeOutVal; 51 | } 52 | 53 | private long sessionTimeOutVal; 54 | public Chat(int chatId, int profileLogo, String profileName, String latestChat, File chatArray, boolean isFirstPrompt, String sessionKey, long sessionStartTime) { 55 | this.chatId = chatId; 56 | this.mProfileLogo = profileLogo; 57 | this.chatName = profileName; 58 | this.mLatestChat = latestChat; 59 | this.chatArray = chatArray; 60 | this.isFirstPrompt = isFirstPrompt; 61 | this.sessionKey = sessionKey; 62 | this.sessionStartTime = sessionStartTime; 63 | this.recordNumber = 0; 64 | this.consent = false; 65 | } 66 | 67 | public int getProfileLogo() { 68 | return mProfileLogo; 69 | } 70 | 71 | public String getChatName() { 72 | return chatName; 73 | } 74 | 75 | public void setChatName(String name){ 76 | chatName = name; 77 | } 78 | 79 | public String getLatestChat() { 80 | return mLatestChat; 81 | } 82 | 83 | public void setLatestChat(String chat){mLatestChat = chat;} 84 | public int getChatId(){ 85 | return chatId; 86 | } 87 | 88 | public File getChatArray(){ 89 | return chatArray; 90 | } 91 | 92 | public void setConsent(boolean a){ 93 | this.consent = a; 94 | } 95 | 96 | public boolean getConsent(){ 97 | return this.consent; 98 | } 99 | 100 | public boolean sessionKeyExists(){return sessionKey!=null;} 101 | 102 | 103 | } 104 | -------------------------------------------------------------------------------- /app/src/main/java/com/lucario/gpt4allandroid/ChatAdapter.java: -------------------------------------------------------------------------------- 1 | package com.lucario.gpt4allandroid; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import androidx.annotation.NonNull; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | 13 | import java.util.List; 14 | 15 | public class ChatAdapter extends RecyclerView.Adapter { 16 | 17 | private Context mContext; 18 | private List mChatList; 19 | private onClick listener; 20 | private deleteItem deleteItemListener; 21 | public ChatAdapter(Context context, List chatList, onClick listener, deleteItem deleteItemListener) { 22 | mContext = context; 23 | mChatList = chatList; 24 | this.listener = listener; 25 | this.deleteItemListener = deleteItemListener; 26 | } 27 | 28 | public void setChatList(List mChatList){ 29 | this.mChatList = mChatList; 30 | } 31 | @NonNull 32 | @Override 33 | public ChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 34 | View view = LayoutInflater.from(mContext).inflate(R.layout.chat_heads, parent, false); 35 | return new ChatViewHolder(view); 36 | } 37 | 38 | @Override 39 | public void onBindViewHolder(@NonNull ChatViewHolder holder, int position) { 40 | Chat chat = mChatList.get(position); 41 | holder.profileName.setText(chat.getChatName()); 42 | holder.latestChat.setText(chat.getLatestChat()); 43 | } 44 | 45 | @Override 46 | public int getItemCount() { 47 | return mChatList.size(); 48 | } 49 | 50 | public class ChatViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 51 | 52 | public ImageView profileLogo; 53 | public TextView profileName; 54 | public TextView latestChat; 55 | 56 | public ChatViewHolder(@NonNull View itemView) { 57 | super(itemView); 58 | profileLogo = itemView.findViewById(R.id.profile_logo); 59 | profileName = itemView.findViewById(R.id.profile_name); 60 | latestChat = itemView.findViewById(R.id.latest_chat); 61 | itemView.setOnClickListener(this); 62 | } 63 | 64 | @Override 65 | public void onClick(View view) { 66 | listener.clicked(getAdapterPosition()); 67 | } 68 | } 69 | public interface onClick{ 70 | void clicked(int position); 71 | } 72 | 73 | public Context getContext(){ 74 | return mContext; 75 | } 76 | 77 | public void deleteItem(int position){ 78 | deleteItemListener.deleteItem(position); 79 | } 80 | public interface deleteItem{ 81 | public void deleteItem(int position); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/lucario/gpt4allandroid/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.lucario.gpt4allandroid; 2 | import androidx.appcompat.app.AlertDialog; 3 | import androidx.appcompat.app.AppCompatActivity; 4 | import androidx.recyclerview.widget.ItemTouchHelper; 5 | import androidx.recyclerview.widget.LinearLayoutManager; 6 | import androidx.recyclerview.widget.RecyclerView; 7 | 8 | import android.annotation.SuppressLint; 9 | import android.app.DownloadManager; 10 | import android.app.ProgressDialog; 11 | import android.content.BroadcastReceiver; 12 | import android.content.Context; 13 | import android.content.Intent; 14 | import android.content.IntentFilter; 15 | import android.content.SharedPreferences; 16 | import android.database.Cursor; 17 | import android.net.Uri; 18 | import android.os.Bundle; 19 | import android.os.Environment; 20 | import android.util.Log; 21 | import android.widget.ImageButton; 22 | import android.widget.LinearLayout; 23 | import android.widget.ProgressBar; 24 | import android.widget.Toast; 25 | 26 | import com.google.android.material.floatingactionbutton.FloatingActionButton; 27 | 28 | import java.io.File; 29 | import java.io.FileInputStream; 30 | import java.io.FileOutputStream; 31 | import java.io.IOException; 32 | import java.io.ObjectInputStream; 33 | import java.io.ObjectOutputStream; 34 | import java.io.Serializable; 35 | import java.util.ArrayList; 36 | import java.util.List; 37 | 38 | @SuppressWarnings("JavaJniMissingFunction") 39 | public class MainActivity extends AppCompatActivity implements ChatAdapter.onClick, ChatAdapter.deleteItem { 40 | 41 | private static String modelPath = "/storage/emulated/0/Android/data/com.lucario.gpt4allandroid/files/Documents/ggml-gpt4all-j-v1.3-groovy.bin"; 42 | public static native void prompt(long model, String prompt, int contextSize); 43 | 44 | public static native void destoryModel(long model); 45 | public static native long loadGo(String fname, int nThreads); 46 | private ChatAdapter mAdapter; 47 | private List mChatList; 48 | 49 | private Intent messageViewIntent; 50 | 51 | private int numThreads = 0; 52 | private FloatingActionButton newChatButton; 53 | static SharedPreferences pref; 54 | private static long model; 55 | private boolean modelExists = false; 56 | 57 | private long downloadID; 58 | 59 | @Override 60 | protected void onCreate(Bundle savedInstanceState) { 61 | super.onCreate(savedInstanceState); 62 | setContentView(R.layout.activity_main); 63 | 64 | pref = getSharedPreferences("nums", MODE_PRIVATE); 65 | modelExists = pref.getBoolean("model", false); 66 | numThreads = pref.getInt("num-threads", 4); 67 | RecyclerView mRecyclerView = findViewById(R.id.chat_recycler_view); 68 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 69 | ImageButton settingsButton = findViewById(R.id.toolbar_settings); 70 | settingsButton.setOnClickListener(e -> { 71 | SettingsDialogFragment dialog = new SettingsDialogFragment(); 72 | dialog.show(getSupportFragmentManager(), "SettingsDialogFragment"); 73 | 74 | }); 75 | 76 | BroadcastReceiver receiver = new BroadcastReceiver() { 77 | @Override 78 | public void onReceive(Context context, Intent intent) { 79 | String action = intent.getAction(); 80 | if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) { 81 | long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); 82 | if (id == downloadID) { 83 | File sourceFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "ggml-gpt4all-j-v1.3-groovy.bin"); 84 | File destinationFile = new File(getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "ggml-gpt4all-j-v1.3-groovy.bin"); 85 | boolean isMoved = sourceFile.renameTo(destinationFile); 86 | Ddialog.setCancelable(true); 87 | Ddialog.dismiss(); 88 | showLoading(); 89 | new Thread(() -> { 90 | createEmptyTextFile(); 91 | model = loadGo(modelPath, numThreads); 92 | }).start(); 93 | } 94 | } 95 | } 96 | }; 97 | 98 | File modelFile = new File(modelPath); 99 | if(!modelFile.exists()){ 100 | String downloadUrl = "https://gpt4all.io/models/ggml-gpt4all-j-v1.3-groovy.bin"; 101 | DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); 102 | DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadUrl)); 103 | request.setTitle("File Download"); 104 | request.setDescription("Downloading file..."); 105 | request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOCUMENTS, "ggml-gpt4all-j-v1.3-groovy.bin"); 106 | downloadID = downloadManager.enqueue(request); 107 | registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); 108 | showDownload(); 109 | } else { 110 | pref.edit().putBoolean("model", true).apply(); 111 | showLoading(); 112 | new Thread(() -> { 113 | createEmptyTextFile(); 114 | model = loadGo(modelPath, numThreads); 115 | }).start(); 116 | } 117 | 118 | newChatButton = findViewById(R.id.fab_new_chat); 119 | mChatList = new ArrayList<>(); 120 | loadChatList(); 121 | 122 | 123 | newChatButton.setOnClickListener(e->{ 124 | messageViewIntent = new Intent(MainActivity.this, MessageView.class); 125 | Chat chat = new Chat(mChatList.size()+1, 0, "null", "null", new File(String.valueOf(System.currentTimeMillis())), true, null, 0); 126 | mChatList.add(chat); 127 | messageViewIntent.putExtra("model-pointer", model); 128 | messageViewIntent.putExtra("chat", mChatList.size()-1); 129 | saveChatList(); 130 | messageViewIntent.putExtra("chatList", (Serializable) mChatList); 131 | startActivity(messageViewIntent); 132 | mAdapter.setChatList(mChatList); 133 | mAdapter.notifyItemInserted(mChatList.size()); 134 | // finish(); 135 | }); 136 | mAdapter = new ChatAdapter(this, mChatList, this, this); 137 | mRecyclerView.setAdapter(mAdapter); 138 | ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(mAdapter)); 139 | itemTouchHelper.attachToRecyclerView(mRecyclerView); 140 | 141 | } 142 | 143 | 144 | 145 | static { 146 | System.loadLibrary("llmodel"); 147 | System.loadLibrary("gptj-default"); 148 | } 149 | 150 | 151 | // Random stuff for the chat interface 152 | private void saveChatList() { 153 | // Write the chat list to a file 154 | try { 155 | FileOutputStream fos = openFileOutput("chat_list.ser", MODE_PRIVATE); 156 | ObjectOutputStream oos = new ObjectOutputStream(fos); 157 | oos.writeObject(mChatList); 158 | oos.close(); 159 | fos.close(); 160 | } catch (IOException e) { 161 | e.printStackTrace(); 162 | } 163 | } 164 | 165 | private void loadChatList() { 166 | // Read the chat list from a file 167 | try { 168 | FileInputStream fis = openFileInput("chat_list.ser"); 169 | ObjectInputStream ois = new ObjectInputStream(fis); 170 | mChatList = (ArrayList) ois.readObject(); 171 | ois.close(); 172 | fis.close(); 173 | } catch (IOException | ClassNotFoundException e) { 174 | e.printStackTrace(); 175 | // Toast.makeText(this, "Load failed", Toast.LENGTH_SHORT).show(); 176 | } 177 | 178 | // Initialize the chat list if it doesn't exist 179 | if (mChatList == null) { 180 | mChatList = new ArrayList<>(); 181 | } 182 | } 183 | 184 | @Override 185 | public void clicked(int position) { 186 | if(model!=0){ 187 | Chat item = mChatList.get(position); 188 | // Create an Intent to start a new activity 189 | Intent intent = new Intent(MainActivity.this, MessageView.class); 190 | intent.putExtra("model-pointer", model); 191 | intent.putExtra("chat", position); 192 | intent.putExtra("old-chat", true); 193 | intent.putExtra("chatList", (Serializable) mChatList); 194 | // Start the new activity 195 | startActivity(intent); 196 | } 197 | } 198 | 199 | @Override 200 | public void deleteItem(int position) { 201 | ArrayList temp = new ArrayList<>(mChatList.size()); 202 | for(int i = 0; i < mChatList.size(); i++){ 203 | if(i == position){ 204 | } else if (i>position){ 205 | Chat chat = mChatList.get(i); 206 | temp.add(chat); 207 | } else { 208 | temp.add(mChatList.get(i)); 209 | } 210 | } 211 | mChatList = temp; 212 | saveChatList(); 213 | mAdapter.setChatList(mChatList); 214 | mAdapter.notifyItemRemoved(position); 215 | } 216 | 217 | public static void createEmptyTextFile() { 218 | // Specify the directory path 219 | String directoryPath = "/storage/emulated/0/Android/data/com.lucario.gpt4allandroid/files/Documents"; 220 | 221 | // Create a File object with the specified directory path and file name 222 | File file = new File(directoryPath, "response.txt"); 223 | 224 | try { 225 | // Check if the file exists 226 | if (file.exists()) { 227 | // Delete the existing file 228 | file.delete(); 229 | } 230 | 231 | // Create the empty text file 232 | if (file.createNewFile()) { 233 | System.out.println("Empty text file created successfully."); 234 | } else { 235 | System.out.println("Error occurred while creating the empty text file."); 236 | } 237 | } catch (IOException e) { 238 | System.out.println("An error occurred while creating the empty text file."); 239 | e.printStackTrace(); 240 | } 241 | } 242 | 243 | private AlertDialog Ddialog; 244 | private void showDownload() { 245 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 246 | builder.setTitle("Downloading Model") 247 | .setMessage("Please wait while the Model is being downloaded.\n Check notif for progress"); 248 | 249 | Ddialog = builder.create(); 250 | Ddialog.setCancelable(false); 251 | Ddialog.show(); 252 | } 253 | 254 | 255 | private void showLoading() { 256 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 257 | builder.setTitle("Loading Model") 258 | .setMessage("Please wait while the Model is being loaded"); 259 | 260 | AlertDialog dialog = builder.create(); 261 | dialog.setCancelable(false); 262 | new Thread(new Runnable() { 263 | @Override 264 | public void run() { 265 | while(model==0){} 266 | runOnUiThread(() -> {dialog.setCancelable(true); dialog.dismiss(); newChatButton.setEnabled(true);}); 267 | 268 | } 269 | }).start(); 270 | dialog.show(); 271 | } 272 | 273 | public static void updateModel(int numThreads){ 274 | destoryModel(model); 275 | new Thread(() -> { 276 | createEmptyTextFile(); 277 | model = loadGo(modelPath, numThreads); 278 | }).start(); 279 | } 280 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lucario/gpt4allandroid/Message.java: -------------------------------------------------------------------------------- 1 | package com.lucario.gpt4allandroid; 2 | 3 | import java.io.Serializable; 4 | 5 | public class Message implements Serializable { 6 | public static String SENT_BY_ME = "me"; 7 | public static String SENT_BY_BOT= "bot"; 8 | 9 | public static String FAILED_RESPONSE = "failed"; 10 | 11 | String message; 12 | String sentBy; 13 | int currentIndex; 14 | boolean firstTime; 15 | 16 | public void setFinished(boolean finished) { 17 | this.finished = finished; 18 | // this.message = null; 19 | } 20 | 21 | boolean finished; 22 | 23 | public String getMessage() { 24 | return message; 25 | } 26 | 27 | public void setMessage(String message) { 28 | this.message = message; 29 | } 30 | 31 | public String getSentBy() { 32 | return sentBy; 33 | } 34 | 35 | public void setSentBy(String sentBy) { 36 | this.sentBy = sentBy; 37 | } 38 | 39 | public void append(String msg){message = message + msg;} 40 | public void setFirstTime(boolean firstTime) {this.firstTime = firstTime;} 41 | public Message(String message, String sentBy) { 42 | this.message = ""; 43 | append(message); 44 | this.sentBy = sentBy; 45 | this.firstTime = true; 46 | this.currentIndex = 0; 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lucario/gpt4allandroid/MessageAdapter.java: -------------------------------------------------------------------------------- 1 | package com.lucario.gpt4allandroid; 2 | 3 | import android.content.ClipData; 4 | import android.content.ClipboardManager; 5 | import android.content.Context; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.LinearLayout; 10 | import android.widget.TextView; 11 | import android.widget.Toast; 12 | 13 | import androidx.annotation.NonNull; 14 | import androidx.recyclerview.widget.RecyclerView; 15 | 16 | import java.util.List; 17 | 18 | public class MessageAdapter extends RecyclerView.Adapter { 19 | 20 | List messageList; 21 | MessageDoneListener listener; 22 | Context context; 23 | 24 | ClipboardManager clipboardManager; 25 | public MessageAdapter(List messageList, MessageDoneListener listener, Context context) { 26 | this.messageList = messageList; 27 | this.listener = listener; 28 | this.context = context; 29 | this.clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); 30 | } 31 | 32 | @NonNull 33 | @Override 34 | public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 35 | View chatView = LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_item,null); 36 | MyViewHolder myViewHolder = new MyViewHolder(chatView); 37 | return myViewHolder; 38 | } 39 | 40 | @Override 41 | public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { 42 | Message message = messageList.get(position); 43 | 44 | holder.rightTextView.setOnLongClickListener(e->{ 45 | copyToClipboard(holder.rightTextView.getText().toString()); 46 | Toast.makeText(context, "Copied Text", Toast.LENGTH_SHORT).show(); 47 | return true; 48 | }); 49 | 50 | holder.leftTextView.setOnLongClickListener(e->{ 51 | copyToClipboard(holder.leftTextView.getText().toString()); 52 | Toast.makeText(context, "Copied Text", Toast.LENGTH_SHORT).show(); 53 | return true; 54 | }); 55 | 56 | if (message.getSentBy().equals(Message.SENT_BY_ME)) { 57 | holder.leftChatView.setVisibility(View.GONE); 58 | holder.rightChatView.setVisibility(View.VISIBLE); 59 | holder.rightTextView.setText(message.getMessage()); 60 | } else { 61 | holder.rightChatView.setVisibility(View.GONE); 62 | holder.leftChatView.setVisibility(View.VISIBLE); 63 | if (message.finished) { 64 | holder.leftTextView.setText(message.getMessage()); 65 | } else { 66 | holder.leftTextView.setText(message.getMessage()); 67 | } 68 | } 69 | } 70 | 71 | private void copyToClipboard(String text) { 72 | ClipData clip = ClipData.newPlainText("Copied Text", text); 73 | clipboardManager.setPrimaryClip(clip); 74 | } 75 | 76 | @Override 77 | public int getItemCount() { 78 | return messageList.size(); 79 | } 80 | 81 | 82 | public class MyViewHolder extends RecyclerView.ViewHolder{ 83 | LinearLayout leftChatView,rightChatView; 84 | TextView leftTextView,rightTextView; 85 | 86 | public MyViewHolder(@NonNull View itemView) { 87 | super(itemView); 88 | leftChatView = itemView.findViewById(R.id.left_chat_view); 89 | rightChatView = itemView.findViewById(R.id.right_chat_view); 90 | leftTextView = itemView.findViewById(R.id.left_chat_text_view); 91 | rightTextView = itemView.findViewById(R.id.right_chat_text_view); 92 | } 93 | } 94 | 95 | public interface MessageDoneListener{ 96 | public void setFirstTime(int position); 97 | } 98 | } 99 | 100 | -------------------------------------------------------------------------------- /app/src/main/java/com/lucario/gpt4allandroid/MessageView.java: -------------------------------------------------------------------------------- 1 | package com.lucario.gpt4allandroid; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Intent; 5 | import android.content.SharedPreferences; 6 | import android.content.pm.PackageManager; 7 | import android.graphics.Bitmap; 8 | import android.graphics.BitmapFactory; 9 | import android.icu.text.SimpleDateFormat; 10 | import android.os.Bundle; 11 | import android.os.Environment; 12 | import android.os.Handler; 13 | import android.os.Looper; 14 | 15 | import android.print.PrintAttributes; 16 | import android.util.Base64; 17 | import android.view.LayoutInflater; 18 | import android.view.View; 19 | import android.webkit.WebView; 20 | import android.webkit.WebViewClient; 21 | import android.widget.EditText; 22 | import android.widget.ImageButton; 23 | import android.widget.TextView; 24 | import android.widget.Toast; 25 | 26 | import androidx.activity.result.ActivityResultLauncher; 27 | import androidx.annotation.NonNull; 28 | import androidx.appcompat.app.AlertDialog; 29 | import androidx.appcompat.app.AppCompatActivity; 30 | import androidx.appcompat.widget.AppCompatButton; 31 | import androidx.cardview.widget.CardView; 32 | import androidx.recyclerview.widget.LinearLayoutManager; 33 | import androidx.recyclerview.widget.RecyclerView; 34 | 35 | import org.json.JSONArray; 36 | import org.json.JSONException; 37 | import org.json.JSONObject; 38 | 39 | import java.io.ByteArrayOutputStream; 40 | import java.io.File; 41 | import java.io.FileInputStream; 42 | import java.io.FileOutputStream; 43 | import java.io.FileWriter; 44 | import java.io.IOException; 45 | import java.io.ObjectInputStream; 46 | import java.io.ObjectOutputStream; 47 | import java.nio.file.FileSystems; 48 | import java.nio.file.Files; 49 | import java.nio.file.Path; 50 | import java.nio.file.Paths; 51 | import java.nio.file.StandardWatchEventKinds; 52 | import java.nio.file.WatchEvent; 53 | import java.nio.file.WatchKey; 54 | import java.nio.file.WatchService; 55 | import java.util.ArrayList; 56 | import java.util.Date; 57 | import java.util.LinkedList; 58 | import java.util.List; 59 | import java.util.Locale; 60 | import java.util.Timer; 61 | import java.util.TimerTask; 62 | import java.util.concurrent.TimeUnit; 63 | 64 | public class MessageView extends AppCompatActivity implements MessageAdapter.MessageDoneListener{ 65 | RecyclerView recyclerView; 66 | EditText messageEditText; 67 | ImageButton sendButton; 68 | static List messageList; 69 | JSONArray messageArray; 70 | static MessageAdapter messageAdapter; 71 | 72 | TextView timerText; 73 | 74 | public static int sessionTimeout, httpSessionTimeout; 75 | private File chatFile; 76 | private Chat chat; 77 | private List mChatList; 78 | private String question; 79 | long prevTime; 80 | private static final int PERMISSION_REQUEST_CODE = 111; 81 | private ActivityResultLauncher launcher; 82 | 83 | private long model; 84 | private int ctxSize; 85 | 86 | private boolean isChat = true; 87 | private StringBuilder builder; 88 | 89 | private String response = ""; 90 | 91 | @Override 92 | protected void onStart() { 93 | super.onStart(); 94 | } 95 | 96 | @SuppressLint("SetTextI18n") 97 | @Override 98 | protected void onCreate(Bundle savedInstanceState) { 99 | super.onCreate(savedInstanceState); 100 | SharedPreferences pref = getSharedPreferences("nums", MODE_PRIVATE); 101 | ctxSize = pref.getInt("ctx-size", 2048); 102 | model = getIntent().getLongExtra("model-pointer", 0); 103 | if(model == 0){ 104 | finish(); 105 | } 106 | setContentView(R.layout.message_view); 107 | 108 | ImageButton settingsButton = findViewById(R.id.toolbar_settings); 109 | settingsButton.setOnClickListener(e -> { 110 | SettingsDialogFragment dialog = new SettingsDialogFragment(); 111 | dialog.show(getSupportFragmentManager(), "SettingsDialogFragment"); 112 | }); 113 | 114 | if(isChat){ 115 | builder = new StringBuilder(); 116 | } 117 | 118 | mChatList = (List) getIntent().getSerializableExtra("chatList"); 119 | int s = getIntent().getIntExtra("chat", 0); 120 | System.out.println(s); 121 | chat = mChatList.get(s); 122 | chatFile = chat.getChatArray(); 123 | messageList = new ArrayList<>(); 124 | messageArray = new JSONArray(); 125 | 126 | loadChatList(chatFile); 127 | 128 | CardView buttonsView = findViewById(R.id.buttonsView); 129 | AppCompatButton chatButton = findViewById(R.id.chatButton); 130 | AppCompatButton completionButton = findViewById(R.id.textButton); 131 | chatButton.setOnClickListener(e->{buttonsView.setVisibility(View.GONE);}); 132 | completionButton.setOnClickListener(e->{isChat = false; buttonsView.setVisibility(View.GONE);}); 133 | 134 | 135 | recyclerView = findViewById(R.id.recycler_view); 136 | messageEditText = findViewById(R.id.message_edit_text); 137 | sendButton = findViewById(R.id.send_btn); 138 | 139 | //setup recycler view 140 | messageAdapter = new MessageAdapter(messageList, this, this); 141 | recyclerView.setAdapter(messageAdapter); 142 | recyclerView.getItemAnimator().setChangeDuration(0); 143 | LinearLayoutManager llm = new LinearLayoutManager(this); 144 | llm.setStackFromEnd(true); 145 | recyclerView.setLayoutManager(llm); 146 | 147 | sendButton.setOnClickListener((v) -> { 148 | buttonsView.setVisibility(View.GONE); 149 | MainActivity.createEmptyTextFile(); 150 | String question = messageEditText.getText().toString().trim(); 151 | addToChat(question, Message.SENT_BY_ME); 152 | messageEditText.setText(""); 153 | callAPI(question); 154 | }); 155 | } 156 | 157 | void addToChat(String message, String sentBy) { 158 | runOnUiThread(() -> { 159 | messageList.add(new Message(message, sentBy)); 160 | saveChatList(chatFile.getName(), messageList); 161 | messageAdapter.notifyDataSetChanged(); 162 | recyclerView.smoothScrollToPosition(messageAdapter.getItemCount()); 163 | }); 164 | } 165 | 166 | private void saveChatList(String name, List saveList) { 167 | // Write the chat list to a file 168 | try { 169 | FileOutputStream fos = openFileOutput(name, MODE_PRIVATE); 170 | ObjectOutputStream oos = new ObjectOutputStream(fos); 171 | oos.writeObject(saveList); 172 | oos.close(); 173 | fos.close(); 174 | } catch (IOException e) { 175 | e.printStackTrace(); 176 | } 177 | } 178 | 179 | private void loadChatList(File name) { 180 | // Read the chat list from a file 181 | try { 182 | FileInputStream fis = openFileInput(name.getName()); 183 | ObjectInputStream ois = new ObjectInputStream(fis); 184 | messageList = (ArrayList) ois.readObject(); 185 | ois.close(); 186 | fis.close(); 187 | } catch (IOException | ClassNotFoundException e) { 188 | e.printStackTrace(); 189 | // Toast.makeText(this, "Load failed", Toast.LENGTH_SHORT).show(); 190 | } 191 | // Initialize the chat list if it doesn't exist 192 | if (messageList == null) { 193 | messageList = new ArrayList<>(); 194 | } 195 | } 196 | 197 | 198 | // Handle the permission request response 199 | @Override 200 | public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { 201 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 202 | if (requestCode == PERMISSION_REQUEST_CODE) { 203 | if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 204 | } else { 205 | Toast.makeText(this, "Microphone permission is not granted!", Toast.LENGTH_SHORT).show(); 206 | } 207 | } 208 | } 209 | 210 | 211 | void addResponse(String response, boolean failed) { 212 | // messageList.remove(messageList.size()-1); 213 | if (failed) { 214 | addToChat(response, Message.FAILED_RESPONSE); 215 | } else { 216 | addToChat(response, Message.SENT_BY_BOT); 217 | } 218 | 219 | mChatList.set(chat.getChatId() - 1, chat); 220 | saveChatList("chat_list.ser", mChatList); 221 | } 222 | 223 | 224 | void callAPI(String question) { 225 | if (chat.getChatName().equals("null") || !(chat.getChatName().length() > 1)) { 226 | if (question.length() > 15) { 227 | chat.setChatName(question.substring(0, 15)); 228 | } else { 229 | chat.setChatName(question); 230 | } 231 | mChatList.set(chat.getChatId() - 1, chat); 232 | saveChatList("chat_list.ser", mChatList); 233 | } 234 | 235 | sendConvoMessage(question); 236 | } 237 | 238 | private void sendConvoMessage(String message) { 239 | addResponse("", false); 240 | ctxSize = getSharedPreferences("nums", MODE_PRIVATE).getInt("ctx-size", 2048); 241 | if(isChat && !(builder.length() >1)){ 242 | String chatStuff = "Human: [Usermessage]\nAI: [Assistant response]\n\nHuman: "; 243 | message = chatStuff + message + "\n\nAI:"; 244 | builder.append(message); 245 | } else { 246 | builder.append(" "); 247 | builder.append(response); 248 | builder.append("\n\nHuman: "); 249 | builder.append(message); 250 | builder.append("\n\nAI:"); 251 | } 252 | sendMessage(builder.toString()); 253 | } 254 | 255 | 256 | private void sendMessage(String message) { 257 | // String sMessage = "### Human:" + message; 258 | // sMessage = sMessage + "\\n### Assistant:"; 259 | // System.out.println(sMessage); 260 | String finalSMessage = message; 261 | new Thread(new Runnable() { 262 | @Override 263 | public void run() { 264 | MainActivity.prompt(model, finalSMessage, ctxSize); 265 | } 266 | }).start(); 267 | new Thread(() -> watchFileForChanges()).start(); 268 | } 269 | 270 | private void addMessage(String text, boolean done){ 271 | if (done) { 272 | // messageList.get(messageList.size()-1).message = text; 273 | // runOnUiThread(() -> messageAdapter.notifyItemChanged(messageList.size() - 1)); 274 | Message msg = messageList.get(messageList.size() - 1); 275 | msg.setFinished(true); 276 | if (msg.getMessage().length() > 20) { 277 | chat.setLatestChat(msg.getMessage().substring(0, 17) + "..."); 278 | } else { 279 | chat.setLatestChat(msg.getMessage()); 280 | } 281 | new Thread(() -> { 282 | saveChatList(chatFile.getName(), messageList); 283 | mChatList.set(chat.getChatId() - 1, chat); 284 | saveChatList("chat_list.ser", mChatList); 285 | }).start(); 286 | } else { 287 | response = text; 288 | messageList.get(messageList.size() - 1).message = text; 289 | runOnUiThread(() -> messageAdapter.notifyItemChanged(messageList.size() - 1)); 290 | } 291 | } 292 | 293 | 294 | 295 | @Override 296 | public void onBackPressed() { 297 | super.onBackPressed(); 298 | if(messageList.size() > 1){ 299 | Message msg = messageList.get(messageList.size() - 1); 300 | msg.setFinished(true); 301 | if (msg.getMessage().length() > 20) { 302 | chat.setLatestChat(msg.getMessage().substring(0, 17) + "..."); 303 | } else { 304 | chat.setLatestChat(msg.getMessage()); 305 | } 306 | } 307 | new Thread(() -> { 308 | saveChatList(chatFile.getName(), messageList); 309 | mChatList.set(chat.getChatId() - 1, chat); 310 | saveChatList("chat_list.ser", mChatList); 311 | }).start(); 312 | finish(); 313 | } 314 | 315 | @Override 316 | public void setFirstTime(int position) { 317 | messageList.get(position).setFirstTime(false); 318 | new Thread(new Runnable() { 319 | @Override 320 | public void run() { 321 | saveChatList(chatFile.getName(), messageList); 322 | } 323 | }).start(); 324 | } 325 | 326 | public void watchFileForChanges() { 327 | try { 328 | String builder = ""; 329 | long time = System.currentTimeMillis(); 330 | Path path = Paths.get("/storage/emulated/0/Android/data/com.lucario.gpt4allandroid/files/Documents/response.txt"); 331 | WatchService watchService = FileSystems.getDefault().newWatchService(); 332 | path.getParent().register(watchService, StandardWatchEventKinds.ENTRY_MODIFY); 333 | while (true) { 334 | WatchKey key; 335 | try { 336 | key = watchService.take(); 337 | } catch (InterruptedException ex) { 338 | return; 339 | } 340 | 341 | for (WatchEvent event : key.pollEvents()) { 342 | if (event.context().toString().equals(path.getFileName().toString())) { 343 | // Read content of modified file and print it 344 | String content = new String(Files.readAllBytes(path)); 345 | if(content.length() > builder.length()) { 346 | builder = content; 347 | time = System.currentTimeMillis(); 348 | if (isChat && content.contains("Human:")) { 349 | addMessage(content, true); 350 | return; 351 | // System.out.println("done"); 352 | } 353 | addMessage(content, false); 354 | } 355 | } 356 | } 357 | 358 | boolean valid = key.reset(); 359 | if (!valid) { 360 | break; 361 | } 362 | 363 | // if(System.currentTimeMillis()-time > 1000){ 364 | // addMessage("", true); 365 | // break; 366 | // } 367 | } 368 | } catch (IOException e) { 369 | e.printStackTrace(); 370 | } 371 | } 372 | 373 | 374 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lucario/gpt4allandroid/SettingsDialogFragment.java: -------------------------------------------------------------------------------- 1 | package com.lucario.gpt4allandroid; 2 | 3 | import static android.content.Context.MODE_PRIVATE; 4 | 5 | import android.app.Dialog; 6 | import android.content.SharedPreferences; 7 | import android.os.Bundle; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.widget.Button; 11 | import android.widget.EditText; 12 | 13 | import androidx.annotation.NonNull; 14 | import androidx.appcompat.app.AlertDialog; 15 | import androidx.fragment.app.DialogFragment; 16 | 17 | import java.util.Objects; 18 | 19 | public class SettingsDialogFragment extends DialogFragment { 20 | 21 | private EditText contextSizeEditText; 22 | 23 | private EditText numThreadsEditText; 24 | 25 | @NonNull 26 | @Override 27 | public Dialog onCreateDialog(Bundle savedInstanceState) { 28 | // Create a new AlertDialog.Builder object 29 | AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity()); 30 | 31 | // Inflate the custom layout for the dialog 32 | View view = LayoutInflater.from(getActivity()).inflate(R.layout.dialog_settings, null); 33 | 34 | // Find the EditText and Button views in the layout 35 | contextSizeEditText = view.findViewById(R.id.session_time_edit_text); 36 | numThreadsEditText = view.findViewById(R.id.http_timeout_edit_text); 37 | setValues(); 38 | Button saveButton = view.findViewById(R.id.save_button); 39 | 40 | SharedPreferences sharedPreferences = requireActivity().getSharedPreferences("nums", MODE_PRIVATE); 41 | 42 | saveButton.setOnClickListener(e->{ 43 | try{ 44 | sharedPreferences.edit().putInt("ctx-size", Integer.parseInt(contextSizeEditText.getText().toString())).apply(); 45 | sharedPreferences.edit().putInt("num-threads", Integer.parseInt(numThreadsEditText.getText().toString())).apply(); 46 | // MainActivity.updateModel(sharedPreferences.getInt("num-threads", 4)); 47 | } catch(Exception ignored){ 48 | sharedPreferences.edit().putInt("ctx-size", 2048).apply(); 49 | sharedPreferences.edit().putInt("num-threads", 4).apply(); 50 | } 51 | }); 52 | // Set the custom layout for the dialog and return it 53 | builder.setView(view); 54 | return builder.create(); 55 | } 56 | 57 | private void setValues(){ 58 | SharedPreferences sharedPreferences = requireActivity().getSharedPreferences("nums", MODE_PRIVATE); 59 | contextSizeEditText.setText(String.valueOf(sharedPreferences.getInt("ctx-size", 2048))); 60 | numThreadsEditText.setText(String.valueOf(sharedPreferences.getInt("num-threads", 4))); 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/lucario/gpt4allandroid/SwipeToDeleteCallback.java: -------------------------------------------------------------------------------- 1 | package com.lucario.gpt4allandroid; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Color; 5 | import android.graphics.drawable.ColorDrawable; 6 | import android.graphics.drawable.Drawable; 7 | import android.view.View; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.recyclerview.widget.ItemTouchHelper; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | 13 | public class SwipeToDeleteCallback extends ItemTouchHelper.SimpleCallback { 14 | 15 | private ChatAdapter mAdapter; 16 | private Drawable deleteIcon; 17 | private ColorDrawable background; 18 | 19 | public SwipeToDeleteCallback(ChatAdapter adapter) { 20 | super(0, ItemTouchHelper.LEFT); 21 | mAdapter = adapter; 22 | deleteIcon = mAdapter.getContext().getDrawable(R.drawable.ic_delete); 23 | background = new ColorDrawable(Color.RED); 24 | } 25 | 26 | @Override 27 | public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) { 28 | return false; 29 | } 30 | 31 | @Override 32 | public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { 33 | int position = viewHolder.getAdapterPosition(); 34 | mAdapter.deleteItem(position); 35 | } 36 | 37 | @Override 38 | public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, 39 | float dX, float dY, int actionState, boolean isCurrentlyActive) { 40 | super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); 41 | 42 | View itemView = viewHolder.itemView; 43 | int itemHeight = itemView.getHeight(); 44 | int intrinsicWidth = deleteIcon.getIntrinsicWidth(); 45 | int intrinsicHeight = deleteIcon.getIntrinsicHeight(); 46 | 47 | int deleteIconTop = itemView.getTop() + (itemHeight - intrinsicHeight) / 2; 48 | int deleteIconMargin = (itemHeight - intrinsicHeight) / 2; 49 | int deleteIconLeft = itemView.getRight() - deleteIconMargin - intrinsicWidth; 50 | int deleteIconRight = itemView.getRight() - deleteIconMargin; 51 | int deleteIconBottom = deleteIconTop + intrinsicHeight; 52 | 53 | deleteIcon.setBounds(deleteIconLeft, deleteIconTop, deleteIconRight, deleteIconBottom); 54 | background.setBounds(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom()); 55 | 56 | background.draw(c); 57 | deleteIcon.draw(c); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/jniLibs/arm64-v8a/libgptj-default.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ronith256/LocalGPT-Android/b59c8ba837a2eb3aaf6ae2a61e14889859143eb8/app/src/main/jniLibs/arm64-v8a/libgptj-default.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/arm64-v8a/libllmodel.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ronith256/LocalGPT-Android/b59c8ba837a2eb3aaf6ae2a61e14889859143eb8/app/src/main/jniLibs/arm64-v8a/libllmodel.so -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_lock_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_logout_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_mic_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_person_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_settings_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_stop_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/chatgpt_icon.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/custom_edittext.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_chat_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_send_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_password_visibility.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_corner.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/text_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 20 | 21 | 22 | 26 | 27 | 28 | 35 | 36 | 45 | 46 | 47 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 70 | 71 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /app/src/main/res/layout/chat_heads.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 22 | 23 | 29 | 30 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/chat_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 26 | 27 | 38 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 24 | 25 | 32 | 33 | 42 | 43 | 44 |