Ahmad Naser Turnkey Ecosystem Ahmad Naser
May 9, 2018

Android WordPress Blog Project Kotlin Volley, retrofit2, Gson, Okhttp, Picasso, Ahbottomnavigation

Published in the Ahmad Naser ecosystem. Estimated reading time: 21 minutes.

We are going to use multiple libraries and backend services to build Our wordpress blog app such as Android, GBEAPI, WordPress, Kotlin, Volley,HTML,CSS,JS, retrofit2, Gson, Okhttp, Picasso, Ahbottomnavigation.

Follow the following steps:

1-reserve your WordPress hosting from greenbackend and get your wordpress greenbackend api license
http://greenbackend.com/developer-hosting

2-create a new android project using android studio and replace grandle build with the following gradle build file

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.ahmadnaser.gbeapiswpblogdemo"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    implementation 'com.android.support:support-v4:26.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    implementation 'com.android.support:cardview-v7:26.1.0'
    //3rd party libs
    implementation 'com.squareup.okhttp3:okhttp:3.9.1'
    implementation 'com.google.code.gson:gson:2.8.0'
    implementation 'com.squareup.picasso:picasso:2.5.2'
    implementation 'com.aurelhubert:ahbottomnavigation:1.3.3'
    implementation "com.squareup.retrofit2:retrofit:2.3.0"
    implementation "com.squareup.retrofit2:adapter-rxjava2:2.3.0"
    implementation "com.squareup.retrofit2:converter-gson:2.3.0"
    implementation 'com.android.volley:volley:1.0.0'
}

3-Create a new class for app

GBEAPISDemo

package com.ahmadnaser.gbeapiswpblogdemo

//package com.ahmadnaser.gbeapisdemo
///Developed By Ahmad Naser Turnkey Solutions LLC - Greenbackend.com
///http://greenbackend.com/developer-hosting
///www.greenbackend.com
// www.khottah.com
///greenbackend@ahmadnaser.com
///copyright © 2018 ANTS LLC. All rights reserved.


import android.app.Application
import com.android.volley.Request
import com.android.volley.RequestQueue
import com.android.volley.toolbox.Volley


class GBEAPISDemo:Application(){


    companion object {
        private val TAG = GBEAPISDemo::class.java.simpleName 

        //define signleton app
        @get:Synchronized var instance: GBEAPISDemo? = null
            private set
    }

    val requestQueue: RequestQueue? = null
        get() {
            if(field == null){
                return  Volley.newRequestQueue(applicationContext)
            }
            return field
        }

    override fun onCreate() { 


        super.onCreate()
        instance = this
    }


    fun <T> addToRequestQueue(request: Request<T>){
        request.tag = TAG
        requestQueue?.add(request)
    }




}

4-Add app and permissions to your manifest file

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

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

    <application
        android:name="com.ahmadnaser.gbeapiswpblogdemo.GBEAPISDemo"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name="com.ahmadnaser.gbeapiswpblogdemo.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
   
    </application>

</manifest>

5-create main content in res/layout
content_main.xml

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

</LinearLayout>

6-add AHBottomNavigation to main activity layout and include the main content
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.ahmadnaser.gbeapiswpblogdemo.MainActivity">

    <include layout="@layout/content_main" />


    <com.aurelhubert.ahbottomnavigation.AHBottomNavigation
        android:id="@+id/navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom" />

</android.support.design.widget.CoordinatorLayout>

7- Add Tabs to MainActivity,

package com.ahmadnaser.gbeapiswpblogdemo

import android.graphics.Color
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.ahmadnaser.gbeapiswpblogdemo.R
import com.ahmadnaser.gbeapiswpblogdemo.fragments.CategoryFragment
import com.ahmadnaser.gbeapiswpblogdemo.fragments.FavFragment
import com.ahmadnaser.gbeapiswpblogdemo.fragments.HomeFragment
import com.aurelhubert.ahbottomnavigation.AHBottomNavigation
import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity(), AHBottomNavigation.OnTabSelectedListener {
    override fun onTabSelected(position: Int, wasSelected: Boolean): Boolean {

        return true
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        navigation.setOnTabSelectedListener(this)
        this.createNavItems()

    }

    private fun createNavItems() {
        //CREATE ITEMS
        val home = AHBottomNavigationItem("Home", R.drawable.ic_home_black_48dp)
        val category = AHBottomNavigationItem("Category", R.drawable.ic_list_black_48dp)

        //ADD PROPERTIES
        navigation.addItem(home)
        navigation.addItem(category)
      

        //SET PROPERTIES
        navigation.defaultBackgroundColor = Color.parseColor("#ffffff")
        navigation.accentColor = Color.parseColor("#FFC107")
        navigation.inactiveColor = Color.parseColor("#BDBDBD")

        navigation.currentItem = 0
    }
}

8- Create Config file for the urls
ANConfig Class

    companion object {

        val GBE_CAT_SINGLE_URL = "http://greenapis.com/gbe-api/v1/category/"
        val GBE_ApiKey = "ApiKey"
        val GBE_ApiKey_Value = "******"

    }

9-create HomeFragment

package com.ahmadnaser.gbeapiswpblogdemo.fragments


import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.ahmadnaser.gbeapiswpblogdemo.HomeAdapter
import com.ahmadnaser.gbeapiswpblogdemo.LatestPosts
import com.ahmadnaser.gbeapiswpblogdemo.R
import com.google.gson.GsonBuilder
import kotlinx.android.synthetic.main.fragment_home.*
import okhttp3.*
import java.io.IOException


class HomeFragment : Fragment() {


    companion object {
        fun newInstance(): HomeFragment {
            var fragmentHome = HomeFragment()
            return fragmentHome
        }
    }


    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        // Inflate the layout for this fragment
        val rootView = inflater!!.inflate(R.layout.fragment_home, container, false)

        val rv = rootView.findViewById<View>(R.id.recyclerView_home) as RecyclerView
        rv.layoutManager = LinearLayoutManager(newInstance().context)
        return rootView
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        fetchJson()
    }


    //Fetch JSON Data
    private fun fetchJson() {

        try {
            println("Attempting to Fetch JSON")

            //REST API Url
            val url = "http://greenapis.com/gbe-api/v1/latest/"

            val request = Request.Builder().url(url).addHeader("ApiKey","****").build()

            val client = OkHttpClient()
            client.newCall(request).enqueue(object : Callback {
                override fun onResponse(call: Call?, response: Response?) {
                    val body = response?.body()?.string()
                    println(body)

                    val gson = GsonBuilder().create()

                    val homeFeed = gson.fromJson(body, LatestPosts::class.java)




if(activity != null) {
    Thread {

        activity.runOnUiThread { recyclerView_home.adapter = HomeAdapter(homeFeed) }
    }.start()

}
                }

                override fun onFailure(call: Call?, e: IOException?) {
                    println("Failed to execute request")
                }
            })

        }
        catch (e: Exception) {
            // handler
        }
        finally {
            // optional finally block
        }






    }

}



fragment_home.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="blogapp.bittupatel.in.kotlinblogapp.com.ahmadnaser.gbeapiswpblogdemo.fragments.HomeFragment">

    <!-- TODO: Update blank fragment layout -->
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView_home"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </android.support.v7.widget.RecyclerView>

</FrameLayout>

10-Create Models Posts, and View post_row.xml
Models.kt

 class LatestPosts(val feed: List<Feed>) {

     var total_items:Int = 0
     var total_pages:Int = 0



}

class Feed(	   val id : Int,
               val category : String,
               val name : String,
               val description : String,
               val image : String,
               val image_big : String,
               val status : String,
               val profilePic : String,
               val timeStamp : Long,
               val author : String,
               val url : String,
               val is_protected : Boolean,
               val post_password : String   )

post_row.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.CardView
        android:id="@+id/cardView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:minHeight="250dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0">

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

            <ImageView
                android:id="@+id/postThumbnail"
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:scaleType="centerCrop"
                app:srcCompat="@mipmap/ic_launcher" />

            <TextView
                android:id="@+id/postTitle"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_margin="5dp"
                android:text="Post Title"
                android:textAppearance="@style/TextAppearance.AppCompat.Medium"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/authorName"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginBottom="5dp"
                android:layout_marginLeft="5dp"
                android:layout_marginRight="5dp"
                android:text="Author Name"
                android:textAppearance="@style/TextAppearance.Compat.Notification.Info" />

        </LinearLayout>


    </android.support.v7.widget.CardView>


</android.support.constraint.ConstraintLayout>

11-Holder for HomeAdapter

package com.ahmadnaser.gbeapiswpblogdemo

import android.content.Intent
import android.support.v7.widget.RecyclerView
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import com.ahmadnaser.gbeapiswpblogdemo.R


class CustomHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    var post: Feed? = null
    var postTitle: TextView = itemView.findViewById<View>(R.id.postTitle) as TextView
    var authorName: TextView = itemView.findViewById<View>(R.id.authorName) as TextView
    var thumbnailImageView: ImageView = itemView.findViewById<View>(R.id.postThumbnail) as ImageView

    companion object {
        val POST_ID = "POST_ID"
        val POST_IMAGE_KEY = "POST_IMAGE"
        val POST_TITLE_KEY = "POST_TITLE"
        val POST_AUTHOR_KEY = "POST_AUTHOR"
        val POST_DATE_KEY = "POST_DATE"
        val POST_CONTENT_KEY = "POST_CONTENT"

    }

    init {
        itemView.setOnClickListener {
            val intent = Intent(itemView.context, PostDetail::class.java)

            intent.putExtra(POST_ID, post?.id)
            intent.putExtra(POST_IMAGE_KEY, post?.image)
            intent.putExtra(POST_TITLE_KEY, post?.name)
            intent.putExtra(POST_AUTHOR_KEY, post?.author)
            intent.putExtra(POST_DATE_KEY, post?.timeStamp.toString())
            intent.putExtra(POST_CONTENT_KEY, post?.description)

            itemView.context.startActivity(intent)
        }
    }
}
package com.ahmadnaser.gbeapiswpblogdemo

import android.support.v7.widget.RecyclerView
import android.text.Html
import android.view.LayoutInflater
import android.view.ViewGroup
import com.ahmadnaser.gbeapiswpblogdemo.R
import com.squareup.picasso.Picasso


class HomeAdapter(val homeFeed: LatestPosts) : RecyclerView.Adapter<CustomHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomHolder {
        val v = LayoutInflater.from(parent.context).inflate(R.layout.post_row, parent, false)
        return CustomHolder(v)
    }

    override fun onBindViewHolder(holder: CustomHolder?, position: Int) {



        try {
            val post = homeFeed.feed!!.get(position)

            holder?.postTitle?.text = Html.fromHtml(post.name)
            holder?.authorName?.text = post.timeStamp.toString() + "  •  " + post.author

            Picasso.with(holder?.itemView?.context).load(post.image).into(holder?.thumbnailImageView)

            holder?.post = post
        }
        catch (e: Exception) {
            // handler
        }
        finally {
            // optional finally block
        }




    }

    override fun getItemCount(): Int {
        return homeFeed.feed.count()
    }
}

12-Call the fragment from main

class MainActivity : AppCompatActivity(), AHBottomNavigation.OnTabSelectedListener {
    override fun onTabSelected(position: Int, wasSelected: Boolean): Boolean {
        when (position) {
            0 -> {
                val fragment = HomeFragment.newInstance()
                supportFragmentManager.beginTransaction().replace(R.id.container, fragment).commit()
            }

        }
        return true
    }

13-create CategoryFragment

package com.ahmadnaser.gbeapiswpblogdemo.fragments


import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.ahmadnaser.gbeapiswpblogdemo.CategoryAdapter
import com.ahmadnaser.gbeapiswpblogdemo.CategoryFeed
import com.ahmadnaser.gbeapiswpblogdemo.R
import com.google.gson.GsonBuilder
import kotlinx.android.synthetic.main.fragment_category.*
import okhttp3.*
import java.io.IOException

class CategoryFragment : Fragment() {

    companion object {
        fun newInstance(): CategoryFragment {
            val fragmentCat = CategoryFragment()
            return fragmentCat
        }
    }

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        // Inflate the layout for this fragment
        val rootView = inflater!!.inflate(R.layout.fragment_category, container, false)

        val rv = rootView.findViewById<View>(R.id.recyclerView_cat) as RecyclerView
        rv.layoutManager = LinearLayoutManager(newInstance().context)

        return rootView
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        fetchJson()
    }

    //Fetch JSON Data
    private fun fetchJson() {
        println("Attempting to Fetch JSON")

        //REST API Url
        val url = "http://greenapis.com/gbe-api/v1/categories"

        val request = Request.Builder().url(url).addHeader("ApiKey","****").build()


        val client = OkHttpClient()
        client.newCall(request).enqueue(object : Callback {
            override fun onResponse(call: Call?, response: Response?) {
                val body = response?.body()?.string()
                println(body)

                val gson = GsonBuilder().create()

                val categoryFeed = gson.fromJson(body, CategoryFeed::class.java)

                activity.runOnUiThread {
                    recyclerView_cat.adapter = CategoryAdapter(categoryFeed)
                }
            }

            override fun onFailure(call: Call?, e: IOException?) {
                println("Failed to execute request")
            }
        })
    }
}

fragment_category.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.ahmadnaser.gbeapiswpblogdemo.fragments.CategoryFragment">

    <!-- TODO: Update blank fragment layout -->

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView_cat"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>

14- Category Models, CategoryAdapter and CategoryHolder and category_row.xml

class CategoryFeed (

        val menu_mode : String,
        val categories : List<Categories>,
        val featured_categories : List<Featured_categories>,
        val custom_menu : List<String>
) 


class Featured_categories (

        val term_id : Int,
        val name : String
)
class Categories (

        val term_id : Int,
        val name : String
)
package com.ahmadnaser.gbeapiswpblogdemo

import android.content.Intent
import android.support.v7.widget.RecyclerView
import android.text.Html
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import com.ahmadnaser.gbeapiswpblogdemo.R


class CategoryAdapter(val categoryFeed: CategoryFeed) : RecyclerView.Adapter<CategoryHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryHolder {
        val v = LayoutInflater.from(parent.context).inflate(R.layout.category_row, parent, false)
        return CategoryHolder(v)
    }

    override fun onBindViewHolder(holder: CategoryHolder, position: Int) {
        val category = categoryFeed.categories[position]
       // holder.categoryTitle.text = category.name
         holder.categoryTitle.text = Html.fromHtml(category.name).toString();

        holder.imageView_cat.alpha = 0.3f
        when (category.name) {
            "Beauty" -> {
                holder.imageView_cat.setBackgroundResource(R.drawable.blog)
            }
            "Business" -> {
                holder.imageView_cat.setBackgroundResource(R.drawable.cyber_attacks)
            }
            "Fashion" -> {
                holder.imageView_cat.setBackgroundResource(R.drawable.cyber_news)
            }
            "Hair" -> {
                holder.imageView_cat.setBackgroundResource(R.drawable.cyber_security)
            }
            "Health &amp; Fitness" -> {
                holder.imageView_cat.setBackgroundResource(R.drawable.did_you_know)
            }
            "Life &amp; Love" -> {
                holder.imageView_cat.setBackgroundResource(R.drawable.network_security)
            }
            "Makeup &amp; Skincare" -> {
                holder.imageView_cat.setBackgroundResource(R.drawable.new_release)
            }
            "Photography" -> {
                holder.imageView_cat.setBackgroundResource(R.drawable.research)
            }
            "Relationships" -> {
                holder.imageView_cat.setBackgroundResource(R.drawable.tech)
            }
        }

        holder.category = category
    }

    override fun getItemCount(): Int {
        return categoryFeed.categories.count()
    }
}

class CategoryHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    var category: Categories? = null
    var imageView_cat: ImageView = itemView.findViewById<View>(R.id.imageView_cat) as ImageView
    var categoryTitle: TextView = itemView.findViewById<View>(R.id.categoryTitle) as TextView

    companion object {

        val CATEGORY_ID_KEY = "CATEGORY_ID"
        val CATEGORY_TITLE_KEY = "CATEGORY_TITLE"

    }

    init {
        itemView.setOnClickListener {
            val intent = Intent(itemView.context, CategoryDetail::class.java)

            intent.putExtra(CATEGORY_ID_KEY, category?.term_id)
            intent.putExtra(CATEGORY_TITLE_KEY, category?.name)

            itemView.context.startActivity(intent)
        }
    }
}

category_row.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.CardView
        android:id="@+id/cardView_cat"
        android:layout_width="0dp"
        android:layout_height="120dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        app:cardCornerRadius="5dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

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

            <ImageView
                android:id="@+id/imageView_cat"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerInside" />

            <TextView
                android:id="@+id/categoryTitle"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_alignBottom="@+id/imageView_cat"
                android:layout_alignLeft="@+id/imageView_cat"
                android:layout_alignRight="@+id/imageView_cat"
                android:layout_alignTop="@+id/imageView_cat"
                android:layout_margin="1dp"
                android:gravity="center"
                android:text="Hello"
                android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
        </RelativeLayout>
    </android.support.v7.widget.CardView>

</android.support.constraint.ConstraintLayout>

15-Call Category Fragment in MainActivity

    override fun onTabSelected(position: Int, wasSelected: Boolean): Boolean {
        when (position) {
            0 -> {
                val fragment = HomeFragment.newInstance()
                supportFragmentManager.beginTransaction().replace(R.id.container, fragment).commit()
            }
            1 -> {
                val fragment = CategoryFragment.newInstance()
                supportFragmentManager.beginTransaction().replace(R.id.container, fragment).commit()
            }
        }
        return true
    }

16- CategoryDetailActivity, CategoryDetailAdapter, activity_post_detail.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.ahmadnaser.gbeapiswpblogdemo.CategoryDetail">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView_catDetail"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </android.support.v7.widget.RecyclerView>

</android.support.constraint.ConstraintLayout>



package com.ahmadnaser.gbeapiswpblogdemo

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import android.text.Html
import com.ahmadnaser.gbeapiswpblogdemo.R
import com.google.gson.GsonBuilder
import kotlinx.android.synthetic.main.activity_category_detail.*
import okhttp3.*
import java.io.IOException

class CategoryDetail : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_category_detail)

        recyclerView_catDetail.layoutManager = LinearLayoutManager(this)
        val intent = intent
        val navBarTitle = intent.getStringExtra(CategoryHolder.CATEGORY_TITLE_KEY)
        supportActionBar?.title =  Html.fromHtml(navBarTitle).toString();
        val id = intent.getIntExtra(CategoryHolder.CATEGORY_ID_KEY, 0)

        //REST API Url
        val url = "http://greenapis.com/gbe-api/v1/category/" + id
        val request = Request.Builder().url(url).addHeader("ApiKey","****").build()

      //  val request = Request.Builder().url(url).build()

        val client = OkHttpClient()
        client.newCall(request).enqueue(object : Callback {
            override fun onResponse(call: Call?, response: Response?) {
                val body = response?.body()?.string()
                println(body)

                val gson = GsonBuilder().create()

                val homeFeed = gson.fromJson(body, LatestPosts::class.java)

                runOnUiThread {
                    recyclerView_catDetail.adapter = CategoryDetailAdapter(homeFeed)
                }


            }

            override fun onFailure(call: Call?, e: IOException?) {
                println("Failed to execute request")
            }
        })

    }

}





package com.ahmadnaser.gbeapiswpblogdemo

import android.support.v7.widget.RecyclerView
import android.text.Html
import android.view.LayoutInflater
import android.view.ViewGroup
import com.ahmadnaser.gbeapiswpblogdemo.R
import com.squareup.picasso.Picasso


class CategoryDetailAdapter(val homeFeed: LatestPosts) : RecyclerView.Adapter<CustomHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomHolder {
        val v = LayoutInflater.from(parent.context).inflate(R.layout.post_row, parent, false)
        return CustomHolder(v)
    }

    override fun onBindViewHolder(holder: CustomHolder?, position: Int) {


        try {
            val post = homeFeed.feed.get(position)
            holder?.postTitle?.text = Html.fromHtml(post.name)
            holder?.authorName?.text = post.timeStamp.toString() + "  •  " + post.author

            Picasso.with(holder?.itemView?.context).load(post.image).into(holder?.thumbnailImageView)

            holder?.post = post
        }
        catch (e: Exception) {
            // handler
        }
        finally {
            // optional finally block
        }





    }

    override fun getItemCount(): Int {
        return homeFeed.feed.count()
    }
}


//class CategoryDetailHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
//    var post: Post? = null
//
//    var postTitle: TextView = itemView.findViewById<View>(R.id.postTitle) as TextView
//    var authorName: TextView = itemView.findViewById<View>(R.id.authorName) as TextView
//    var thumbnailImageView: ImageView = itemView.findViewById<View>(R.id.postThumbnail) as ImageView
//
//    companion object {
//
//        val POST_IMAGE_KEY = "POST_IMAGE"
//        val POST_TITLE_KEY = "POST_TITLE"
//        val POST_AUTHOR_KEY = "POST_AUTHOR"
//        val POST_DATE_KEY = "POST_DATE"
//        val POST_CONTENT_KEY = "POST_CONTENT"
//
//    }
//
//    init {
//        itemView.setOnClickListener {
//            val intent = Intent(itemView.context, PostDetail::class.java)
//
//            intent.putExtra(POST_IMAGE_KEY, post?.attachments!![0].url)
//            intent.putExtra(POST_TITLE_KEY, post?.title_plain)
//            intent.putExtra(POST_AUTHOR_KEY, post?.author?.name)
//            intent.putExtra(POST_DATE_KEY, post?.date)
//            intent.putExtra(POST_CONTENT_KEY, post?.content)
//
//            itemView.context.startActivity(intent)
//        }
//    }
//}

17- FeedDetails, PostDetailActivity, CategoryDetailAdapter, activity_post_detail.xml

package com.ahmadnaser.gbeapiswpblogdemo

class FeedDetails(
        val id : Int,
val name : String,
val image : String,
val thumbnail : String,
val category : String,
val description : String,
val profilePic : String,
val timeStamp : Long,
val url : String,
val load_url : String,
val load_external_view : Boolean,
val author : String,
val can_comment : String,
val comments : Int,
val story_content : String,
val is_protected : Boolean,
val post_password : String,
val urls : List,
val matches : List>,
val images : String)




import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.text.Html
import android.view.View
import android.widget.Toast

import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.activity_post_detail.*
import org.json.JSONObject
import com.android.volley.Request
import com.android.volley.Response
import com.android.volley.toolbox.JsonObjectRequest
import com.android.volley.AuthFailureError
import com.ahmadnaser.gbeapiswpblogdemo.R


class PostDetail : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_post_detail)

        // we'll change the nav bar title.
        val intent = intent
        val navBarTitle = intent.getStringExtra(CustomHolder.POST_TITLE_KEY)
        supportActionBar?.title = navBarTitle


        val thumbnail = intent.getStringExtra(CustomHolder.POST_IMAGE_KEY)
        Picasso.with(this).load(thumbnail).into(postThumbnail)

        val title = intent.getStringExtra(CustomHolder.POST_TITLE_KEY)
        postTitle.text = Html.fromHtml(title)

        val author = intent.getStringExtra(CustomHolder.POST_AUTHOR_KEY)
        val date = intent.getStringExtra(CustomHolder.POST_DATE_KEY)
        authorName.text = date + "  •  " + author

        // val content = intent.getStringExtra(CustomHolder.POST_CONTENT_KEY)


        fetchJsonContent(intent.getIntExtra(CustomHolder.POST_ID,0),"http://greenapis.com/gbe-api/v1/post/")

    }





    fun  fetchJsonContent(postId:Int,URL:String) {


        var url = URL +  postId
        prgsEmployeeUpdate.visibility = View.VISIBLE

        val jsonObjReq = object : JsonObjectRequest(Request.Method.GET,
                url, null,
                Response.Listener<JSONObject> { response ->



                    try {
                        prgsEmployeeUpdate.visibility = View.GONE
                        println(response.toString())
                        contentBlock!!.settings.loadsImagesAutomatically = true
                        contentBlock!!.settings.javaScriptEnabled = true


                      //  post_content.setVisibility(View.GONE)
                        contentBlock.setVisibility(View.VISIBLE)

                        var post_con = "<!DOCTYPE html>" +
                                "<html lang=\"ar\">" +
                                "  <head>" +
                                "    <meta charset=\"utf-8\">" +
                                "  </head>" +
                                "  <body>" +
                                "    #content# " +
                                "  </body>" +
                                "</html>"
                        try {
                            val in_s = resources.openRawResource(R.raw.post_format_rtl)
                            val b = ByteArray(in_s.available())
                            in_s.read(b)
                            post_con = String(b)
                        } catch (e: Exception) {
                            e.printStackTrace()
                        }


                        post_con = post_con.replace("#title#", response.getString("name"))
                        post_con = post_con.replace("#content#", response.getString("story_content"))
                        //post_contentHTML.loadData(post_con, "text/html; charset=utf-8", "utf-8");

                        if (response.has("load_external_view") && response.getBoolean("load_external_view") === java.lang.Boolean.TRUE) {
                           // contentBlock.loadUrl(response.getString("load_url")) //not webview
                        } else {
                            contentBlock.loadDataWithBaseURL(
                                    null,
                                    post_con,
                                    "text/html",
                                    "UTF-8", null)
                        }


                    }catch (ex:Exception){
                        prgsEmployeeUpdate.visibility = View.GONE
                        Toast.makeText(applicationContext,"Error " + ex.message, Toast.LENGTH_LONG).show()
                    }



                },
                Response.ErrorListener {
                    //Failure Callback
                }) {

            /** Passing some request headers*  */
            @Throws(AuthFailureError::class)
            override fun getHeaders(): Map<String, String> {
                val headers = HashMap<String, String>()
                headers.put("Content-Type", "application/json")
                headers.put("ApiKey", "****")
                return headers
            }
        }







        GBEAPISDemo.instance?.addToRequestQueue(jsonObjReq)


    }




}



activity_post_detail.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context="com.ahmadnaser.gbeapiswpblogdemo.PostDetail">

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

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <ImageView
                android:id="@+id/postThumbnail"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:contentDescription="Post Thumbnail"
                android:minHeight="180dp"
                android:scaleType="centerCrop"
                app:srcCompat="@mipmap/ic_launcher" />

            <TextView
                android:id="@+id/postTitle"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_margin="5dp"
                android:text="Post Title"
                android:textAppearance="@style/TextAppearance.AppCompat.Medium"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/authorName"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginBottom="5dp"
                android:layout_marginLeft="5dp"
                android:layout_marginRight="5dp"
                android:text="Author Name"
                android:textAppearance="@style/TextAppearance.Compat.Notification.Info" />

            <ProgressBar
                android:id="@+id/prgsEmployeeUpdate"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:foregroundGravity="center_horizontal|center"
                android:padding="10dp"
                android:visibility="visible" />

            <WebView  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"
                tools:context="com.kautube.test.ikatanalumni.MainActivity"
                android:id="@+id/contentBlock"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_margin="5dp"
                android:textAppearance="@style/TextAppearance.AppCompat.Medium"
                />


        </LinearLayout>


    </ScrollView>


</LinearLayout>

18- raw/post_format.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="text/html; utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>#title#</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
    <style type="text/css">
        body iframe, body img,
        body embed, body object{max-width:320px!important;height:auto!important;border:0;}
      /**
      * 8.0 Alignments
      */

      .alignleft {
      display: inline;
      float: left;
      }

      .alignright {
      display: inline;
      float: right;
      }

      .aligncenter {
      display: block;
      margin-right: auto;
      margin-left: auto;
      }

      blockquote.alignleft,
      .wp-caption.alignleft,
      img.alignleft {
      margin: 0.4em 1.6em 1.6em 0;
      }

      blockquote.alignright,
      .wp-caption.alignright,
      img.alignright {
      margin: 0.4em 0 1.6em 1.6em;
      }

      blockquote.aligncenter,
      .wp-caption.aligncenter,
      img.aligncenter {
      clear: both;
      margin-top: 0.4em;
      margin-bottom: 1.6em;
      }

      .wp-caption.alignleft,
      .wp-caption.alignright,
      .wp-caption.aligncenter {
      margin-bottom: 1.2em;
      }
      .entry-content,
      .entry-summary {
      padding: 0 7.6923% 7.6923%;
      }

      .entry-content > :last-child,
      .entry-summary > :last-child {
      margin-bottom: 0;
      }

      .entry-content,
      .entry-summary,
      .page-content,
      .comment-content {
      -webkit-hyphens: auto;
      -moz-hyphens: auto;
      -ms-hyphens: auto;
      hyphens: auto;
      word-wrap: break-word;
      }

      .entry-content h1,
      .entry-summary h1,
      .page-content h1,
      .comment-content h1 {
      font-size: 26px;
      font-size: 2.6rem;
      line-height: 1.1538;
      margin-top: 1.8462em;
      margin-bottom: 0.9231em;
      }

      .entry-content h2,
      .entry-summary h2,
      .page-content h2,
      .comment-content h2 {
      font-size: 22px;
      font-size: 2.2rem;
      line-height: 1.3636;
      margin-top: 2.1818em;
      margin-bottom: 1.0909em;
      }

      .entry-content h3,
      .entry-summary h3,
      .page-content h3,
      .comment-content h3 {
      font-size: 18px;
      font-size: 1.8rem;
      line-height: 1.3333;
      margin-top: 2.6667em;
      margin-bottom: 1.3333em;
      }

      .entry-content h4,
      .entry-content h5,
      .entry-content h6,
      .entry-summary h4,
      .entry-summary h5,
      .entry-summary h6,
      .page-content h4,
      .page-content h5,
      .page-content h6,
      .comment-content h4,
      .comment-content h5,
      .comment-content h6 {
      font-size: 15px;
      font-size: 1.5rem;
      line-height: 1.2;
      margin-top: 3.2em;
      margin-bottom: 1.6em;
      }

      .entry-content h5,
      .entry-content h6,
      .entry-summary h5,
      .entry-summary h6,
      .page-content h5,
      .page-content h6,
      .comment-content h5,
      .comment-content h6 {
      letter-spacing: 0.1em;
      text-transform: uppercase;
      }

      .entry-content > h1:first-child,
      .entry-content > h2:first-child,
      .entry-content > h3:first-child,
      .entry-content > h4:first-child,
      .entry-content > h5:first-child,
      .entry-content > h6:first-child,
      .entry-summary > h1:first-child,
      .entry-summary > h2:first-child,
      .entry-summary > h3:first-child,
      .entry-summary > h4:first-child,
      .entry-summary > h5:first-child,
      .entry-summary > h6:first-child,
      .page-content > h1:first-child,
      .page-content > h2:first-child,
      .page-content > h3:first-child,
      .page-content > h4:first-child,
      .page-content > h5:first-child,
      .page-content > h6:first-child,
      .comment-content > h1:first-child,
      .comment-content > h2:first-child,
      .comment-content > h3:first-child,
      .comment-content > h4:first-child,
      .comment-content > h5:first-child,
      .comment-content > h6:first-child {
      margin-top: 0;
      }

      .entry-content a,
      .entry-summary a,
      .page-content a,
      .comment-content a,
      .pingback .comment-body > a {
      border-bottom: 1px solid #333;
      }

      .entry-content a:hover,
      .entry-content a:focus,
      .entry-summary a:hover,
      .entry-summary a:focus,
      .page-content a:hover,
      .page-content a:focus,
      .comment-content a:hover,
      .comment-content a:focus,
      .pingback .comment-body > a:hover,
      .pingback .comment-body > a:focus {
      border-bottom: 0;
      }

      .entry-content a img,
      .entry-summary a img,
      .page-content a img,
      .comment-content a img {
      display: block;
      }

      .entry-content .more-link,
      .entry-summary .more-link:after {
      white-space: nowrap;
      }

      .entry-content .more-link:after,
      .entry-summary .more-link:after {
      content: "\f429";
      font-size: 16px;
      position: relative;
      top: 5px;
      }
    </style>
  </head>
  <body>#content#</body>
</html>

One response to “Android WordPress Blog Project Kotlin Volley, retrofit2, Gson, Okhttp, Picasso, Ahbottomnavigation”

Leave a Reply

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