diff --git a/.gitignore b/.gitignore index aa724b7..eb8dd00 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,11 @@ .DS_Store /build /captures +/gradle.properties +bin/ +gen/ +# Proguard folder generated by Eclipse +proguard/ .externalNativeBuild .cxx local.properties diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index 79ee123..6e6eec1 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,6 @@ \ No newline at end of file diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml deleted file mode 100644 index 0a9027d..0000000 --- a/.idea/deploymentTargetDropDown.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 526b4c2..a2d7c21 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -13,7 +13,6 @@ - diff --git a/.idea/misc.xml b/.idea/misc.xml index c2064d2..c3fd6cf 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -5,6 +5,10 @@ + + + + @@ -15,18 +19,43 @@ + + - + + + + + + + + + + + + + - + + + + + + + + + + + + diff --git a/apk/News.apk b/apk/News.apk new file mode 100644 index 0000000..3973d06 Binary files /dev/null and b/apk/News.apk differ diff --git a/app/build.gradle b/app/build.gradle index 95548f6..305cde4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,30 +1,42 @@ - plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-kapt' - id 'androidx.navigation.safeargs.kotlin' } apply plugin: 'kotlin-android' android { - compileSdk 30 + defaultConfig { -// configurations.all { -// resolutionStrategy { force 'androidx.core:core-ktx:1.6.0' } -// } + multiDexEnabled true applicationId "com.example.newsapp" minSdk 21 - targetSdk 30 + targetSdk 31 versionCode 1 versionName "1.0" - compileSdk 31 + compileSdk 32 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + +// Please ensure you have a valid API KEY for https://newsapi.org/ +// to use this app +// A valid key will need to be entered + buildConfigField("String", 'API_KEY', API_KEY) +// buildConfigField('String' , 'myParameter', 'someValue') + + } + + compileOptions { + + coreLibraryDesugaringEnabled true + + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } buildTypes { @@ -32,52 +44,46 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } +// buildTypes.each { +// it.buildConfigField 'String', 'API_KEY', API_KEY +// } } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } + + kotlinOptions { jvmTarget = '1.8' } + namespace 'com.example.newsapp' } dependencies { - implementation 'androidx.constraintlayout:constraintlayout:2.1.3' - def room_version = '2.4.2' - def nav_version = '2.4.1' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.viewpager2:viewpager2:1.0.0' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1' - - implementation "androidx.navigation:navigation-fragment-ktx:2.4.1" - implementation "androidx.navigation:navigation-ui-ktx:$nav_version" - - implementation "androidx.room:room-runtime:$room_version" - kapt "androidx.room:room-compiler:$room_version" - implementation "androidx.activity:activity-ktx:1.4.0" - - implementation 'androidx.core:core-ktx:1.7.0' - implementation 'androidx.appcompat:appcompat:1.4.1' - implementation 'com.google.android.material:material:1.5.0' + implementation 'androidx.core:core-ktx:1.8.0' + implementation 'androidx.appcompat:appcompat:1.4.2' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.legacy:legacy-support-v4:1.0.0' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + def room_version = '2.4.2' + implementation "androidx.room:room-runtime:$room_version" + kapt "androidx.room:room-compiler:$room_version" + implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0' - -// GSON - implementation 'com.squareup.retrofit2:converter-gson:2.9.0' - implementation 'com.squareup.picasso:picasso:2.71828' - implementation "androidx.core:core-ktx:+" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.facebook.shimmer:shimmer:0.5.0' + } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1732ab1..8cad41b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,9 +1,15 @@ - + + + + + + + + + - - - + android:name=".SavedNewsActivity" + android:label="Saved News" + android:parentActivityName=".MainActivity" /> \ No newline at end of file diff --git a/app/src/main/java/com/example/newsapp/CustomAdapter.kt b/app/src/main/java/com/example/newsapp/CustomAdapter.kt deleted file mode 100644 index 6b56fa5..0000000 --- a/app/src/main/java/com/example/newsapp/CustomAdapter.kt +++ /dev/null @@ -1,108 +0,0 @@ -package com.example.newsapp - -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ImageButton -import android.widget.ImageView -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import com.squareup.picasso.Picasso - - -class CustomAdapter(private val newsList: MutableList, val saveListener: SaveClickListener) : RecyclerView.Adapter(){ - - private lateinit var context: Context - - companion object{ - var myClickListener: SaveClickListener? = null - - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - - val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false) - context = parent.context - return ViewHolder(view) - } - - - private fun openNews(url: String, cont: Context){ - - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) - cont.startActivity(intent) - } - - - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - - val newsData = newsList[holder.adapterPosition] - - holder.headLine.text = newsData.headLine - holder.description.text = newsData.description - - if(!newsData.image.isNullOrEmpty()) { - Picasso.get().load(newsData.image).fit().centerCrop().into(holder.image) - }else{ - holder.image.visibility = View.GONE - } - - - if(context.toString().contains("com.example.newsapp.SavedNews")){ - holder.saveBtn.setImageResource(R.drawable.ic_baseline_delete_outline_24) - } - - - holder.image.setOnClickListener { - newsData.url?.let { it1 -> openNews(it1, context) } - } - - holder.headLine.setOnClickListener{ newsData.url?.let { it1 -> - openNews( - it1, context) - } } - - holder.shareBtn.setOnClickListener { - - val intent = Intent(Intent.ACTION_SEND) - intent.putExtra(Intent.EXTRA_TEXT, "Hey, checkout this news : " + newsData.url) - intent.type = "text/plain" - context.startActivity(Intent.createChooser(intent, "Share with :")) - - } - - myClickListener = saveListener - - holder.saveBtn.setOnClickListener { - if (myClickListener != null) { - myClickListener?.onSaveBtnClick(holder.adapterPosition) - } - } - } - - - - override fun getItemCount(): Int { - return newsList.size - } - - class ViewHolder(ItemView: View?) : RecyclerView.ViewHolder(ItemView!!){ - - val saveBtn: ImageButton = itemView.findViewById(R.id.save_news) - val shareBtn: ImageButton = itemView.findViewById(R.id.share_btn) - val image: ImageView = itemView.findViewById(R.id.news_image) - val headLine: TextView = itemView.findViewById(R.id.headline) - val description: TextView = itemView.findViewById(R.id.description) - - - } - - interface SaveClickListener { - fun onSaveBtnClick(position: Int) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/example/newsapp/MainActivity.kt b/app/src/main/java/com/example/newsapp/MainActivity.kt index f47e182..405b9f2 100644 --- a/app/src/main/java/com/example/newsapp/MainActivity.kt +++ b/app/src/main/java/com/example/newsapp/MainActivity.kt @@ -1,203 +1,185 @@ package com.example.newsapp +import android.content.Context import android.content.Intent +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build import android.os.Bundle import android.view.Menu import android.view.MenuItem import android.view.View -import android.view.animation.TranslateAnimation -import android.widget.* -import androidx.annotation.Nullable +import android.widget.TextView import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.Toolbar import androidx.lifecycle.ViewModelProvider -import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 +import com.example.newsapp.adapters.FragmentAdapter import com.example.newsapp.architecture.NewsViewModel import com.facebook.shimmer.ShimmerFrameLayout - +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator +//import com.example.newsapp.BuildConfig class MainActivity : AppCompatActivity() { + companion object { + + var APIRequestError = false + var errorMessage = "error" + const val API_KEY = BuildConfig.API_KEY + const val TOTAL_NEWS_TAB = 7 + const val TOP_HEADLINES_COUNT = 5 + const val GENERAL = "general" + const val SCIENCE = "science" + const val HEALTH = "health" + const val ENTERTAINMENT = "entertainment" + const val BUSINESS = "business" + const val TECHNOLOGY = "technology" + const val SPORTS = "sports" + const val NEWS_URL = "news url" + const val NEWS_TITLE = "news title" + const val NEWS_IMAGE_URL = "news image url" + const val NEWS_SOURCE = "news source" + const val NEWS_PUBLICATION_TIME = "news publication time" + const val NEWS_DESCRIPTION = "news description" + const val NEWS_CONTENT = "news content" + var generalNews: ArrayList = ArrayList() + var entertainmentNews: MutableList = mutableListOf() + var businessNews: MutableList = mutableListOf() + var healthNews: MutableList = mutableListOf() + var scienceNews: MutableList = mutableListOf() + var sportsNews: MutableList = mutableListOf() + var techNews: MutableList = mutableListOf() + + } + +// Tabs Title + private val newsCategories = arrayOf( + "Home", + BUSINESS, + ENTERTAINMENT, + SCIENCE, + SPORTS, + TECHNOLOGY, + HEALTH + ) + private lateinit var viewModel: NewsViewModel - private lateinit var recyclerView: RecyclerView - private lateinit var newsContainer: FrameLayout - private lateinit var fetchedNews: MutableList - private lateinit var mAdapter: CustomAdapter + private lateinit var tabLayout: TabLayout + private lateinit var viewPager: ViewPager2 + private lateinit var fragmentAdapter: FragmentAdapter private lateinit var shimmerLayout: ShimmerFrameLayout - private lateinit var categoryContainer: LinearLayout - private lateinit var dropBtn: ImageButton - - private var hideCategory: Boolean = false + private var totalRequestCount = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) - categoryContainer = findViewById(R.id.cate_container) - dropBtn = findViewById(R.id.drop_btn) - newsContainer = findViewById(R.id.news_container) - recyclerView = findViewById(R.id.recyclerView) - shimmerLayout = findViewById(R.id.shimmerLayout) - recyclerView.layoutManager = LinearLayoutManager(this) - recyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL)) - viewModel = ViewModelProvider(this)[NewsViewModel::class.java] - fetchedNews = mutableListOf() - - findViewById