How to use AI to create android app

Android Studio 11 Medium Android Phone emulator
Finally a customed WordPress Client developed using AI Assistance and AI Agent in the flesh

The biggest hurdle of creating an App for android device is learning how to use its tool chain.

This is how I did it using AI and AI Agent or AI Assistance.

Step 1 – Use an AI to explore idea

I am not able to add my site into official WordPress (hosted in AWS lightsail) despite using my username password, then later failed at even using the application password generated from profile section in the wp-admin. After scouring the internet for solution(s), the recommendations suggest installing jetpack or other plugin from WordPress that will allow usage of official WordPress mobile app.

No thank you I do not want to install jetpack or another plugin to my site. Good thing to know that the XML-RPC is still available at the date of this post and WordPress 7.0 still have XML-RPC support.

Knowing that XML-RPC is still supported, I had chosen DeepSeek to explore the idea creation of an app for WordPress.

First prompt is a very generic providing scope and what are the expected functionality

The solution by DeepSeek is simple, provide prerequisites. then it goes further by providing the 8 steps that are needed and additional consideration such as security recommendation. Generated code are in koitln, as I prefer JAVA, i just prompt it to return the code in JAVA.

The 8 steps are:

Step 1 Add Dependencies to the build.gradle

dependencies {
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.9.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.11.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel:2.6.1'
    implementation 'androidx.lifecycle:lifecycle-livedata:2.6.1'
    implementation 'androidx.recyclerview:recyclerview:1.3.1'
}

Step 2 Data Models

WordPressPost.java

import com.google.gson.annotations.SerializedName;

public class WordPressPost {
    @SerializedName("id")
    private int id;
    
    @SerializedName("title")
    private PostTitle title;
    
    @SerializedName("content")
    private PostContent content;
    
    @SerializedName("excerpt")
    private PostExcerpt excerpt;
    
    @SerializedName("status")
    private String status;
    
    @SerializedName("date")
    private String date;
    
    @SerializedName("modified")
    private String modified;

    // Getters and Setters
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    
    public PostTitle getTitle() { return title; }
    public void setTitle(PostTitle title) { this.title = title; }
    
    public PostContent getContent() { return content; }
    public void setContent(PostContent content) { this.content = content; }
    
    public PostExcerpt getExcerpt() { return excerpt; }
    public void setExcerpt(PostExcerpt excerpt) { this.excerpt = excerpt; }
    
    public String getStatus() { return status; }
    public void setStatus(String status) { this.status = status; }
    
    public String getDate() { return date; }
    public void setDate(String date) { this.date = date; }
    
    public String getModified() { return modified; }
    public void setModified(String modified) { this.modified = modified; }
}

class PostTitle {
    @SerializedName("rendered")
    private String rendered;

    public String getRendered() { return rendered; }
    public void setRendered(String rendered) { this.rendered = rendered; }
}

class PostContent {
    @SerializedName("rendered")
    private String rendered;

    public String getRendered() { return rendered; }
    public void setRendered(String rendered) { this.rendered = rendered; }
}

class PostExcerpt {
    @SerializedName("rendered")
    private String rendered;

    public String getRendered() { return rendered; }
    public void setRendered(String rendered) { this.rendered = rendered; }
}

CreatePostRequest.java

import com.google.gson.annotations.SerializedName;

public class CreatePostRequest {
    @SerializedName("title")
    private String title;
    
    @SerializedName("content")
    private String content;
    
    @SerializedName("excerpt")
    private String excerpt;
    
    @SerializedName("status")
    private String status;

    public CreatePostRequest(String title, String content, String excerpt) {
        this.title = title;
        this.content = content;
        this.excerpt = excerpt;
        this.status = "publish";
    }

    // Getters
    public String getTitle() { return title; }
    public String getContent() { return content; }
    public String getExcerpt() { return excerpt; }
    public String getStatus() { return status; }
}

UpdatePostResquest.java

import com.google.gson.annotations.SerializedName;

public class UpdatePostRequest {
    @SerializedName("title")
    private String title;
    
    @SerializedName("content")
    private String content;
    
    @SerializedName("excerpt")
    private String excerpt;
    
    @SerializedName("status")
    private String status;

    public UpdatePostRequest(String title, String content, String excerpt) {
        this.title = title;
        this.content = content;
        this.excerpt = excerpt;
    }

    // Getters
    public String getTitle() { return title; }
    public String getContent() { return content; }
    public String getExcerpt() { return excerpt; }
    public String getStatus() { return status; }
}

Step 3 Network Layer

BasicAuthInterceptor.java

import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

public class BasicAuthInterceptor implements Interceptor {
    private String username;
    private String password;

    public BasicAuthInterceptor(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        String credentials = username + ":" + password;
        String auth = "Basic " + android.util.Base64.encodeToString(
                credentials.getBytes(),
                android.util.Base64.NO_WRAP
        );

        Request originalRequest = chain.request();
        Request newRequest = originalRequest.newBuilder()
                .header("Authorization", auth)
                .build();

        return chain.proceed(newRequest);
    }
}

WordPressService.java

import java.util.List;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path;

public interface WordPressService {
    @GET("/wp-json/wp/v2/posts")
    Call<List<WordPressPost>> getPosts();
    
    @GET("/wp-json/wp/v2/posts/{id}")
    Call<WordPressPost> getPost(@Path("id") int id);
    
    @POST("/wp-json/wp/v2/posts")
    Call<WordPressPost> createPost(@Body CreatePostRequest post);
    
    @PUT("/wp-json/wp/v2/posts/{id}")
    Call<WordPressPost> updatePost(@Path("id") int id, @Body UpdatePostRequest post);
    
    @DELETE("/wp-json/wp/v2/posts/{id}")
    Call<Void> deletePost(@Path("id") int id);
}

ApiClient.java

import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class ApiClient {
    private static Retrofit retrofit = null;

    public static Retrofit getClient(String baseUrl, String username, String password) {
        if (retrofit == null) {
            OkHttpClient client = new OkHttpClient.Builder()
                    .addInterceptor(new BasicAuthInterceptor(username, password))
                    .build();

            retrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .client(client)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }

    public static WordPressService getWordPressService(String baseUrl, String username, String password) {
        Retrofit retrofit = getClient(baseUrl, username, password);
        return retrofit.create(WordPressService.class);
    }
}

Step 4 Repository

WordPressRepository.java

import androidx.lifecycle.MutableLiveData;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class WordPressRepository {
    private WordPressService wordPressService;
    private MutableLiveData<List<WordPressPost>> postsLiveData = new MutableLiveData<>();
    private MutableLiveData<WordPressPost> postLiveData = new MutableLiveData<>();
    private MutableLiveData<String> errorLiveData = new MutableLiveData<>();

    public WordPressRepository(WordPressService wordPressService) {
        this.wordPressService = wordPressService;
    }

    public MutableLiveData<List<WordPressPost>> getPostsLiveData() {
        return postsLiveData;
    }

    public MutableLiveData<WordPressPost> getPostLiveData() {
        return postLiveData;
    }

    public MutableLiveData<String> getErrorLiveData() {
        return errorLiveData;
    }

    public void getPosts() {
        wordPressService.getPosts().enqueue(new Callback<List<WordPressPost>>() {
            @Override
            public void onResponse(Call<List<WordPressPost>> call, Response<List<WordPressPost>> response) {
                if (response.isSuccessful() && response.body() != null) {
                    postsLiveData.setValue(response.body());
                } else {
                    errorLiveData.setValue("Failed to get posts: " + response.message());
                }
            }

            @Override
            public void onFailure(Call<List<WordPressPost>> call, Throwable t) {
                errorLiveData.setValue("Error: " + t.getMessage());
            }
        });
    }

    public void getPost(int id) {
        wordPressService.getPost(id).enqueue(new Callback<WordPressPost>() {
            @Override
            public void onResponse(Call<WordPressPost> call, Response<WordPressPost> response) {
                if (response.isSuccessful() && response.body() != null) {
                    postLiveData.setValue(response.body());
                } else {
                    errorLiveData.setValue("Failed to get post: " + response.message());
                }
            }

            @Override
            public void onFailure(Call<WordPressPost> call, Throwable t) {
                errorLiveData.setValue("Error: " + t.getMessage());
            }
        });
    }

    public void createPost(String title, String content, String excerpt) {
        CreatePostRequest request = new CreatePostRequest(title, content, excerpt);
        wordPressService.createPost(request).enqueue(new Callback<WordPressPost>() {
            @Override
            public void onResponse(Call<WordPressPost> call, Response<WordPressPost> response) {
                if (response.isSuccessful() && response.body() != null) {
                    postLiveData.setValue(response.body());
                    getPosts(); // Refresh posts list
                } else {
                    errorLiveData.setValue("Failed to create post: " + response.message());
                }
            }

            @Override
            public void onFailure(Call<WordPressPost> call, Throwable t) {
                errorLiveData.setValue("Error: " + t.getMessage());
            }
        });
    }

    public void updatePost(int id, String title, String content, String excerpt) {
        UpdatePostRequest request = new UpdatePostRequest(title, content, excerpt);
        wordPressService.updatePost(id, request).enqueue(new Callback<WordPressPost>() {
            @Override
            public void onResponse(Call<WordPressPost> call, Response<WordPressPost> response) {
                if (response.isSuccessful() && response.body() != null) {
                    postLiveData.setValue(response.body());
                    getPosts(); // Refresh posts list
                } else {
                    errorLiveData.setValue("Failed to update post: " + response.message());
                }
            }

            @Override
            public void onFailure(Call<WordPressPost> call, Throwable t) {
                errorLiveData.setValue("Error: " + t.getMessage());
            }
        });
    }

    public void deletePost(int id) {
        wordPressService.deletePost(id).enqueue(new Callback<Void>() {
            @Override
            public void onResponse(Call<Void> call, Response<Void> response) {
                if (response.isSuccessful()) {
                    getPosts(); // Refresh posts list
                } else {
                    errorLiveData.setValue("Failed to delete post: " + response.message());
                }
            }

            @Override
            public void onFailure(Call<Void> call, Throwable t) {
                errorLiveData.setValue("Error: " + t.getMessage());
            }
        });
    }
}

Step 5 ViewModel

WordPressViewModel.java

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class WordPressViewModel extends ViewModel {
    private WordPressRepository repository;
    private MutableLiveData<List<WordPressPost>> postsLiveData = new MutableLiveData<>();
    private MutableLiveData<WordPressPost> postLiveData = new MutableLiveData<>();
    private MutableLiveData<String> errorLiveData = new MutableLiveData<>();

    public void init(WordPressService wordPressService) {
        repository = new WordPressRepository(wordPressService);
        
        // Observe repository LiveData
        repository.getPostsLiveData().observeForever(postsLiveData::setValue);
        repository.getPostLiveData().observeForever(postLiveData::setValue);
        repository.getErrorLiveData().observeForever(errorLiveData::setValue);
    }

    public LiveData<List<WordPressPost>> getPostsLiveData() {
        return postsLiveData;
    }

    public LiveData<WordPressPost> getPostLiveData() {
        return postLiveData;
    }

    public LiveData<String> getErrorLiveData() {
        return errorLiveData;
    }

    public void loadPosts() {
        repository.getPosts();
    }

    public void loadPost(int id) {
        repository.getPost(id);
    }

    public void createPost(String title, String content, String excerpt) {
        repository.createPost(title, content, excerpt);
    }

    public void updatePost(int id, String title, String content, String excerpt) {
        repository.updatePost(id, title, content, excerpt);
    }

    public void deletePost(int id) {
        repository.deletePost(id);
    }

    public void clearError() {
        errorLiveData.setValue(null);
    }
}

Step 6 Adapter

PostAdapter.java

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

public class PostAdapter extends RecyclerView.Adapter<PostAdapter.ViewHolder> {
    private List<WordPressPost> posts;
    private OnItemClickListener onItemClickListener;
    private OnDeleteClickListener onDeleteClickListener;

    public interface OnItemClickListener {
        void onItemClick(WordPressPost post);
    }

    public interface OnDeleteClickListener {
        void onDeleteClick(WordPressPost post);
    }

    public PostAdapter(List<WordPressPost> posts, OnItemClickListener onItemClickListener, OnDeleteClickListener onDeleteClickListener) {
        this.posts = posts;
        this.onItemClickListener = onItemClickListener;
        this.onDeleteClickListener = onDeleteClickListener;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_post, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        WordPressPost post = posts.get(position);
        holder.tvTitle.setText(post.getTitle().getRendered());
        holder.tvDate.setText(post.getDate());

        holder.itemView.setOnClickListener(v -> {
            if (onItemClickListener != null) {
                onItemClickListener.onItemClick(post);
            }
        });

        holder.btnDelete.setOnClickListener(v -> {
            if (onDeleteClickListener != null) {
                onDeleteClickListener.onDeleteClick(post);
            }
        });
    }

    @Override
    public int getItemCount() {
        return posts != null ? posts.size() : 0;
    }

    public void setPosts(List<WordPressPost> posts) {
        this.posts = posts;
        notifyDataSetChanged();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView tvTitle;
        TextView tvDate;
        ImageButton btnDelete;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            tvTitle = itemView.findViewById(R.id.tvTitle);
            tvDate = itemView.findViewById(R.id.tvDate);
            btnDelete = itemView.findViewById(R.id.btnDelete);
        }
    }
}

Step 7 Main Activity

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private WordPressViewModel viewModel;
    private PostAdapter adapter;
    private RecyclerView rvPosts;
    private Button btnAddPost;

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

        initViews();
        setupViewModel();
        setupRecyclerView();
        setupObservers();
        
        viewModel.loadPosts();
    }

    private void initViews() {
        rvPosts = findViewById(R.id.rvPosts);
        btnAddPost = findViewById(R.id.btnAddPost);
        
        btnAddPost.setOnClickListener(v -> showAddPostDialog());
    }

    private void setupViewModel() {
        String baseUrl = "https://your-wordpress-site.com";
        String username = "your-username";
        String password = "your-application-password";
        
        WordPressService service = ApiClient.getWordPressService(baseUrl, username, password);
        
        viewModel = new ViewModelProvider(this).get(WordPressViewModel.class);
        viewModel.init(service);
    }

    private void setupRecyclerView() {
        adapter = new PostAdapter(
                null,
                this::showEditPostDialog,
                post -> viewModel.deletePost(post.getId())
        );
        
        rvPosts.setLayoutManager(new LinearLayoutManager(this));
        rvPosts.setAdapter(adapter);
    }

    private void setupObservers() {
        viewModel.getPostsLiveData().observe(this, new Observer<List<WordPressPost>>() {
            @Override
            public void onChanged(List<WordPressPost> posts) {
                adapter.setPosts(posts);
            }
        });

        viewModel.getErrorLiveData().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String error) {
                if (error != null) {
                    Toast.makeText(MainActivity.this, "Error: " + error, Toast.LENGTH_LONG).show();
                    viewModel.clearError();
                }
            }
        });
    }

    private void showAddPostDialog() {
        View dialogView = LayoutInflater.from(this).inflate(R.layout.dialog_post_edit, null);
        EditText editTitle = dialogView.findViewById(R.id.editTitle);
        EditText editContent = dialogView.findViewById(R.id.editContent);
        EditText editExcerpt = dialogView.findViewById(R.id.editExcerpt);

        new AlertDialog.Builder(this)
                .setTitle("Add New Post")
                .setView(dialogView)
                .setPositiveButton("Create", (dialog, which) -> {
                    String title = editTitle.getText().toString();
                    String content = editContent.getText().toString();
                    String excerpt = editExcerpt.getText().toString();
                    viewModel.createPost(title, content, excerpt.isEmpty() ? null : excerpt);
                })
                .setNegativeButton("Cancel", null)
                .show();
    }

    private void showEditPostDialog(WordPressPost post) {
        View dialogView = LayoutInflater.from(this).inflate(R.layout.dialog_post_edit, null);
        EditText editTitle = dialogView.findViewById(R.id.editTitle);
        EditText editContent = dialogView.findViewById(R.id.editContent);
        EditText editExcerpt = dialogView.findViewById(R.id.editExcerpt);

        editTitle.setText(post.getTitle().getRendered());
        editContent.setText(post.getContent().getRendered());
        editExcerpt.setText(post.getExcerpt().getRendered());

        new AlertDialog.Builder(this)
                .setTitle("Edit Post")
                .setView(dialogView)
                .setPositiveButton("Update", (dialog, which) -> {
                    String title = editTitle.getText().toString();
                    String content = editContent.getText().toString();
                    String excerpt = editExcerpt.getText().toString();
                    viewModel.updatePost(post.getId(), title, content, excerpt.isEmpty() ? null : excerpt);
                })
                .setNegativeButton("Cancel", null)
                .show();
    }
}

Step 8 Layout Files

item_post.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp">

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

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tvTitle"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="18sp"
                android:textStyle="bold"/>

            <TextView
                android:id="@+id/tvDate"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="12sp"
                android:textColor="@android:color/darker_gray"
                android:layout_marginTop="4dp"/>
        </LinearLayout>

        <ImageButton
            android:id="@+id/btnDelete"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:src="@android:drawable/ic_delete"
            android:contentDescription="Delete"/>
    </LinearLayout>

</androidx.cardview.widget.CardView>

dialog_post_edit.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">

    <EditText
        android:id="@+id/editTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Title"
        android:inputType="textCapWords"
        android:layout_marginBottom="8dp"/>

    <EditText
        android:id="@+id/editContent"
        android:layout_width="match_parent"
        android:layout_height="120dp"
        android:hint="Content"
        android:inputType="textMultiLine"
        android:gravity="top"
        android:layout_marginBottom="8dp"/>

    <EditText
        android:id="@+id/editExcerpt"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:hint="Excerpt"
        android:inputType="textMultiLine"
        android:gravity="top"/>

</LinearLayout>

Step 9 AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.wordpressapp">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.WordPressApp">
        
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

The 2nd prompt allows the fundamental of the code to be functional, yet there is things needed to be added.

Step 2 – Refine and add feature to the app

And understanding as a WordPress user myself, there are need for the WordPress app user to post from more than one site.

Prompt DeepSeek to provide further enhancement

Additional codes are generated and the codes that require changes were then suggested by DeepSeek to ensure that the WordPress App can perform CRUD for the post and alllow user to add mutliple sites.

I am satisfied with the codes generated so far. Since I have no android app development experience and I do not have Android Studio experience. It is time for the next step.

Step 3 – Get DeepSeek to suggest best practice of placing the codes into Android Studio

To put all the code to test, I will need the code to be organized correctly as a project in Android Studio.

Another important milestone, to add the codes into Android Studio

Project Structure recommended by DeepSeek

WordPressApp/
├── app/
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/
│   │   │   │   └── com/
│   │   │   │       └── example/
│   │   │   │           └── wordpressapp/
│   │   │   │               ├── activity/
│   │   │   │               │   ├── MainActivity.java
│   │   │   │               │   ├── SiteListActivity.java
│   │   │   │               │   └── AddSiteActivity.java
│   │   │   │               ├── adapter/
│   │   │   │               │   ├── PostAdapter.java
│   │   │   │               │   └── SiteAdapter.java
│   │   │   │               ├── data/
│   │   │   │               │   ├── model/
│   │   │   │               │   │   ├── WordPressPost.java
│   │   │   │               │   │   ├── CreatePostRequest.java
│   │   │   │               │   │   ├── UpdatePostRequest.java
│   │   │   │               │   │   └── WordPressSite.java
│   │   │   │               │   └── PrefsHelper.java
│   │   │   │               ├── network/
│   │   │   │               │   ├── ApiClient.java
│   │   │   │               │   ├── BasicAuthInterceptor.java
│   │   │   │               │   └── WordPressService.java
│   │   │   │               ├── repository/
│   │   │   │               │   └── WordPressRepository.java
│   │   │   │               └── viewmodel/
│   │   │   │                   └── WordPressViewModel.java
│   │   │   ├── res/
│   │   │   │   ├── layout/
│   │   │   │   │   ├── activity_main.xml
│   │   │   │   │   ├── activity_site_list.xml
│   │   │   │   │   ├── activity_add_site.xml
│   │   │   │   │   ├── item_post.xml
│   │   │   │   │   ├── item_site.xml
│   │   │   │   │   └── dialog_post_edit.xml
│   │   │   │   ├── menu/
│   │   │   │   │   └── main_menu.xml
│   │   │   │   ├── values/
│   │   │   │   │   ├── colors.xml
│   │   │   │   │   ├── strings.xml
│   │   │   │   │   ├── styles.xml
│   │   │   │   │   └── themes.xml
│   │   │   │   └── AndroidManifest.xml
│   │   │   └── assets/
│   │   └── test/           # Unit tests
│   ├── build.gradle        # Module-level build.gradle
│   └── proguard-rules.pro
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── build.gradle            # Project-level build.gradle
├── settings.gradle
├── gradle.properties
└── local.properties

Nice structure. Development of android app is accelerated from AI usage in this case DeepSeek.

And the following are the scaf-folding that would make the project work.

Project level build.gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:8.0.2'
        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20'
        
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

Model level build.gradle (app/build.gradle)

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}

android {
    namespace 'com.example.wordpressapp'
    compileSdk 33

    defaultConfig {
        applicationId "com.example.wordpressapp"
        minSdk 24
        targetSdk 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    buildFeatures {
        viewBinding true
    }
}

dependencies {
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.9.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'androidx.lifecycle:lifecycle-livedata:2.6.1'
    implementation 'androidx.lifecycle:lifecycle-viewmodel:2.6.1'
    implementation 'androidx.navigation:navigation-fragment:2.5.3'
    implementation 'androidx.navigation:navigation-ui:2.5.3'
    implementation 'androidx.recyclerview:recyclerview:1.3.1'
    
    // Retrofit & Gson
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.11.0'
    
    // Testing
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

settings.gradle

pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}
rootProject.name = "WordPressApp"
include ':app'

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.wordpressapp">

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

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.WordPressApp"
        tools:targetApi="31">

        <activity
            android:name=".activity.SiteListActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".activity.AddSiteActivity"
            android:exported="false" />

        <activity
            android:name=".activity.MainActivity"
            android:exported="false" />

    </application>

</manifest>

colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="purple_200">#FFBB86FC</color>
    <color name="purple_500">#FF6200EE</color>
    <color name="purple_700">#FF3700B3</color>
    <color name="teal_200">#FF03DAC5</color>
    <color name="teal_700">#FF018786</color>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>
    <color name="green">#FF4CAF50</color>
</resources>

strings.xml

<resources>
    <string name="app_name">WordPress App</string>
    <string name="menu_sites">Manage Sites</string>
    <string name="menu_switch_site">Switch Site</string>
    <string name="title_activity_main">WordPress Posts</string>
    <string name="title_activity_site_list">WordPress Sites</string>
    <string name="title_activity_add_site">Add/Edit Site</string>
</resources>

theme.xml

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Base.Theme.WordPressApp" parent="Theme.Material3.DayNight">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
    </style>

    <style name="Theme.WordPressApp" parent="Base.Theme.WordPressApp" />
</resources>

Create layout files

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/tvCurrentSite"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Current Site: None"
        android:textSize="16sp"
        android:layout_marginBottom="16dp"/>

    <Button
        android:id="@+id/btnAddPost"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Add New Post"
        android:layout_marginBottom="16dp"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rvPosts"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

activity_site_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="WordPress Sites"
        android:textSize="24sp"
        android:textStyle="bold"
        android:gravity="center"
        android:layout_marginBottom="16dp"/>

    <Button
        android:id="@+id/btnAddSite"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Add New Site"
        android:layout_marginBottom="16dp"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rvSites"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

activity_add_site.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="16dp">

        <EditText
            android:id="@+id/etSiteName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Site Name"
            android:layout_marginBottom="16dp"/>

        <EditText
            android:id="@+id/etBaseUrl"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Base URL (e.g., https://yoursite.com)"
            android:inputType="textUri"
            android:layout_marginBottom="16dp"/>

        <EditText
            android:id="@+id/etUsername"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Username"
            android:layout_marginBottom="16dp"/>

        <EditText
            android:id="@+id/etPassword"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Application Password"
            android:inputType="textPassword"
            android:layout_marginBottom="16dp"/>

        <CheckBox
            android:id="@+id/cbDefault"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Set as default site"
            android:layout_marginBottom="16dp"/>

        <Button
            android:id="@+id/btnSave"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Save Site"/>

    </LinearLayout>
</ScrollView>

After the long session of implementing with the DeepSeek recommendation. Click on the build button.

The result of the API prompt is a disaster. Gradle error.

The major roadblock that prevents further development are due to my zero proficiency on using Android Studio. An AI that needs to have integration into Android Studio is needed.

Step 3 – Pivot from DeepSeek to Gemini AI free tier

Activated the Android Studio 11 free AI

Major roadblock adverted using the Gemini AI which can be activated as long as you have a google account.

Use the Step 2 with more specific task that can be solved with as little request as little token as free tier is limited.

Long story short, focus to resolve gradle error issue. Then, add incremental feature into the app, allow offline saving of post if device has no internet connection or network issue. Finally, add WYSIWYG into the WordPress App editor.

The results are clear, a code that is Android Studio 11 ready, and ready for release in Bitbucket. KarMeng / wordpressclient — Bitbucket

Step 4 – Revisit DeepSeek to perform further road map planning for the WordPress App

Part of roadmap planning, understand own WordPress app and the official WordPress

Result of reviewing, this allows the developer of the app, in this case me to focus development on top which feature.

Extra tip document using git repository

Use the git repository such as BitBucket or GitHub or your favourite Git repo to track your progress. This allow good documentation and would allow developer to know what was change with each feature enhancement. Good documentation of change in Git will allow inexperience or novice developers to understand the code better.

Graphic representation of commit from all branches from SourceTree

And this is how I used AI assistance to develop a product from a known pain point as a WordPress user.

Leave a Reply

Your email address will not be published. Required fields are marked *