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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?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
1 2 3 4 5 6 7 8 |
<?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?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,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
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
1 2 3 4 5 6 7 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
<?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
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) } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
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
1 2 3 4 5 6 7 8 9 10 11 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
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 ) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
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 & Fitness" -> { holder.imageView_cat.setBackgroundResource(R.drawable.did_you_know) } "Life & Love" -> { holder.imageView_cat.setBackgroundResource(R.drawable.network_security) } "Makeup & 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<?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
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
<?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
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<String>, val matches : List<List<String>>, 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
<?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
<!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> |