Skip to content

Instantly share code, notes, and snippets.

@haerulmuttaqin
Created March 15, 2021 22:36
Show Gist options
  • Save haerulmuttaqin/84bda70bbec453f686d3bd890a9c2da1 to your computer and use it in GitHub Desktop.
Save haerulmuttaqin/84bda70bbec453f686d3bd890a9c2da1 to your computer and use it in GitHub Desktop.
#1 — Job Finder App (https://youtu.be/YlB0Oulip4o)
import dagger.Module;
import dagger.android.ContributesAndroidInjector;
import id.haerulmuttaqin.jobfinder.ui.MainActivity;
@Module
public abstract class ActivityBuilder {
@ContributesAndroidInjector
abstract MainActivity mainActivity();
}
import java.util.List;
import id.haerulmuttaqin.jobfinder.data.entity.GithubJob;
import retrofit2.Call;
import retrofit2.http.GET;
public interface ApiInterface {
@GET("positions.json")
Call<List<GithubJob>> getJobList();
}
import java.util.List;
import javax.inject.Inject;
import id.haerulmuttaqin.jobfinder.data.entity.GithubJob;
import retrofit2.Call;
public class ConnectionServer {
@Inject
ApiInterface apiInterface;
public ConnectionServer(ApiInterface apiInterface) {
this.apiInterface = apiInterface;
}
public Call<List<GithubJob>> getJobList() {
return apiInterface.getJobList();
};
}
public class GithubJob {
@SerializedName("id")
@Expose
public String id;
@SerializedName("type")
@Expose
public String type;
@SerializedName("url")
@Expose
public String url;
@SerializedName("created_at")
@Expose
public String createdAt;
@SerializedName("company")
@Expose
public String company;
@SerializedName("company_url")
@Expose
public String companyUrl;
@SerializedName("location")
@Expose
public String location;
@SerializedName("title")
@Expose
public String title;
@SerializedName("description")
@Expose
public String description;
@SerializedName("how_to_apply")
@Expose
public String howToApply;
@SerializedName("company_logo")
@Expose
public String companyLogo;
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View" />
<variable
name="item"
type="id.haerulmuttaqin.jobfinder.data.entity.GithubJob" />
</data>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_marginTop="8dp"
app:cardCornerRadius="10dp"
app:cardElevation="0dp"
app:strokeColor="#f1f1f1"
app:strokeWidth="1dp">
<LinearLayout
android:orientation="vertical"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginBottom="9dp"
android:padding="4dp" >
<com.google.android.material.card.MaterialCardView
android:id="@+id/photoPreviewLayout"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginStart="6dp"
android:src="@drawable/ic_photo"
app:strokeWidth="0.5dp"
app:strokeColor="#f1f1f1"
app:cardElevation="0dp"
app:cardCornerRadius="8dp">
<ImageView
android:id="@+id/photoPreview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerInside"
android:layout_margin="5dp"
tools:ignore="UnusedAttribute" />
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/progress"
android:layout_width="60dp"
android:layout_height="60dp"
android:orientation="vertical"
app:shimmer_auto_start="true"
app:shimmer_duration="800">
<View
android:layout_width="60dp"
android:layout_height="60dp"
android:gravity="center"
android:layout_gravity="center"
android:background="@color/gray_100" />
</com.facebook.shimmer.ShimmerFrameLayout>
</com.google.android.material.card.MaterialCardView>
<LinearLayout
android:layout_toEndOf="@id/photoPreviewLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/title"
android:ellipsize="end"
android:maxLines="2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="@{item.title}"
android:textColor="@color/gray_500"
android:textSize="18sp"
android:layout_marginBottom="3dp"
android:fontFamily="@font/bold"/>
<RelativeLayout
android:layout_marginStart="14dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/lampIcon"
android:src="@drawable/ic_round_business_24"
app:tint="@color/gray_300"
android:layout_marginEnd="5dp"
android:layout_width="15dp"
android:layout_height="16sp"/>
<TextView
android:layout_toEndOf="@id/lampIcon"
android:id="@+id/lamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/gray_400"
android:text="@{item.company}"
android:textSize="12sp"
android:singleLine="true"
android:ellipsize="end"
android:fontFamily="@font/book"/>
</RelativeLayout>
<RelativeLayout
android:layout_marginStart="14dp"
android:layout_marginTop="3dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/locIcon"
android:src="@drawable/ic_outline_location_on_24"
app:tint="@color/gray_300"
android:layout_marginEnd="5dp"
android:layout_width="15dp"
android:layout_height="16sp"/>
<TextView
android:layout_toEndOf="@id/locIcon"
android:id="@+id/address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/gray_400"
android:text="@{item.location}"
android:textSize="12sp"
android:singleLine="true"
android:ellipsize="end"
android:fontFamily="@font/book"/>
</RelativeLayout>
<RelativeLayout
android:layout_marginStart="14dp"
android:layout_marginTop="3dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:paddingStart="6dp"
android:paddingEnd="6dp"
android:paddingTop="2dp"
android:paddingBottom="2dp"
android:background="@drawable/bg_job_type"
android:textColor="@color/blue_500"
android:text="@{item.type}"
android:textSize="10sp"
android:fontFamily="@font/book"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="6dp"
android:paddingStart="6dp"
android:paddingEnd="6dp"
android:paddingTop="2dp"
android:paddingBottom="2dp"
android:layout_alignParentEnd="true"
android:textColor="@color/gray_300"
android:text="@{item.createdAt}"
android:textSize="11sp"
android:fontFamily="@font/book"/>
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import javax.inject.Inject;
import id.haerulmuttaqin.jobfinder.R;
import id.haerulmuttaqin.jobfinder.base.BaseActivity;
import id.haerulmuttaqin.jobfinder.data.api.ConnectionServer;
import id.haerulmuttaqin.jobfinder.databinding.ActivityMainBinding;
public class MainActivity extends BaseActivity<ActivityMainBinding, MainViewModel> implements MainViewModel.Navigator {
@Inject ConnectionServer server;
private ActivityMainBinding binding;
private MainViewModel viewModel;
@Override
public int getBindingVariable() {
return 0;
}
@Override
public int getLayoutId() {
return R.layout.activity_main;
}
@Override
public MainViewModel getViewModel() {
return viewModel;
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = getViewDataBinding();
viewModel = new ViewModelProvider(this, new MainViewModel.ModelFactory(this, server)).get(MainViewModel.class);
viewModel.setNavigator(this);
viewModel.getJobFromServer();
viewModel.jobList.observe(this, githubJobs -> {
binding.recyclerView.setAdapter(new MainAdapter(githubJobs));
});
}
}
import android.annotation.SuppressLint;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import java.util.List;
import id.haerulmuttaqin.jobfinder.R;
import id.haerulmuttaqin.jobfinder.data.entity.GithubJob;
import id.haerulmuttaqin.jobfinder.databinding.ItemJobBinding;
public class MainAdapter extends RecyclerView.Adapter<MainAdapter.RecyclerViewAdapter> {
private List<GithubJob> data;
protected MainAdapter(List<GithubJob> data) {
this.data = data;
}
@NonNull
@Override
public RecyclerViewAdapter onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
ItemJobBinding binding = DataBindingUtil.inflate(inflater, R.layout.item_job, parent, false);
return new RecyclerViewAdapter(binding);
}
public void clear() {
int size = data.size();
data.clear();
notifyItemRangeRemoved(0, size);
notifyDataSetChanged();
}
@Override
public void onBindViewHolder(RecyclerViewAdapter holder, int position) {
GithubJob items = data.get(position);
if(items != null) {
holder.bind(items);
}
}
@Override
public int getItemCount() {
return data == null ? 0 : data.size();
}
public static class RecyclerViewAdapter extends RecyclerView.ViewHolder {
ItemJobBinding binding;
public RecyclerViewAdapter(ItemJobBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
@SuppressLint("UseCompatLoadingForDrawables")
void bind(@NonNull GithubJob data) {
Glide.with(binding.photoPreview.getContext())
.load(data.companyLogo)
.error(binding.photoPreview.getContext().getDrawable(R.drawable.ic_round_business_center_24))
.transition(DrawableTransitionOptions.withCrossFade())
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
binding.progress.stopShimmer();
binding.progress.setVisibility(View.GONE);
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
binding.progress.stopShimmer();
binding.progress.setVisibility(View.GONE);
return false;
}
})
.into(binding.photoPreview);
binding.setItem(data);
binding.executePendingBindings();
}
}
}
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import java.util.List;
import id.haerulmuttaqin.jobfinder.base.BaseViewModel;
import id.haerulmuttaqin.jobfinder.data.api.ConnectionServer;
import id.haerulmuttaqin.jobfinder.data.entity.GithubJob;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class MainViewModel extends BaseViewModel<MainViewModel.Navigator> {
MutableLiveData<List<GithubJob>> jobList = new MutableLiveData<>();
public MainViewModel(Context context, ConnectionServer connectionServer) {
super(context, connectionServer);
}
public void getJobFromServer() {
getNavigator().showProgress();
getConnectionServer().getJobList().enqueue(new Callback<List<GithubJob>>() {
@Override
public void onResponse(Call<List<GithubJob>> call, Response<List<GithubJob>> response) {
if (response.isSuccessful() && response.body().size() > 0) {
jobList.postValue(response.body());
}
getNavigator().hideProgress();
}
@Override
public void onFailure(Call<List<GithubJob>> call, Throwable t) {
t.getLocalizedMessage();
getNavigator().hideProgress();
}
});
}
public static class ModelFactory implements ViewModelProvider.Factory {
private Context context;
private ConnectionServer server;
public ModelFactory(Context context, ConnectionServer server) {
this.context = context;
this.server = server;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return (T) new MainViewModel(context, server);
}
}
interface Navigator {
void showProgress();
void hideProgress();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment