Dedicated Server

How to convert any blogger blog into a native android app ?

A lot of bloggers would like to convert Blogger blog into native android app. Of course we could view our blog in a mobile web browser if we setup a mobile responsive template, but what if we have to customise our blog's look and feel? What if we have to support additional native features like share to whatsapp ?

We may also make use of a WebView to just load the webpage inside the app, but this provides less chances of customisation, or we may make use of any hybrid app platform to convert blogger blog into native android app.

In fact we have access to some of the blog contents in the form of json apis. You could check here for more details. You could then club the blogger functionalities with other native android features as well.


In this tutorial let us try to make a simple native android app which helps us to view our blog posts in a customised layout.




Try the complete app here  or you may check the source code in bitbucket here

This is the json endpoint that we are going to use http://www.your_blog.com/feeds/posts/default?alt=json
Here you will have to replace www.your_blog.com  with your blog url(remember this only works with a blogger blog)

Now let us try some code

The following dependencies will be used :

compile 'com.android.support:cardview-v7:27.1.0'
compile 'com.android.support:design:27.1.0'
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.picasso:picasso:2.71828'
compile 'me.gujun.android.taggroup:library:1.4@aar'

The following is the tree view of the json that we are about to parse


Here the key "entry" is the json array that we are about to parse to get the list of posts. It is basically having the following json structure


{
"feed": {
  "entry" : [
  {
    "id":{..},
    "published":{..},
    "updated":{..},
    "category":{..},
    "tile":{..},
    "content":{..}
  }
]
}
}

Here we will be parsing the title, content objects (which obviously holds the title and content of each post)

First create a layout for  recyclerview in  like this


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ProgressBar
        android:id="@+id/progress"
        android:visibility="gone"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</RelativeLayout>


Now define the recyclerview in your activity like this
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

You should also declare and initialise your progress bar (refer attached source code for the same)

Now create the following POJO classes

BlogResponse.java

public class BlogResponse {
    @SerializedName("feed")
    @Expose
    private BlogFeed blogFeed;

    public BlogFeed getBlogFeed() {
        return blogFeed;
    }

    public void setBlogFeed(BlogFeed blogFeed) {
        this.blogFeed = blogFeed;
    }
}

BlogFeed.java


public class BlogFeed {
    @SerializedName("entry")
    @Expose
    private ArrayList<Post> post = null;

    public ArrayList<Post> getPost() {
        return post;
    }

    public void setPost(ArrayList<Post> post) {
        this.post = post;
    }
}

Post.java
public class Post {
    @SerializedName("category")
    @Expose
    private List<Category> category = null;
    @SerializedName("title")
    @Expose
    private Title title;
    @SerializedName("content")
    @Expose
    private Content content;
    @SerializedName("media$thumbnail")
    @Expose
    private Media$thumbnail media$thumbnail;

    public List<Category> getCategory() {
        return category;
    }

    public void setCategory(List<Category> category) {
        this.category = category;
    }

    public Title getTitle() {
        return title;
    }

    public void setTitle(Title title) {
        this.title = title;
    }

    public Content getContent() {
        return content;
    }

    public void setContent(Content content) {
        this.content = content;
    }

    public Media$thumbnail getMedia$thumbnail() {
        return media$thumbnail;
    }

    public void setMedia$thumbnail(Media$thumbnail media$thumbnail) {
        this.media$thumbnail = media$thumbnail;
    }
}

Category.java
public class Category implements Parcelable {
    @SerializedName("scheme")
    @Expose
    private String scheme;
    @SerializedName("term")
    @Expose
    private String term;

    public Category(Parcel in) {
        this.scheme = in.readString();
        this.term = in.readString();
    }

    public String getScheme() {
        return scheme;
    }

    public void setScheme(String scheme) {
        this.scheme = scheme;
    }

    public String getTerm() {
        return term;
    }

    public void setTerm(String term) {
        this.term = term;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(scheme);
        parcel.writeString(term);
    }

    public static final Parcelable.Creator<Category> CREATOR = new Parcelable.Creator<Category>() {
        @Override
        public Category createFromParcel(Parcel in) {
            return new Category(in);
        }

        @Override
        public Category[] newArray(int size) {
            return new Category[size];
        }
    };
}

Title.java
public class Title {
    @SerializedName("type")
    @Expose
    private String type;
    @SerializedName("$t")
    @Expose
    private String $t;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String get$t() {
        return $t;
    }

    public void set$t(String $t) {
        this.$t = $t;
    }

}

Content.java
public class Content {
    @SerializedName("type")
    @Expose
    private String type;
    @SerializedName("$t")
    @Expose
    private String $t;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String get$t() {
        return $t;
    }

    public void set$t(String $t) {
        this.$t = $t;
    }
}

If you find it difficult to understand how the above POJO classes were generated, please go here and check as i have already explained it in one of my previous posts

You may also use http://www.jsonschema2pojo.org/ where you can automatically generate your pojo classes by pasting in your response.

Now let us look into our retrofit network call


 private void getBlog() {
        Gson gson = new GsonBuilder()
                .setLenient()
                .create();
        Retrofit retrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create(gson))
                .baseUrl(blog_url)
                .build();
        RequestInterface requestInterface = retrofit.create(RequestInterface.class);
        Call<BlogResponse> call = requestInterface.getBlogData();
        call.enqueue(new Callback<BlogResponse>() {
            @Override
            public void onResponse(Call<BlogResponse> call, Response<BlogResponse> response) {
                progress.setVisibility(View.GONE);
                Toast.makeText(MainActivity.this, "success!", Toast.LENGTH_SHORT).show();
                if (response.isSuccessful()) {
                    if (response.body() != null) {
                        BlogFeed blogFeed = response.body().getBlogFeed();
                        posts = blogFeed.getPost();
                        postAdapter = new PostAdapter(posts, MainActivity.this);
                        recyclerView.setAdapter(postAdapter);
                        SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
                        editor.putString("blog", blog_url);
                        editor.apply();
                    }
                } else {
                    Toast.makeText(MainActivity.this, "Oops Something went wrong!", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<BlogResponse> call, Throwable t) {
                progress.setVisibility(View.GONE);
                Toast.makeText(MainActivity.this, "Failure", Toast.LENGTH_SHORT).show();
            }
        });
    }

Here we have fetched all our posts to the arraylist like this posts = blogFeed.getPost(); .We can now pass this arraylist to our adapter. So we need to create a recyclerview adapter that accepts a context and above mentioned arraylist in its constructor. See below code for our recyclerview adapter

PostAdapter.java

public class PostAdapter extends RecyclerView.Adapter<PostAdapter.ViewHolder> {
    private ArrayList<Post> records=new ArrayList<>();
    private Context context;
    private ArrayList<Category> category = new ArrayList<>();


    public PostAdapter(ArrayList<Post> records, Context context) {
        this.context=context;
        this.records=records;
    }

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

    @Override
    public void onBindViewHolder(PostAdapter.ViewHolder holder, final int position) {

        if (records.get(position).getCategory()!=null) {
             category = new ArrayList<>(records.get(position).getCategory());
        }

        holder.title.setText(records.get(position).getTitle().get$t());
        holder.desc.setText(Html.fromHtml(records.get(position).getContent().get$t()));
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Bundle bundle = new Bundle();

                if (category!=null)
                bundle.putParcelableArrayList("category", category);

                Intent intent=new Intent(context,PostDetails.class);
                intent.putExtra("title",records.get(position).getTitle().get$t());
                intent.putExtra("desc",records.get(position).getContent().get$t());
                intent.putExtras(bundle);
                context.startActivity(intent);

            }
        });

    }

    @Override
    public int getItemCount() {
        return records.size();
    }


    public class ViewHolder extends RecyclerView.ViewHolder {
        private TextView title,desc;
        private ImageView post_image;
        public ViewHolder(View view) {
            super(view);

            post_image=(ImageView)view.findViewById(R.id.post_image);
            title=(TextView)view.findViewById(R.id.title);
            desc=(TextView)view.findViewById(R.id.desc);
        }
    }
}

Thats it! you are now all set to generate a native android app for your blogger blog. If you are a blogger without any prior programming knowledge, just download the app from here and set the url to your blog url. The app will start functioning with your blog's posts

21 comments:

  1. How to add images in the listing page and details page ? How can I directly show the web view ?

    ReplyDelete
    Replies
    1. If you use a webview, you wont be able to customise the layout as per your needs. Also while using webview you dont need the json response format, you can just use mWebView.loadUrl(“your blog url here”);

      Delete
    2. Also inorder to add images, check inside your json’s “key” and see if any image links are present. If so, parse the link value like we did for title and content. Once parsed, use a library like picasso or glide to display it in an imageview

      Delete
  2. I need to display posts from each category. Can you please help me to do it?

    ReplyDelete
    Replies
    1. For that, you will need to generate an api key by visiting here https://developers.google.com/blogger/docs/3.0/using , then you will need to pass the API key and label name(category) here https://www.googleapis.com/blogger/v3/blogs/2399953/posts/search?q=label:label1&key=THE-KEY (replace label1 with your category name and THE-KEY with your API key)

      Delete
    2. Thanks for your quick reply. I want to display pictures present in my posts as well. Pictures are not getting displayed. (Shows as cyan boxes)

      Delete
    3. what library are you using for displaying image responses, also can you share one of your image urls?

      Delete
  3. I'm not using any libraries. I want to show images present inside my blog posts. Like we parsed the text.

    ReplyDelete
    Replies
    1. you can use a dependency like picasso to load the image url into an imageview. First of all add this dependency to your build.gradle file >> implementation 'com.squareup.picasso:picasso:2.71828' and then try this code >> Picasso.get().load("pass img url here").into(imageView);

      Delete
    2. you may check this link for picasso documentation >> https://square.github.io/picasso/

      Delete
  4. If i make this app then i upload it to play store with ads

    ReplyDelete
  5. Hi Navneet,

    At the end of the blog post you wrote change url to blog url. Can you confirm where to change it? Does that mean this is the only change required to convert a blog posts to a app?

    ReplyDelete
    Replies
    1. yes that would be the only change required, first unzip the project and open the project using android studio, then goto the mainactivity.java file, look for the method getBlog() and place your blog url like ".baseUrl(blog_url)"

      Delete
    2. Thank you Navneet. Once the blog url is edited, can I rename the entire folder and convert it to .APK format to upload it to playstore? If yes how do I convert zip to apk format using android studio?

      Are there any additional steps?

      Delete
    3. Once you have edited the url, try running the project in a real device/emulator. After that, in android studio top menu, go to build>>Build bundles/apk to generate an apk. But inorder to upload it to playstore, you will need to generate a signed apk file, for which you will need to go to build>>generate signed build option.

      Delete
  6. Thank you so much. It is working. So every time there is a new post on the blogger, will it get updated in the app as well?

    I have tested it with emulator and it asked me to enter blog URL. Is it the same for users who would like to install app through play store? or will they be able to see it by default.

    ReplyDelete
  7. Useful post! I would love to use a Native Mobile App Builder and convert my blog into a native android app and provide it to my readers.

    ReplyDelete
  8. Great blog.you put Good stuff.All the topics were explained briefly.so quickly understand for me.I am waiting for your next fantastic blog.Thanks for sharing.Any coures related details learn...
    Blockchain Technology

    ReplyDelete
  9. Is this a paid topic or do you change it yourself?
    However, stopping by with great quality writing, it's hard to see any good blog today.
    https://licensedinfo.com/
    Graphics Converter Pro
    Ashampoo Snap

    ReplyDelete