├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── anncode │ │ └── offersandcoupons │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── anncode │ │ │ └── offersandcoupons │ │ │ ├── BaseCode.kt │ │ │ ├── Coupon.kt │ │ │ ├── CouponDetailActivity.kt │ │ │ ├── MainActivity.kt │ │ │ └── RecyclerCouponsAdapter.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── gradient.xml │ │ ├── header.png │ │ ├── ic_launcher_background.xml │ │ ├── rounded_corner_category.xml │ │ └── rounded_corner_deadline.xml │ │ ├── layout │ │ ├── activity_coupon_detail.xml │ │ ├── activity_main.xml │ │ └── card_coupon.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── anncode │ └── offersandcoupons │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 28 9 | defaultConfig { 10 | applicationId "com.anncode.offersandcoupons" 11 | minSdkVersion 15 12 | targetSdkVersion 28 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 28 | implementation 'com.android.support:appcompat-v7:28.0.0' 29 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 30 | testImplementation 'junit:junit:4.12' 31 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 32 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 33 | implementation 'com.squareup.retrofit2:retrofit:2.3.0' 34 | implementation 'com.squareup.retrofit2:converter-gson:2.3.0' 35 | implementation 'com.android.support:recyclerview-v7:28.0.0' 36 | implementation 'com.squareup.picasso:picasso:2.71828' 37 | implementation 'com.android.support:cardview-v7:28.0.0' 38 | implementation 'de.hdodenhof:circleimageview:3.0.0' 39 | } 40 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/anncode/offersandcoupons/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.anncode.offersandcoupons 2 | 3 | import android.support.test.InstrumentationRegistry 4 | import android.support.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getTargetContext() 22 | assertEquals("com.anncode.offersandcoupons", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/anncode/offersandcoupons/BaseCode.kt: -------------------------------------------------------------------------------- 1 | package com.anncode.offersandcoupons 2 | 3 | /* 4 | * 5 | * 6 | * MainActivity.kt 7 | val rvCoupons: RecyclerView = findViewById(R.id.rvCoupons) 8 | rvCoupons.layoutManager = LinearLayoutManager(this) 9 | val coupons = ArrayList() 10 | 11 | val apiService = getClientService() 12 | val call = apiService.getCoupons() 13 | 14 | call.enqueue(object : Callback { 15 | override fun onFailure(call: Call, t: Throwable) { 16 | Log.e("ERROR: ", t.message) 17 | t.stackTrace 18 | } 19 | 20 | override fun onResponse(call: Call, response: Response) { 21 | val offersJsonArray = response.body()?.getAsJsonArray("offers") 22 | offersJsonArray?.forEach { jsonElement: JsonElement -> 23 | var jsonObject = jsonElement.asJsonObject 24 | var coupon = Coupon(jsonObject) 25 | coupons.add(coupon) 26 | } 27 | rvCoupons.adapter = RecyclerCouponsAdapter(coupons, R.layout.card_coupon) 28 | 29 | } 30 | 31 | 32 | }) 33 | 34 | 35 | val apiKey = "69d1837829128f9565368ca704c63207" 36 | val urlApi = "http://feed.linkmydeals.com/" 37 | 38 | fun getClientService(): ApiService { 39 | val authInterceptor = Interceptor { chain -> 40 | val url = chain.request().url().newBuilder() 41 | .addQueryParameter("API_KEY", apiKey) 42 | .addQueryParameter("format", "json") 43 | .build() 44 | 45 | val newRequest = chain.request() 46 | .newBuilder() 47 | .url(url) 48 | .build() 49 | 50 | chain.proceed(newRequest) 51 | } 52 | 53 | val client = OkHttpClient.Builder() 54 | .addInterceptor(authInterceptor).build() 55 | 56 | val retrofit = Retrofit.Builder() 57 | .baseUrl(urlApi) 58 | .client(client) 59 | .addConverterFactory(GsonConverterFactory.create()) 60 | .build() 61 | 62 | return retrofit.create(ApiService::class.java) 63 | } 64 | 65 | interface ApiService { 66 | @GET("getOffers/") 67 | fun getCoupons(): Call 68 | } 69 | 70 | 71 | CouponDetailActivity.java 72 | 73 | couponSelected = intent.getSerializableExtra("COUPON") as Coupon 74 | 75 | var tvTitleDetail: TextView = findViewById(R.id.tvTitleDetail) 76 | var tvDescriptionShortDetail: TextView = findViewById(R.id.tvDescriptionShortDetail) 77 | var tvCategoryDetail: TextView = findViewById(R.id.tvCategoryDetail) 78 | var tvDateDetail: TextView = findViewById(R.id.tvDateDetail) 79 | var tvDescriptionDetailData: TextView = findViewById(R.id.tvDescriptionDetailData) 80 | var tvOffertDetailData: TextView = findViewById(R.id.tvOffertDetailData) 81 | var tvWebsiteDetailData: TextView = findViewById(R.id.tvWebsiteDetailData) 82 | var tvDateEndData: TextView = findViewById(R.id.tvDateEndData) 83 | var imgHeaderDetail: ImageView = findViewById(R.id.imgHeaderDetail) 84 | var imgCouponDetail: CircleImageView = findViewById(R.id.imgCouponDetail) 85 | var btnOpenOffer: Button = findViewById(R.id.btnOpenOffer) 86 | 87 | tvTitleDetail.text = couponSelected?.title 88 | tvDescriptionShortDetail.text = couponSelected?.descriptionShort 89 | tvCategoryDetail.text = couponSelected?.category 90 | tvDateDetail.text = couponSelected?.endDate 91 | tvDescriptionDetailData.text = couponSelected?.description 92 | tvOffertDetailData.text = couponSelected?.offer 93 | tvWebsiteDetailData.text = couponSelected?.website 94 | tvDateEndData.text = couponSelected?.endDate 95 | 96 | Picasso.get().load(couponSelected?.image_url).resize(520, 520).centerCrop().into(imgHeaderDetail) 97 | Picasso.get().load(couponSelected?.image_url).resize(520, 520).centerCrop().into(imgCouponDetail) 98 | 99 | btnOpenOffer.setOnClickListener { 100 | val openURL = Intent(Intent.ACTION_VIEW) 101 | openURL.data = Uri.parse(couponSelected?.url) 102 | startActivity(openURL) 103 | } 104 | 105 | * */ -------------------------------------------------------------------------------- /app/src/main/java/com/anncode/offersandcoupons/Coupon.kt: -------------------------------------------------------------------------------- 1 | package com.anncode.offersandcoupons 2 | 3 | import com.google.gson.JsonObject 4 | import java.lang.Exception 5 | import java.text.ParseException 6 | import java.text.SimpleDateFormat 7 | import java.io.Serializable 8 | import java.util.* 9 | 10 | class Coupon(couponJson: JsonObject?) : Serializable { 11 | 12 | lateinit var id: String 13 | lateinit var image_url: String 14 | lateinit var title: String 15 | lateinit var descriptionShort: String 16 | lateinit var category: String 17 | lateinit var description:String 18 | lateinit var offer: String 19 | lateinit var website: String 20 | lateinit var endDate: String 21 | lateinit var url: String 22 | 23 | init { 24 | try { 25 | id = couponJson!!.get(ID).asString 26 | image_url = couponJson!!.get(IMAGE_URL).asString 27 | title = couponJson!!.get(TITLE).asString 28 | descriptionShort = chunkWords(couponJson!!.get(DESCRIPTION_SHORT).asString, ' ', 5) 29 | category = chunkWords(couponJson!!.get(CATEGORY).asString, ',', 1) 30 | description = couponJson!!.get(DESCRIPTION).asString 31 | offer = couponJson!!.get(OFFER).asString 32 | website = couponJson!!.get(WEBSITE).asString 33 | endDate = getFormatDate(couponJson!!.get(END_DATE).asString) 34 | url = couponJson!!.get(URL).asString 35 | }catch (e: Exception){ 36 | e.printStackTrace() 37 | } 38 | 39 | 40 | } 41 | 42 | companion object { 43 | private val ID = "lmd_id" 44 | private val IMAGE_URL = "image_url" 45 | private val TITLE = "title" 46 | private val DESCRIPTION_SHORT = "offer_text" 47 | private val CATEGORY = "categories" 48 | private val DESCRIPTION = "description" 49 | private val OFFER = "offer" 50 | private val WEBSITE = "store" 51 | private val END_DATE = "end_date" 52 | private val URL = "url" 53 | } 54 | 55 | private fun getFormatDate(dateCoupon:String):String { 56 | val format = SimpleDateFormat("yyyy-MM-dd") 57 | val dateFormat = SimpleDateFormat("dd MMMM yyyy") 58 | try { 59 | val parsedDateFormat = format.parse(dateCoupon) 60 | val cal = Calendar.getInstance() 61 | cal.time = parsedDateFormat 62 | return dateFormat.format(cal.time) 63 | } catch (e: ParseException) { 64 | e.printStackTrace() 65 | return "" 66 | } 67 | } 68 | 69 | 70 | private fun chunkWords(string: String, delimiter: Char, quantity: Int): String { 71 | val words = string.split(delimiter) 72 | var newString: String = "" 73 | 74 | for (i in 0..quantity){ 75 | newString += words.get(i) + " " 76 | } 77 | 78 | return newString 79 | } 80 | } -------------------------------------------------------------------------------- /app/src/main/java/com/anncode/offersandcoupons/CouponDetailActivity.kt: -------------------------------------------------------------------------------- 1 | package com.anncode.offersandcoupons 2 | 3 | import android.content.Intent 4 | import android.net.Uri 5 | import android.support.v7.app.AppCompatActivity 6 | import android.os.Bundle 7 | import android.widget.Button 8 | import android.widget.ImageView 9 | import android.widget.TextView 10 | import com.squareup.picasso.Picasso 11 | import de.hdodenhof.circleimageview.CircleImageView 12 | 13 | class CouponDetailActivity : AppCompatActivity() { 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | setContentView(R.layout.activity_coupon_detail) 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/anncode/offersandcoupons/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.anncode.offersandcoupons 2 | 3 | import android.support.v7.app.AppCompatActivity 4 | import android.os.Bundle 5 | 6 | class MainActivity : AppCompatActivity() { 7 | 8 | override fun onCreate(savedInstanceState: Bundle?) { 9 | super.onCreate(savedInstanceState) 10 | setContentView(R.layout.activity_main) 11 | supportActionBar?.hide() 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/anncode/offersandcoupons/RecyclerCouponsAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.anncode.offersandcoupons 2 | 3 | import android.content.Intent 4 | import android.support.v7.widget.RecyclerView 5 | import android.util.Log 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.widget.ImageView 10 | import android.widget.TextView 11 | import com.squareup.picasso.Picasso 12 | 13 | class RecyclerCouponsAdapter(var coupons : ArrayList, var resource: Int) : RecyclerView.Adapter() { 14 | 15 | override fun onCreateViewHolder(p0: ViewGroup, p1: Int): CardCouponHolder { 16 | var view: View = LayoutInflater.from(p0!!.context).inflate(resource, p0, false) 17 | return CardCouponHolder(view) 18 | } 19 | 20 | override fun getItemCount(): Int { 21 | return coupons.size 22 | } 23 | 24 | override fun onBindViewHolder(p0: CardCouponHolder, p1: Int) { 25 | var coupon = coupons.get(p1) 26 | p0.setDataCard(coupon) 27 | } 28 | 29 | class CardCouponHolder(v: View) : RecyclerView.ViewHolder(v), View.OnClickListener { 30 | 31 | private var coupon: Coupon? = null 32 | private var imgCoupon: ImageView = v.findViewById(R.id.imgCoupon) 33 | private var tvTitle: TextView = v.findViewById(R.id.tvTitle) 34 | private var tvDescriptionShort: TextView = v.findViewById(R.id.tvDescriptionShort) 35 | private var tvCategory: TextView = v.findViewById(R.id.tvCategory) 36 | private var tvDate: TextView = v.findViewById(R.id.tvDate) 37 | 38 | init { 39 | v.setOnClickListener(this) 40 | } 41 | 42 | fun setDataCard(coupon: Coupon){ 43 | this.coupon = coupon 44 | Picasso.get().load(coupon.image_url).resize(520, 520).centerCrop().into(imgCoupon) 45 | tvTitle.setText(coupon.title) 46 | tvDescriptionShort.setText(coupon.descriptionShort) 47 | tvCategory.setText(coupon.category) 48 | tvDate.setText(coupon.endDate) 49 | 50 | } 51 | 52 | override fun onClick(v: View) { 53 | Log.i("CLICK Coupon: ", coupon?.title) 54 | val context = v.context 55 | val showPhotoIntent = Intent(context, CouponDetailActivity::class.java) 56 | showPhotoIntent.putExtra("COUPON", coupon) 57 | context.startActivity(showPhotoIntent) 58 | 59 | } 60 | 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/gradient.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anncode1/Curso-Arquitectura-Android/f236ad2a4bd7e3bb4ad46c6c9530f1821bf2cc2a/app/src/main/res/drawable/header.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_corner_category.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_corner_deadline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_coupon_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 22 | 35 | 45 | 61 | 62 | 74 | 77 | 81 | 85 | 93 | 94 | 104 | 105 | 106 | 112 | 113 | 126 | 127 | 137 | 138 | 139 | 149 | 150 | 160 | 161 | 172 | 183 | 193 | 206 | 217 | 225 | 235 | 243 | 252 | 253 | 254 | 255 |