Skip to content

Commit 94ff992

Browse files
Add Retrofit networking lesson
1 parent 71e5d7d commit 94ff992

File tree

40 files changed

+669
-22
lines changed

40 files changed

+669
-22
lines changed

app/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ dependencies {
100100
implementation libs.androidx.room.runtime
101101
annotationProcessor libs.androidx.room.compiler
102102
annotationProcessor libs.glide.compiler
103+
implementation libs.retrofit2
104+
implementation libs.retrofit2.converter.gson
103105

104106
// Testing
105107
testImplementation libs.junit

app/src/main/AndroidManifest.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,18 @@
316316
android:label="@string/room_database"
317317
android:parentActivityName=".ui.screens.android.lessons.data.room.RoomActivity" />
318318

319+
<activity
320+
android:name=".ui.screens.android.lessons.networking.retrofit.RetrofitActivity"
321+
android:exported="false"
322+
android:label="@string/retrofit"
323+
android:parentActivityName=".ui.screens.main.MainActivity" />
324+
325+
<activity
326+
android:name=".ui.screens.android.lessons.networking.retrofit.RetrofitCodeActivity"
327+
android:exported="false"
328+
android:label="@string/retrofit"
329+
android:parentActivityName=".ui.screens.android.lessons.networking.retrofit.RetrofitActivity" />
330+
319331
<activity
320332
android:name=".ui.screens.settings.SettingsActivity"
321333
android:exported="false"

app/src/main/assets/quiz_questions.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,10 @@
2323
"question": "What component shows a scrollable list of items?",
2424
"options": ["RecyclerView", "TextView", "ImageView", "Button"],
2525
"answer": 0
26+
},
27+
{
28+
"question": "Which library simplifies HTTP requests by turning APIs into interfaces?",
29+
"options": ["Volley", "Retrofit", "Glide", "Room"],
30+
"answer": 1
2631
}
2732
]
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package com.d4rk.androidtutorials.java.ui.screens.android.lessons.networking.retrofit;
2+
3+
import android.content.Intent;
4+
import android.os.Bundle;
5+
import android.os.Handler;
6+
import android.os.Looper;
7+
8+
import androidx.annotation.Nullable;
9+
10+
import com.d4rk.androidtutorials.java.databinding.ActivityRetrofitBinding;
11+
import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity;
12+
import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate;
13+
14+
import com.d4rk.androidtutorials.java.R;
15+
16+
import retrofit2.Call;
17+
import retrofit2.Callback;
18+
import retrofit2.Response;
19+
import retrofit2.Retrofit;
20+
import retrofit2.converter.gson.GsonConverterFactory;
21+
import retrofit2.http.GET;
22+
23+
/**
24+
* Demonstrates a simple HTTP request using Retrofit.
25+
*/
26+
public class RetrofitActivity extends UpNavigationActivity {
27+
private ActivityRetrofitBinding binding;
28+
private final Handler handler = new Handler(Looper.getMainLooper());
29+
private JsonPlaceholderApi api;
30+
31+
interface JsonPlaceholderApi {
32+
@GET("todos/1")
33+
Call<Todo> getTodo();
34+
}
35+
36+
static class Todo {
37+
public String title;
38+
}
39+
40+
@Override
41+
protected void onCreate(@Nullable Bundle savedInstanceState) {
42+
super.onCreate(savedInstanceState);
43+
binding = ActivityRetrofitBinding.inflate(getLayoutInflater());
44+
setContentView(binding.getRoot());
45+
46+
EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this);
47+
edgeToEdgeDelegate.applyEdgeToEdge(binding.constraintLayout);
48+
49+
Retrofit retrofit = new Retrofit.Builder()
50+
.baseUrl("https://jsonplaceholder.typicode.com/")
51+
.addConverterFactory(GsonConverterFactory.create())
52+
.build();
53+
api = retrofit.create(JsonPlaceholderApi.class);
54+
55+
binding.buttonFetch.setOnClickListener(v -> {
56+
binding.buttonFetch.setEnabled(false);
57+
api.getTodo().enqueue(new Callback<Todo>() {
58+
@Override
59+
public void onResponse(Call<Todo> call, Response<Todo> response) {
60+
if (response.isSuccessful() && response.body() != null) {
61+
binding.textViewResult.setText(response.body().title);
62+
} else {
63+
binding.textViewResult.setText(R.string.snack_general_error);
64+
}
65+
binding.buttonFetch.setEnabled(true);
66+
}
67+
68+
@Override
69+
public void onFailure(Call<Todo> call, Throwable t) {
70+
binding.textViewResult.setText(R.string.snack_general_error);
71+
binding.buttonFetch.setEnabled(true);
72+
}
73+
});
74+
});
75+
76+
binding.floatingButtonShowSyntax.setOnClickListener(v ->
77+
startActivity(new Intent(RetrofitActivity.this, RetrofitCodeActivity.class)));
78+
handler.postDelayed(() -> binding.floatingButtonShowSyntax.shrink(), 5000);
79+
}
80+
81+
@Override
82+
protected void onDestroy() {
83+
super.onDestroy();
84+
handler.removeCallbacksAndMessages(null);
85+
}
86+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.d4rk.androidtutorials.java.ui.screens.android.lessons.networking.retrofit;
2+
3+
import android.os.Bundle;
4+
5+
import androidx.annotation.NonNull;
6+
import androidx.annotation.Nullable;
7+
import androidx.fragment.app.Fragment;
8+
import androidx.viewpager2.adapter.FragmentStateAdapter;
9+
10+
import com.d4rk.androidtutorials.java.R;
11+
import com.d4rk.androidtutorials.java.databinding.ActivityTabLayoutBinding;
12+
import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity;
13+
import com.d4rk.androidtutorials.java.ui.screens.android.lessons.networking.retrofit.tabs.RetrofitTabCodeFragment;
14+
import com.d4rk.androidtutorials.java.ui.screens.android.lessons.networking.retrofit.tabs.RetrofitTabLayoutFragment;
15+
import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate;
16+
import com.google.android.material.tabs.TabLayoutMediator;
17+
18+
import java.util.ArrayList;
19+
20+
/**
21+
* Displays Java and XML snippets for the Retrofit example.
22+
*/
23+
public class RetrofitCodeActivity extends UpNavigationActivity {
24+
private ActivityTabLayoutBinding binding;
25+
26+
@Override
27+
protected void onCreate(@Nullable Bundle savedInstanceState) {
28+
super.onCreate(savedInstanceState);
29+
binding = ActivityTabLayoutBinding.inflate(getLayoutInflater());
30+
setContentView(binding.getRoot());
31+
32+
EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this);
33+
edgeToEdgeDelegate.applyEdgeToEdge(binding.appBarLayout);
34+
35+
setupViewPager();
36+
37+
new TabLayoutMediator(binding.tabs, binding.viewpager, (tab, position) -> {
38+
ViewPagerAdapter adapter = (ViewPagerAdapter) binding.viewpager.getAdapter();
39+
if (adapter != null) {
40+
tab.setText(adapter.getPageTitle(position));
41+
}
42+
}).attach();
43+
}
44+
45+
private void setupViewPager() {
46+
ViewPagerAdapter adapter = new ViewPagerAdapter(this);
47+
adapter.addFragment(new RetrofitTabCodeFragment(), getString(R.string.code_java));
48+
adapter.addFragment(new RetrofitTabLayoutFragment(), getString(R.string.layout_xml));
49+
binding.viewpager.setAdapter(adapter);
50+
}
51+
52+
private static class ViewPagerAdapter extends FragmentStateAdapter {
53+
private final ArrayList<Fragment> fragmentList = new ArrayList<>();
54+
private final ArrayList<String> fragmentTitleList = new ArrayList<>();
55+
56+
ViewPagerAdapter(@NonNull UpNavigationActivity activity) {
57+
super(activity);
58+
}
59+
60+
void addFragment(@NonNull Fragment fragment, @NonNull String title) {
61+
fragmentList.add(fragment);
62+
fragmentTitleList.add(title);
63+
}
64+
65+
@NonNull
66+
@Override
67+
public Fragment createFragment(int position) {
68+
return fragmentList.get(position);
69+
}
70+
71+
@Override
72+
public int getItemCount() {
73+
return fragmentList.size();
74+
}
75+
76+
CharSequence getPageTitle(int position) {
77+
return fragmentTitleList.get(position);
78+
}
79+
}
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.d4rk.androidtutorials.java.ui.screens.android.lessons.networking.retrofit.tabs;
2+
3+
import android.content.SharedPreferences;
4+
import android.graphics.Typeface;
5+
import android.os.Bundle;
6+
import android.util.Log;
7+
import android.view.LayoutInflater;
8+
import android.view.View;
9+
import android.view.ViewGroup;
10+
11+
import androidx.annotation.NonNull;
12+
import androidx.fragment.app.Fragment;
13+
import androidx.preference.PreferenceManager;
14+
15+
import com.d4rk.androidtutorials.java.R;
16+
import com.d4rk.androidtutorials.java.databinding.FragmentCodeBinding;
17+
import com.d4rk.androidtutorials.java.utils.CodeHighlighter;
18+
import com.d4rk.androidtutorials.java.utils.CodeViewUtils;
19+
import com.d4rk.androidtutorials.java.utils.FontManager;
20+
import com.google.android.gms.ads.AdRequest;
21+
22+
import java.io.BufferedReader;
23+
import java.io.IOException;
24+
import java.io.InputStream;
25+
import java.io.InputStreamReader;
26+
27+
import me.zhanghai.android.fastscroll.FastScrollerBuilder;
28+
29+
/**
30+
* Shows the Java implementation for the Retrofit example.
31+
*/
32+
public class RetrofitTabCodeFragment extends Fragment {
33+
34+
@Override
35+
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
36+
FragmentCodeBinding binding = FragmentCodeBinding.inflate(inflater, container, false);
37+
new FastScrollerBuilder(binding.scrollView).useMd2Style().build();
38+
binding.adView.loadAd(new AdRequest.Builder().build());
39+
40+
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext());
41+
Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs);
42+
CodeViewUtils.applyDefaults(monospaceFont, binding.codeView);
43+
44+
StringBuilder builder = new StringBuilder();
45+
InputStream inputStream = getResources().openRawResource(R.raw.text_retrofit_java);
46+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
47+
String line;
48+
while ((line = reader.readLine()) != null) {
49+
builder.append(line).append('\n');
50+
}
51+
binding.codeView.setText(builder.toString());
52+
CodeHighlighter.applyJavaTheme(binding.codeView);
53+
} catch (IOException e) {
54+
Log.e("RetrofitTabCode", "Error reading code", e);
55+
}
56+
return binding.getRoot();
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.d4rk.androidtutorials.java.ui.screens.android.lessons.networking.retrofit.tabs;
2+
3+
import android.content.SharedPreferences;
4+
import android.graphics.Typeface;
5+
import android.os.Bundle;
6+
import android.util.Log;
7+
import android.view.LayoutInflater;
8+
import android.view.View;
9+
import android.view.ViewGroup;
10+
11+
import androidx.annotation.NonNull;
12+
import androidx.fragment.app.Fragment;
13+
import androidx.preference.PreferenceManager;
14+
15+
import com.d4rk.androidtutorials.java.R;
16+
import com.d4rk.androidtutorials.java.databinding.FragmentLayoutBinding;
17+
import com.d4rk.androidtutorials.java.utils.CodeHighlighter;
18+
import com.d4rk.androidtutorials.java.utils.CodeViewUtils;
19+
import com.d4rk.androidtutorials.java.utils.FontManager;
20+
import com.google.android.gms.ads.AdRequest;
21+
22+
import java.io.BufferedReader;
23+
import java.io.IOException;
24+
import java.io.InputStream;
25+
import java.io.InputStreamReader;
26+
27+
import me.zhanghai.android.fastscroll.FastScrollerBuilder;
28+
29+
/**
30+
* Shows the XML layout for the Retrofit example.
31+
*/
32+
public class RetrofitTabLayoutFragment extends Fragment {
33+
34+
@Override
35+
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
36+
FragmentLayoutBinding binding = FragmentLayoutBinding.inflate(inflater, container, false);
37+
new FastScrollerBuilder(binding.scrollView).useMd2Style().build();
38+
binding.adView.loadAd(new AdRequest.Builder().build());
39+
40+
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext());
41+
Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs);
42+
CodeViewUtils.applyDefaults(monospaceFont, binding.codeView);
43+
44+
StringBuilder builder = new StringBuilder();
45+
InputStream inputStream = getResources().openRawResource(R.raw.text_retrofit_xml);
46+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
47+
String line;
48+
while ((line = reader.readLine()) != null) {
49+
builder.append(line).append('\n');
50+
}
51+
binding.codeView.setText(builder.toString());
52+
CodeHighlighter.applyXmlTheme(binding.codeView);
53+
} catch (IOException e) {
54+
Log.e("RetrofitTabLayout", "Error reading layout", e);
55+
}
56+
return binding.getRoot();
57+
}
58+
}

app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/repository/LessonRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public com.d4rk.androidtutorials.java.data.repository.LessonRepository.Lesson ge
3838
new com.d4rk.androidtutorials.java.data.repository.LessonRepository.Lesson(R.string.grid_view, R.raw.text_grid_view_java, R.raw.text_grid_view_xml);
3939
case "WebView" ->
4040
new com.d4rk.androidtutorials.java.data.repository.LessonRepository.Lesson(R.string.web_view, R.raw.text_webview_java, R.raw.text_webview_xml);
41+
case "Retrofit" ->
42+
new com.d4rk.androidtutorials.java.data.repository.LessonRepository.Lesson(R.string.retrofit, R.raw.text_retrofit_java, R.raw.text_retrofit_xml);
4143
case "BottomNavigation" ->
4244
new com.d4rk.androidtutorials.java.data.repository.LessonRepository.Lesson(R.string.bottom_navigation, R.raw.text_bottom_navigation_java, R.raw.text_bottom_navigation_xml);
4345
case "NavigationDrawer" ->
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:fillColor="?attr/colorSecondary"
8+
android:pathData="M6.5,20Q5.88,20 5.44,19.56Q5,19.12 5,18.5L5,15.5Q5,14.88 5.44,14.44Q5.88,14 6.5,14Q7.12,14 7.56,14.44Q8,14.88 8,15.5L8,18.5Q8,19.12 7.56,19.56Q7.12,20 6.5,20ZM12.5,20Q11.88,20 11.44,19.56Q11,19.12 11,18.5L11,10.5Q11,9.88 11.44,9.44Q11.88,9 12.5,9Q13.12,9 13.56,9.44Q14,9.88 14,10.5L14,18.5Q14,19.12 13.56,19.56Q13.12,20 12.5,20ZM18.5,20Q17.88,20 17.44,19.56Q17,19.12 17,18.5L17,5.5Q17,4.88 17.44,4.44Q17.88,4 18.5,4Q19.12,4 19.56,4.44Q20,4.88 20,5.5L20,18.5Q20,19.12 19.56,19.56Q19.12,20 18.5,20Z"/>
9+
</vector>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
android:id="@+id/constraintLayout"
5+
android:layout_width="match_parent"
6+
android:layout_height="match_parent">
7+
8+
<com.google.android.material.textview.MaterialTextView
9+
android:id="@+id/textViewSummary"
10+
android:layout_width="0dp"
11+
android:layout_height="wrap_content"
12+
android:padding="16dp"
13+
android:text="@string/summary_retrofit"
14+
app:layout_constraintEnd_toEndOf="parent"
15+
app:layout_constraintStart_toStartOf="parent"
16+
app:layout_constraintTop_toTopOf="parent" />
17+
18+
<com.google.android.material.button.MaterialButton
19+
android:id="@+id/buttonFetch"
20+
style="@style/Widget.Material3.Button.ElevatedButton"
21+
android:layout_width="wrap_content"
22+
android:layout_height="wrap_content"
23+
android:layout_marginTop="16dp"
24+
android:text="@string/fetch_data"
25+
app:layout_constraintEnd_toEndOf="parent"
26+
app:layout_constraintStart_toStartOf="parent"
27+
app:layout_constraintTop_toBottomOf="@id/textViewSummary" />
28+
29+
<com.google.android.material.textview.MaterialTextView
30+
android:id="@+id/textViewResult"
31+
android:layout_width="0dp"
32+
android:layout_height="wrap_content"
33+
android:layout_marginTop="16dp"
34+
android:padding="16dp"
35+
app:layout_constraintEnd_toEndOf="parent"
36+
app:layout_constraintStart_toStartOf="parent"
37+
app:layout_constraintTop_toBottomOf="@id/buttonFetch" />
38+
39+
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
40+
android:id="@+id/floating_button_show_syntax"
41+
style="@style/Widget.Material3.ExtendedFloatingActionButton.Icon.Surface"
42+
android:layout_width="wrap_content"
43+
android:layout_height="wrap_content"
44+
android:layout_margin="24dp"
45+
android:contentDescription="@string/tooltip_show_java_code_snippet"
46+
android:text="@string/show_code"
47+
android:textSize="14sp"
48+
android:tooltipText="@string/tooltip_show_java_code_snippet"
49+
app:icon="@drawable/ic_code"
50+
app:layout_constraintBottom_toBottomOf="parent"
51+
app:layout_constraintEnd_toEndOf="parent" />
52+
53+
</androidx.constraintlayout.widget.ConstraintLayout>

0 commit comments

Comments
 (0)