Skip to content

Instantly share code, notes, and snippets.

@haerulmuttaqin
Created March 16, 2021 22:50
Show Gist options
  • Save haerulmuttaqin/e61cca6ceb211d55a43d15f63c8b9efb to your computer and use it in GitHub Desktop.
Save haerulmuttaqin/e61cca6ceb211d55a43d15f63c8b9efb to your computer and use it in GitHub Desktop.
#2 — Job Finder App (https://youtu.be/3GkEbf8AQSQ)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="id.haerulmuttaqin.jobfinder.ui.MainViewModel" />
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/blue_200">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="0dp">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapse"
android:layout_width="match_parent"
android:layout_height="280dp"
app:collapsedTitleTextAppearance="@style/collapsedTitleStyle"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginBottom="26dp"
app:expandedTitleMarginStart="26dp"
app:expandedTitleTextAppearance="@style/expandedTitleStyle"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
app:title="@string/app_name"
app:titleEnabled="true">
<RelativeLayout
app:layout_collapseMode="parallax"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/banner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/banner_1" />
<TextView
android:id="@+id/welcomeText"
android:text="Hi, Welcome back"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="26dp"
android:layout_marginTop="40dp"
android:layout_marginEnd="10dp"
android:fontFamily="@font/book"
android:textSize="16sp"
android:textColor="@color/white"
android:layout_toStartOf="@id/notif"/>
<TextView
android:id="@+id/secondaryText"
android:text="Find your perfect job"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/welcomeText"
android:layout_marginStart="26dp"
android:layout_marginTop="2dp"
android:layout_marginEnd="10dp"
android:layout_toStartOf="@id/notif"
android:textSize="26sp"
android:textColor="@color/white"
android:fontFamily="@font/bold" />
<ImageView
android:id="@+id/notif"
android:layout_width="26dp"
android:layout_height="40dp"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="42dp"
android:layout_marginEnd="16dp"
android:contentDescription="@string/app_name"
android:src="@drawable/ic_baseline_more_vert_24"
android:paddingBottom="10dp"
app:tint="@color/white"/>
<LinearLayout
android:id="@+id/searchLayout"
android:orientation="vertical"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="60dp"
app:strokeWidth="0.3dp"
app:cardCornerRadius="20dp"
app:cardElevation="0dp"
app:strokeColor="@color/blue_300"
app:cardBackgroundColor="@color/blue_250">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="60dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="What are you looking for?"
android:singleLine="true"
android:layout_centerVertical="true"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textColor="@color/blue_050"
android:fontFamily="@font/book"
android:textSize="16dp"/>
<ImageView
android:src="@drawable/ic_round_search_24"
android:layout_width="25dp"
android:layout_height="60dp"
android:layout_alignParentEnd="true"
android:layout_marginEnd="16dp"
app:tint="@color/blue_050"/>
</RelativeLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</RelativeLayout>
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="@style/Theme.JobFinder.Toolbar"
android:fitsSystemWindows="true"
app:background="@android:color/transparent"
app:layout_collapseMode="pin"
app:popupTheme="@style/Theme.JobFinder.PopupOverlay"
app:titleTextColor="@color/white" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_rounded_top"
app:layout_anchor="@id/appbar"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/shimmer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginTop="16dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:shimmer_duration="800"
app:shimmer_auto_start="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/item_job_placeholder" />
<include layout="@layout/item_job_placeholder" />
<include layout="@layout/item_job_placeholder" />
<include layout="@layout/item_job_placeholder" />
<include layout="@layout/item_job_placeholder" />
<include layout="@layout/item_job_placeholder" />
<include layout="@layout/item_job_placeholder" />
<include layout="@layout/item_job_placeholder" />
<include layout="@layout/item_job_placeholder" />
</LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout
android:id="@+id/emptyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:fillViewport="true"
android:clickable="false"
android:focusable="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_gravity="center"
android:layout_marginTop="100dp"
android:src="@drawable/ic_undraw_empty" />
<TextView
android:id="@+id/textEmpty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingStart="16dp"
android:paddingTop="16dp"
android:paddingEnd="16dp"
android:text="No Data Found"
android:fontFamily="@font/medium"/>
<TextView
android:id="@+id/textEmptyErr"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingStart="16dp"
android:paddingBottom="16dp"
android:paddingEnd="16dp"
android:fontFamily="@font/book"/>
</LinearLayout>
<ImageView
android:id="@+id/shadow"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginTop="16dp"
android:src="@drawable/bg_shadow_up_to_down"
android:visibility="visible" />
<ImageView
android:background="@drawable/bg_strip_round"
android:layout_width="40dp"
android:layout_height="5dp"
android:layout_marginTop="8dp"
android:layout_centerHorizontal="true"/>
</RelativeLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="10dp"
app:cardCornerRadius="10dp"
app:cardElevation="0dp"
app:strokeWidth="1dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp" >
<View
android:id="@+id/icon"
android:layout_width="60dp"
android:layout_height="60dp"
android:gravity="center"
android:layout_gravity="center"
android:layout_marginStart="10dp"
android:background="@drawable/bg_photo_item" />
<View
android:id="@+id/title"
android:layout_marginStart="16dp"
android:layout_toEndOf="@id/icon"
android:layout_width="180dp"
android:layout_height="17dp"
android:layout_marginTop="2dp"
android:layout_marginEnd="16dp"
android:background="@drawable/bg_strip_round" />
<View
android:id="@+id/subtitle"
android:layout_below="@id/title"
android:layout_marginStart="16dp"
android:layout_marginEnd="130dp"
android:layout_width="110dp"
android:layout_toEndOf="@id/icon"
android:layout_height="10dp"
android:layout_marginTop="10dp"
android:background="@drawable/bg_strip_round" />
<View
android:layout_below="@id/subtitle"
android:layout_marginStart="16dp"
android:layout_marginEnd="130dp"
android:layout_marginBottom="20dp"
android:layout_width="140dp"
android:layout_toEndOf="@id/icon"
android:layout_height="10dp"
android:layout_marginTop="5dp"
android:background="@drawable/bg_strip_round" />
</RelativeLayout>
</com.google.android.material.card.MaterialCardView>
import android.os.Bundle;
import android.view.View;
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));
});
binding.swipeRefresh.setOnRefreshListener(()->viewModel.getJobFromServer());
}
@Override
public void showProgress() {
binding.swipeRefresh.setRefreshing(true);
binding.emptyView.setVisibility(View.GONE);
binding.shimmer.setVisibility(View.VISIBLE);
binding.shimmer.startShimmer();
binding.recyclerView.setVisibility(View.GONE);
}
@Override
public void hideProgress() {
binding.swipeRefresh.setRefreshing(false);
binding.emptyView.setVisibility(View.GONE);
binding.shimmer.setVisibility(View.GONE);
binding.shimmer.stopShimmer();
binding.recyclerView.setVisibility(View.VISIBLE);
}
@Override
public void onGetResult(boolean status, String message) {
if (!status) { //<-- status result is FALSE
binding.textEmptyErr.setText(message);
binding.emptyView.setVisibility(View.VISIBLE);
} else {
binding.emptyView.setVisibility(View.GONE);
}
}
}
package id.haerulmuttaqin.jobfinder.ui;
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.Utils;
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 String formatDate(String date){
return Utils.dateToTimeFormat(date);
}
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().onGetResult(true, "Success");
}
getNavigator().hideProgress();
}
@Override
public void onFailure(Call<List<GithubJob>> call, Throwable t) {
t.getLocalizedMessage();
getNavigator().hideProgress();
getNavigator().onGetResult(false, Utils.errorMessageHandler(call, t));
}
});
}
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();
void onGetResult(boolean status, String message);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment