diff --git a/README.md b/README.md index 1e67a79..bfafc54 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,21 @@ File Structure - RickAndMortyCache - SimpleResponse -### **HomeActivity** -### **RickMortyApplication** +#### **HomeActivity** +#### **RickMortyApplication** + +--- + + +## **MVVM Architecture** + +- + + + +--- +## **Network Debugging** + + + diff --git a/app/build.gradle b/app/build.gradle index 8761fa3..43bd303 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -95,4 +95,6 @@ dependencies { implementation("com.google.firebase:firebase-crashlytics-ktx") implementation("com.google.firebase:firebase-analytics-ktx") + implementation 'com.airbnb.android:lottie:6.0.0' + } \ No newline at end of file diff --git a/app/src/main/assets/loading.json b/app/src/main/assets/loading.json new file mode 100644 index 0000000..4046a96 --- /dev/null +++ b/app/src/main/assets/loading.json @@ -0,0 +1 @@ +{"v":"5.7.8","fr":29.9700012207031,"ip":0,"op":30.0000012219251,"w":500,"h":500,"nm":"Loading","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":13,"s":[546,266,0],"to":[0,-11,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":17,"s":[546,200,0],"to":[0,0,0],"ti":[0,-11,0]},{"t":21.0000008553475,"s":[546,266,0]}],"ix":2,"l":2,"x":"var $bm_rt;\nvar $bm_rt;\nvar n, n, t, t, v, amp, freq, decay;\n$bm_rt = $bm_rt = n = 0;\nif (numKeys > 0) {\n $bm_rt = $bm_rt = n = nearestKey(time).index;\n if (key(n).time > time) {\n n--;\n }\n}\nif (n == 0) {\n $bm_rt = $bm_rt = t = 0;\n} else {\n $bm_rt = $bm_rt = t = sub(time, key(n).time);\n}\nif (n > 0 && t < 1) {\n v = velocityAtTime(sub(key(n).time, div(thisComp.frameDuration, 10)));\n amp = 0.05;\n freq = 4;\n decay = 8;\n $bm_rt = $bm_rt = add(value, div(mul(mul(v, amp), Math.sin(mul(mul(mul(freq, t), 2), Math.PI))), Math.exp(mul(decay, t))));\n} else {\n $bm_rt = $bm_rt = value;\n}"},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[45.137,45.137],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.7490196078431373,0.24705882352941178,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.7490196078431373,0.24705882352941178,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-167.432,-17.432],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":30.0000012219251,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":9,"s":[454,266,0],"to":[0,-11,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":13,"s":[454,200,0],"to":[0,0,0],"ti":[0,-11,0]},{"t":17.0000006924242,"s":[454,266,0]}],"ix":2,"l":2,"x":"var $bm_rt;\nvar $bm_rt;\nvar n, n, t, t, v, amp, freq, decay;\n$bm_rt = $bm_rt = n = 0;\nif (numKeys > 0) {\n $bm_rt = $bm_rt = n = nearestKey(time).index;\n if (key(n).time > time) {\n n--;\n }\n}\nif (n == 0) {\n $bm_rt = $bm_rt = t = 0;\n} else {\n $bm_rt = $bm_rt = t = sub(time, key(n).time);\n}\nif (n > 0 && t < 1) {\n v = velocityAtTime(sub(key(n).time, div(thisComp.frameDuration, 10)));\n amp = 0.05;\n freq = 4;\n decay = 8;\n $bm_rt = $bm_rt = add(value, div(mul(mul(v, amp), Math.sin(mul(mul(mul(freq, t), 2), Math.PI))), Math.exp(mul(decay, t))));\n} else {\n $bm_rt = $bm_rt = value;\n}"},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[45.137,45.137],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.3568627450980392,0.7607843137254902,0.9058823529411765,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.3568627450980392,0.7607843137254902,0.9058823529411765,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-167.432,-17.432],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":30.0000012219251,"st":-359.00001462237,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":4,"s":[365,266,0],"to":[0,-11,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":9,"s":[365,200,0],"to":[0,0,0],"ti":[0,-11,0]},{"t":13.0000005295009,"s":[365,266,0]}],"ix":2,"l":2,"x":"var $bm_rt;\nvar $bm_rt;\nvar n, n, t, t, v, amp, freq, decay;\n$bm_rt = $bm_rt = n = 0;\nif (numKeys > 0) {\n $bm_rt = $bm_rt = n = nearestKey(time).index;\n if (key(n).time > time) {\n n--;\n }\n}\nif (n == 0) {\n $bm_rt = $bm_rt = t = 0;\n} else {\n $bm_rt = $bm_rt = t = sub(time, key(n).time);\n}\nif (n > 0 && t < 1) {\n v = velocityAtTime(sub(key(n).time, div(thisComp.frameDuration, 10)));\n amp = 0.05;\n freq = 4;\n decay = 8;\n $bm_rt = $bm_rt = add(value, div(mul(mul(v, amp), Math.sin(mul(mul(mul(freq, t), 2), Math.PI))), Math.exp(mul(decay, t))));\n} else {\n $bm_rt = $bm_rt = value;\n}"},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[45.137,45.137],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.7019607843137254,0.5333333333333333,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.7019607843137254,0.5333333333333333,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-167.432,-17.432],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":30.0000012219251,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":0,"s":[277,266,0],"to":[0,-11,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":4,"s":[277,200,0],"to":[0,0,0],"ti":[0,-11,0]},{"t":9.00000036657752,"s":[277,266,0]}],"ix":2,"l":2,"x":"var $bm_rt;\nvar $bm_rt;\nvar n, n, t, t, v, amp, freq, decay;\n$bm_rt = $bm_rt = n = 0;\nif (numKeys > 0) {\n $bm_rt = $bm_rt = n = nearestKey(time).index;\n if (key(n).time > time) {\n n--;\n }\n}\nif (n == 0) {\n $bm_rt = $bm_rt = t = 0;\n} else {\n $bm_rt = $bm_rt = t = sub(time, key(n).time);\n}\nif (n > 0 && t < 1) {\n v = velocityAtTime(sub(key(n).time, div(thisComp.frameDuration, 10)));\n amp = 0.05;\n freq = 4;\n decay = 8;\n $bm_rt = $bm_rt = add(value, div(mul(mul(v, amp), Math.sin(mul(mul(mul(freq, t), 2), Math.PI))), Math.exp(mul(decay, t))));\n} else {\n $bm_rt = $bm_rt = value;\n}"},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[45.137,45.137],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.06274509803921569,0.023529411764705882,0.6235294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.06274509803921569,0.023529411764705882,0.6235294117647059,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-167.432,-17.432],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":30.0000012219251,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/assets/no_internet.json b/app/src/main/assets/no_internet.json new file mode 100644 index 0000000..3b3f665 --- /dev/null +++ b/app/src/main/assets/no_internet.json @@ -0,0 +1 @@ +{"v":"4.8.0","meta":{"g":"LottieFiles AE 1.0.0","a":"","k":"","d":"","tc":""},"fr":29.9700012207031,"ip":0,"op":61.0000024845809,"w":260,"h":260,"nm":"Not Done","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[131,130,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[-0.137,-0.137,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":4,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[28.416,28.416,0]},"t":24,"s":[100,100,100]},{"t":29.0000011811942,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0]],"o":[[0,0]],"v":[[153.888,72.281]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.2901960784313726,0.2901960784313726,0.2901960784313726,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2901960784313726,0.5647058823529412,0.8862745098039215,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-35.743,34.684],[34.684,-35.743]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.2901960784313726,0.2901960784313726,0.2901960784313726,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2901960784313726,0.2901960784313726,0.2901960784313726,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":24,"s":[100]},{"t":29.0000011811942,"s":[0]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":899.000036617021,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"wifi_black_24dp1 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[130,130,0],"ix":2},"a":{"a":0,"k":[12,12,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[0.763,0.763,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":4,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[-0.816,-0.816,0]},"t":24,"s":[478.824,478.824,100]},{"t":29.0000011811942,"s":[444,444,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.87,-3.86],[0,0],[-2.76,-2.76],[0,0]],"o":[[0,0],[2.76,-2.76],[0,0],[-3.86,-3.86]],"v":[[-7,1.535],[-5,3.535],[5,3.535],[7,1.535]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[1.66,-1.66],[0,0],[0,0]],"o":[[0,0],[0,0],[-1.65,-1.66]],"v":[[-3,5.535],[0,8.535],[3,5.535]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[6.08,-6.07],[0,0],[-4.97,-4.97],[0,0]],"o":[[0,0],[4.97,-4.97],[0,0],[-6.07,-6.07]],"v":[[-11,-2.465],[-9,-0.465],[9,-0.465],[11,-2.465]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2901960784313726,0.5647058823529412,0.8862745098039215,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[12,11.465],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":899.000036617021,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Settings Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[130,130,0],"ix":2},"a":{"a":0,"k":[95.067,95.067,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.116,0.116,0.667],"y":[1,1,1]},"o":{"x":[1,1,0.333],"y":[-0.004,-0.004,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":24,"s":[100,100,100]},{"t":29.0000011811942,"s":[90,90,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-52.366,0],[0,-52.366],[52.366,0],[0,52.366]],"o":[[52.366,0],[0,52.366],[-52.366,0],[0,-52.366]],"v":[[0,-94.817],[94.817,0],[0,94.817],[-94.817,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.9450980392156862,0.9529411764705882,0.9607843137254902,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[95.067,95.067],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":899.000036617021,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/java/com/example/rickmorty/NetworkViewModel.kt b/app/src/main/java/com/example/rickmorty/NetworkViewModel.kt new file mode 100644 index 0000000..400b3d0 --- /dev/null +++ b/app/src/main/java/com/example/rickmorty/NetworkViewModel.kt @@ -0,0 +1,30 @@ +package com.example.rickmorty + +import android.app.Application +import android.content.Context +import android.net.ConnectivityManager +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData + +class NetworkViewModel(application: Application) : AndroidViewModel(application) { + + private val connectivityManager = + application.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + + private val networkStateLiveData = MutableLiveData() + + init { + checkNetworkState() + } + + fun getNetworkState(): LiveData { + return networkStateLiveData + } + + private fun checkNetworkState() { + val activeNetwork = connectivityManager.activeNetworkInfo + val isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting + networkStateLiveData.value = isConnected + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/rickmorty/characters/CharacterListFragment.kt b/app/src/main/java/com/example/rickmorty/characters/CharacterListFragment.kt index 967f348..531ac02 100644 --- a/app/src/main/java/com/example/rickmorty/characters/CharacterListFragment.kt +++ b/app/src/main/java/com/example/rickmorty/characters/CharacterListFragment.kt @@ -9,17 +9,19 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import com.airbnb.epoxy.EpoxyRecyclerView +import com.airbnb.lottie.LottieAnimationView +import com.example.rickmorty.NetworkViewModel import com.example.rickmorty.R import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch class CharacterListFragment : Fragment() { - - val epoxyController = CharacterListPagingEpoxyController( + private lateinit var networkViewModel: NetworkViewModel + private val epoxyController = CharacterListPagingEpoxyController( ::onCharacterClicked ) - val viewModel: CharacterListViewModel by lazy { + private val viewModel: CharacterListViewModel by lazy { ViewModelProvider(this).get(CharacterListViewModel::class.java) } @@ -33,14 +35,32 @@ class CharacterListFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - lifecycleScope.launch { - viewModel.pagingDataFlow.collectLatest { - epoxyController.submitData(it) + view.findViewById(R.id.loading).visibility = View.VISIBLE + view.findViewById(R.id.no_internet).visibility = View.GONE + + networkViewModel = ViewModelProvider(this).get(NetworkViewModel::class.java) + + + networkViewModel.getNetworkState().observe(viewLifecycleOwner, { + if (it) { + view.findViewById(R.id.loading).visibility = View.GONE + view.findViewById(R.id.epoxy_character_recycler_view).visibility = View.VISIBLE + view.findViewById(R.id.no_internet).visibility = View.GONE + lifecycleScope.launch { + viewModel.pagingDataFlow.collectLatest { + epoxyController.submitData(it) + } + } + } else { + view.findViewById(R.id.loading).visibility = View.GONE + view.findViewById(R.id.epoxy_character_recycler_view).visibility = View.GONE + view.findViewById(R.id.no_internet).visibility = View.VISIBLE } - } + }) + + view.findViewById(R.id.epoxy_character_recycler_view) .setController(epoxyController) - } private fun onCharacterClicked(characterId: Int) { diff --git a/app/src/main/java/com/example/rickmorty/characters/CharacterListPagingDataSource.kt b/app/src/main/java/com/example/rickmorty/characters/CharacterListPagingDataSource.kt index d1db264..2fdf02e 100644 --- a/app/src/main/java/com/example/rickmorty/characters/CharacterListPagingDataSource.kt +++ b/app/src/main/java/com/example/rickmorty/characters/CharacterListPagingDataSource.kt @@ -11,6 +11,7 @@ private const val TAG = "PageResult" class CharacterListPagingDataSource( private val repository: CharacterListRepository ) : PagingSource() { + override suspend fun load(params: LoadParams): LoadResult { return try { val pageNumber = params.key ?: 1 diff --git a/app/src/main/java/com/example/rickmorty/characters/CharacterListPagingEpoxyController.kt b/app/src/main/java/com/example/rickmorty/characters/CharacterListPagingEpoxyController.kt index d564c76..9a7bbca 100644 --- a/app/src/main/java/com/example/rickmorty/characters/CharacterListPagingEpoxyController.kt +++ b/app/src/main/java/com/example/rickmorty/characters/CharacterListPagingEpoxyController.kt @@ -9,49 +9,52 @@ import com.example.rickmorty.databinding.ModelCharacterListTitleBinding import com.example.rickmorty.epoxy.LoadingEpoxyModel import com.example.rickmorty.network.response.GetCharacterByIdResponse import com.squareup.picasso.Picasso -import java.util.* class CharacterListPagingEpoxyController( private val onCharacterClicked: (Int) -> Unit ) : PagingDataEpoxyController() { + + var isLoading: Boolean = true + set(value) { + field = value + if (field) { + requestModelBuild() + } + } + + var characterList: GetCharacterByIdResponse? = null + set(value) { + field = value + if (field != null) { + isLoading = false + requestModelBuild() + } + } + override fun buildItemModel(currentPosition: Int, item: GetCharacterByIdResponse?): EpoxyModel<*> { + + if (item != null) { + isLoading = false + } + + return GridModelForEpoxyPaging( characterId = item!!.id, name = item!!.name, imageUrl = item!!.image, onCharacterClicked = onCharacterClicked ).id(item.id) - } - // this function add all build models to adatpter, - // override this to add new model or remove one - // this require a list of EpoxyModel list of any type has that moment of any , that was returned from above function - override fun addModels(models: List>) { - - // loading status - if (models.isEmpty()) { - LoadingEpoxyModel().id("loading").addTo(this) - return - } - TitleForGridModels("Main Family") - .id("main_family_header") - .addTo(this) - - super.addModels(models.subList(0, 5)) + } - (models.subList(5, models.size) as List).groupBy { - it.name[0].toUpperCase() - }.forEach { - val title = it.key.toString().toUpperCase(Locale.US) - TitleForGridModels(title) - .id(title) - .addTo(this) - super.addModels(it.value) + override fun addModels(models: List>) { + if (isLoading || models.isEmpty()) { + LoadingEpoxyModel().id("load_list").addTo(this) } + super.addModels(models) } - data class GridModelForEpoxyPaging( val characterId: Int, val name: String, diff --git a/app/src/main/java/com/example/rickmorty/characters/CharacterListViewModel.kt b/app/src/main/java/com/example/rickmorty/characters/CharacterListViewModel.kt index 2838d83..7007915 100644 --- a/app/src/main/java/com/example/rickmorty/characters/CharacterListViewModel.kt +++ b/app/src/main/java/com/example/rickmorty/characters/CharacterListViewModel.kt @@ -26,4 +26,6 @@ class CharacterListViewModel : ViewModel() { ) val pagingDataFlow: Flow> = pager.flow.cachedIn(viewModelScope) + + } \ No newline at end of file diff --git a/app/src/main/java/com/example/rickmorty/episodes/EpisodeListPagingSource.kt b/app/src/main/java/com/example/rickmorty/episodes/EpisodeListPagingSource.kt index 5adc917..a507239 100644 --- a/app/src/main/java/com/example/rickmorty/episodes/EpisodeListPagingSource.kt +++ b/app/src/main/java/com/example/rickmorty/episodes/EpisodeListPagingSource.kt @@ -11,18 +11,13 @@ class EpisodeListPagingSource( override suspend fun load(params: LoadParams): LoadResult { return try { - var nextPageNumber = params.key ?: 1 - val prevPageNumber = if (nextPageNumber == 1) { - null - } else { - nextPageNumber - 1 - } + var pageNumber = params.key ?: 1 // do a network call - val episodePageRequest = repository.fetchEpisodes(nextPageNumber) + val episodePageRequest = repository.fetchEpisodes(pageNumber) val episodeResult = episodePageRequest?.results - + var nextPageNumber: Int? = null if (episodePageRequest?.info?.next != null) { val uri = Uri.parse(episodePageRequest.info.next) val nextPageQuery = uri.getQueryParameter("page") @@ -34,7 +29,7 @@ class EpisodeListPagingSource( } LoadResult.Page( data = episodeList.orEmpty(), - prevKey = prevPageNumber, + prevKey = null, nextKey = nextPageNumber ) } catch (e: Exception) { @@ -44,8 +39,6 @@ class EpisodeListPagingSource( } - override val keyReuseSupported: Boolean = true - override fun getRefreshKey(state: PagingState): Int? { // Try to find the page key of the closest page to anchorPosition, from // either the prevKey or the nextKey, but you need to handle nullability @@ -54,9 +47,10 @@ class EpisodeListPagingSource( // * nextKey == null -> anchorPage is the last page. // * both prevKey and nextKey null -> anchorPage is the initial page, so // just return null. - return state.anchorPosition?.let { anchorPosition -> - val anchorPage = state.closestPageToPosition(anchorPosition) - anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1) - } + /* return state.anchorPosition?.let { anchorPosition -> + val anchorPage = state.closestPageToPosition(anchorPosition) + anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1) + }*/ + TODO("fnr") } } \ No newline at end of file diff --git a/app/src/main/java/com/example/rickmorty/epoxy/ErrorEpoxyModel.kt b/app/src/main/java/com/example/rickmorty/epoxy/ErrorEpoxyModel.kt new file mode 100644 index 0000000..6949d27 --- /dev/null +++ b/app/src/main/java/com/example/rickmorty/epoxy/ErrorEpoxyModel.kt @@ -0,0 +1,15 @@ +package com.example.rickmorty.epoxy + +import com.example.rickmorty.R +import com.example.rickmorty.ViewBindingKotlinModel +import com.example.rickmorty.databinding.ModelErrorLoadingBinding + +data class ErrorEpoxyModel( + val title: String +) : ViewBindingKotlinModel(R.layout.model_loading_error) { + override fun ModelErrorLoadingBinding.bind() { + textView2.text = title + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/rickmorty/epoxy/LoadingEpoxyModel.kt b/app/src/main/java/com/example/rickmorty/epoxy/LoadingEpoxyModel.kt index ce9b0d8..6bfcc9c 100644 --- a/app/src/main/java/com/example/rickmorty/epoxy/LoadingEpoxyModel.kt +++ b/app/src/main/java/com/example/rickmorty/epoxy/LoadingEpoxyModel.kt @@ -6,5 +6,10 @@ import com.example.rickmorty.databinding.ModelLoadingBinding class LoadingEpoxyModel: ViewBindingKotlinModel(R.layout.model_loading){ override fun ModelLoadingBinding.bind() { + + } + + override fun getSpanSize(totalSpanCount: Int, position: Int, itemCount: Int): Int { + return totalSpanCount } } \ No newline at end of file diff --git a/app/src/main/java/com/example/rickmorty/network/NetworkLayer.kt b/app/src/main/java/com/example/rickmorty/network/NetworkLayer.kt index 76c07c8..b965c29 100644 --- a/app/src/main/java/com/example/rickmorty/network/NetworkLayer.kt +++ b/app/src/main/java/com/example/rickmorty/network/NetworkLayer.kt @@ -15,13 +15,13 @@ object NetworkLayer { .add(KotlinJsonAdapterFactory()) .build() - val retrofit: Retrofit = Retrofit.Builder() + private val retrofit: Retrofit = Retrofit.Builder() .client(getLoggingHttpClient()) .baseUrl("https://rickandmortyapi.com/api/") .addConverterFactory(MoshiConverterFactory.create(moshi)) .build() - val rickAndMortyService: RickAndMortyService by lazy { + private val rickAndMortyService: RickAndMortyService by lazy { retrofit.create(RickAndMortyService::class.java) } diff --git a/app/src/main/res/layout/fragment_character__list.xml b/app/src/main/res/layout/fragment_character__list.xml index 1129237..ad03e2d 100644 --- a/app/src/main/res/layout/fragment_character__list.xml +++ b/app/src/main/res/layout/fragment_character__list.xml @@ -1,10 +1,22 @@ - + + + + app:spanCount="2"/> + + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/model_error_loading.xml b/app/src/main/res/layout/model_error_loading.xml new file mode 100644 index 0000000..b342955 --- /dev/null +++ b/app/src/main/res/layout/model_error_loading.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/model_loading.xml b/app/src/main/res/layout/model_loading.xml index 7c0d66a..253b0f5 100644 --- a/app/src/main/res/layout/model_loading.xml +++ b/app/src/main/res/layout/model_loading.xml @@ -1,18 +1,20 @@ - + - + \ No newline at end of file