Skip to content

Instantly share code, notes, and snippets.

@haerulmuttaqin
Created March 21, 2021 07:20
Show Gist options
  • Save haerulmuttaqin/8c9738bc580f1f5761bb108aff5687f1 to your computer and use it in GitHub Desktop.
Save haerulmuttaqin/8c9738bc580f1f5761bb108aff5687f1 to your computer and use it in GitHub Desktop.
#5 — Job Finder App (https://youtu.be/YX_3TjKY-Vw)
<?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">
<data>
<variable
name="item"
type="id.haerulmuttaqin.jobfinder.data.entity.GithubJob" />
</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"
android:background="@color/blue_200"
app:elevation="0dp">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="@style/Theme.JobFinder.Toolbar"
app:elevation="0dp"
app:layout_collapseMode="pin"
app:popupTheme="@style/Theme.JobFinder.PopupOverlay"
app:titleTextColor="@color/white" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_rounded_top"
android:fillViewport="true"
android:fitsSystemWindows="true"
app:layout_anchor="@id/appbar"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_rounded_top"
android:fillViewport="true"
android:fitsSystemWindows="true"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<ImageView
android:id="@+id/imgtop"
android:layout_width="40dp"
android:layout_height="5dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="8dp"
android:background="@drawable/bg_strip_round" />
<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"
android:layout_marginTop="15dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<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>
<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>
<ImageView
android:id="@+id/shadow"
android:layout_width="match_parent"
android:layout_height="36dp"
android:layout_marginStart="3dp"
android:layout_marginEnd="3dp"
android:layout_marginTop="15dp"
android:src="@drawable/bg_shadow_up_to_down"
app:layout_anchor="@+id/scroll"
app:layout_anchorGravity="top|center" />
</RelativeLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
<?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">
<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"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
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>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_rounded_top"
android:fillViewport="true"
android:fitsSystemWindows="true"
app:layout_anchor="@id/appbar"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:fillViewport="true">
<RelativeLayout
android:id="@+id/contentLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/markedTitle"
android:text="Marked Job"
android:textSize="16dp"
android:textColor="@color/gray_600"
android:layout_marginStart="21dp"
android:layout_marginEnd="21dp"
android:layout_marginTop="25dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/medium"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewMarked"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginBottom="10dp"
android:layout_below="@id/markedTitle"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:nestedScrollingEnabled="true"
android:orientation="horizontal"/>
<TextView
android:id="@+id/recommendedTitle"
android:text="Recommended Job"
android:textSize="16dp"
android:textColor="@color/gray_600"
android:layout_marginStart="21dp"
android:layout_marginEnd="21dp"
android:layout_marginTop="25dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/recyclerViewMarked"
android:fontFamily="@font/medium"/>
<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"
android:layout_marginBottom="10dp"
android:layout_below="@id/recommendedTitle"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</RelativeLayout>
</androidx.core.widget.NestedScrollView>
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/shimmer"
android:visibility="gone"
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>
<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.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
import dagger.Module;
import dagger.android.ContributesAndroidInjector;
import id.haerulmuttaqin.jobfinder.ui.MainActivity;
import id.haerulmuttaqin.jobfinder.ui.detail.DetailActivity;
import id.haerulmuttaqin.jobfinder.ui.list.ListActivity;
@Module
public abstract class ActivityBuilder {
@ContributesAndroidInjector
abstract MainActivity mainActivity();
@ContributesAndroidInjector
abstract DetailActivity detailActivity();
@ContributesAndroidInjector
abstract ListActivity listActivity();
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="id.haerulmuttaqin.jobfinder">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.JobFinder.NoActionBar">
<activity android:name=".ui.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.detail.DetailActivity"
android:theme="@style/Theme.JobFinder.NoActionBar"/>
<activity
android:name=".ui.list.ListActivity"
android:theme="@style/Theme.JobFinder.NoActionBar"/>
</application>
</manifest>
import java.util.List;
import id.haerulmuttaqin.jobfinder.data.entity.GithubJob;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface ApiInterface {
@GET("positions.json")
Call<List<GithubJob>> getJobList();
@GET("positions.json")
Call<List<GithubJob>> searchJobList(@Query("search") String keyword);
}
dependencies {
implementation 'com.github.haerulmuttaqin:BottomFluxDialog:0.1.1'
}
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 Call<List<GithubJob>> searchJobList(String keyword) {
return apiInterface.searchJobList(keyword);
}
}
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import java.util.List;
import id.haerulmuttaqin.jobfinder.data.entity.GithubJob;
@Dao
public interface GithubJobDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(GithubJob githubJob);
@Query("SELECT * FROM githubjob ORDER BY createdAt DESC LIMIT 10")
LiveData<List<GithubJob>> getLiveData();
@Query("SELECT * FROM githubjob WHERE LOWER(title) LIKE :keyword OR LOWER(description) LIKE :keyword ORDER BY createdAt DESC")
LiveData<List<GithubJob>> searchLiveData(String keyword);
@Query("SELECT * FROM githubjob where is_mark = 1 ORDER BY createdAt DESC")
LiveData<List<GithubJob>> getLiveDataMarked();
@Query("SELECT * FROM githubjob where id = :id")
GithubJob getDataById(String id);
}
import android.content.Context;
import androidx.lifecycle.LiveData;
import java.util.List;
import id.haerulmuttaqin.jobfinder.data.entity.GithubJob;
public class GithubJobRepository {
private static GithubJobDatabase database;
private static GithubJobRepository repository;
public GithubJobRepository(Context context) {
database = GithubJobDatabase.getDatabase(context);
}
public static GithubJobRepository getInstance(Context context) {
if (repository == null) {
repository = new GithubJobRepository(context);
}
return repository;
}
public void updateMarkJob(GithubJob githubJob) {
githubJob.is_mark = githubJob.is_mark == 1 ? 0 : 1;
database.githubJobDao().insert(githubJob);
}
public void insert(GithubJob githubJob) {
if (database.githubJobDao().getDataById(githubJob.id) == null) {
database.githubJobDao().insert(githubJob);
}
}
public GithubJob getById(String id) {
return database.githubJobDao().getDataById(id);
}
public LiveData<List<GithubJob>> getLiveData() {
return database.githubJobDao().getLiveData();
}
public LiveData<List<GithubJob>> searchLiveData(String keyword) {
keyword = "%" + keyword + "%";
return database.githubJobDao().searchLiveData(keyword);
}
public LiveData<List<GithubJob>> getLiveDataMarked() {
return database.githubJobDao().getLiveDataMarked();
}
}
import android.content.Intent;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.material.snackbar.Snackbar;
import javax.inject.Inject;
import id.haerulmuttaqin.jobfinder.R;
import id.haerulmuttaqin.jobfinder.Utils;
import id.haerulmuttaqin.jobfinder.base.BaseActivity;
import id.haerulmuttaqin.jobfinder.data.api.ConnectionServer;
import id.haerulmuttaqin.jobfinder.data.entity.GithubJob;
import id.haerulmuttaqin.jobfinder.data.storage.GithubJobRepository;
import id.haerulmuttaqin.jobfinder.databinding.ActivityListBinding;
import id.haerulmuttaqin.jobfinder.ui.MainAdapter;
import id.haerulmuttaqin.jobfinder.ui.MainViewModel;
import id.haerulmuttaqin.jobfinder.ui.detail.DetailActivity;
public class ListActivity extends BaseActivity<ActivityListBinding, MainViewModel> implements MainViewModel.Navigator {
@Inject
GithubJobRepository repository;
@Inject
ConnectionServer server;
private ActivityListBinding binding;
private MainViewModel viewModel;
@Override
public int getBindingVariable() {
return 0;
}
@Override
public int getLayoutId() {
return R.layout.activity_list;
}
@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, repository)
).get(MainViewModel.class);
viewModel.setNavigator(this);
if (getIntent().getStringExtra("search") != null) {
String keyword = getIntent().getStringExtra("search");
setupActionBar("Search Jobs (" + keyword + ")");
viewModel.searchJobFromServer(keyword);
binding.swipeRefresh.setOnRefreshListener(()->viewModel.searchJobFromServer(keyword));
viewModel.searchLiveData(keyword).observe(this, githubJobs -> {
binding.recyclerView.setAdapter(new MainAdapter(githubJobs, viewModel));
});
} else {
Toast.makeText(this, "Failed get list!", Toast.LENGTH_SHORT).show();
finish();
}
}
private void setupActionBar(String title) {
setSupportActionBar(binding.toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle(title);
}
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
@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);
}
}
@Override
public void onMark(int mark, String title) {
Snackbar.make(binding.getRoot(), mark == 0 ? "\uD83D\uDE13 Unmark " + title : "\uD83D\uDE0D Marked " + title, Snackbar.LENGTH_SHORT).show();
}
@Override
public void onItemClick(GithubJob githubJob) {
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra("item", githubJob);
startActivity(intent);
}
}
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.RelativeLayout;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.textfield.TextInputEditText;
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.data.entity.GithubJob;
import id.haerulmuttaqin.jobfinder.data.storage.GithubJobRepository;
import id.haerulmuttaqin.jobfinder.databinding.ActivityMainBinding;
import id.haerulmuttaqin.jobfinder.ui.detail.DetailActivity;
import id.haerulmuttaqin.jobfinder.ui.list.ListActivity;
public class MainActivity extends BaseActivity<ActivityMainBinding, MainViewModel> implements MainViewModel.Navigator {
@Inject ConnectionServer server;
@Inject GithubJobRepository repository;
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, repository)).get(MainViewModel.class);
viewModel.setNavigator(this);
viewModel.getJobFromServer();
viewModel.getLiveData().observe(this, githubJobs -> {
binding.recyclerView.setAdapter(new MainAdapter(githubJobs, viewModel));
});
viewModel.getLiveDataMarked().observe(this, githubJobs -> {
if (githubJobs.size() > 0) {
binding.markedTitle.setVisibility(View.VISIBLE);
binding.recyclerViewMarked.setVisibility(View.VISIBLE);
binding.recyclerViewMarked.setAdapter(new MainMarkedAdapter(githubJobs, viewModel));
} else {
binding.markedTitle.setVisibility(View.GONE);
binding.recyclerViewMarked.setVisibility(View.GONE);
}
});
binding.swipeRefresh.setOnRefreshListener(()->viewModel.getJobFromServer());
binding.cardSearch.setOnClickListener(v->{
showDialogSearch();
});
}
private void showDialogSearch() {
View view = getLayoutInflater().inflate(R.layout.dialog_search, null);
TextInputEditText m = view.findViewById(R.id.message);
RelativeLayout btnSearch = view.findViewById(R.id.search);
BottomSheetDialog dialog = new BottomSheetDialog(this, R.style.BottomSheetDialogStyle);
btnSearch.setOnClickListener(v -> {
Intent intent = new Intent(this, ListActivity.class);
intent.putExtra("search", m.getText().toString());
startActivity(intent);
});
m.requestFocus();
dialog.setContentView(view);
dialog.show();
}
@Override
public void showProgress() {
binding.swipeRefresh.setRefreshing(true);
binding.emptyView.setVisibility(View.GONE);
binding.shimmer.setVisibility(View.VISIBLE);
binding.shimmer.startShimmer();
binding.contentLayout.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.contentLayout.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);
}
}
@Override
public void onMark(int mark, String title) {
Snackbar.make(binding.getRoot(), mark == 0 ? "\uD83D\uDE13 Unmark " + title : "\uD83D\uDE0D Marked " + title, Snackbar.LENGTH_SHORT).show();
}
@Override
public void onItemClick(GithubJob githubJob) {
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra("item", githubJob);
startActivity(intent);
}
}
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
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 id.haerulmuttaqin.jobfinder.data.storage.GithubJobRepository;
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, GithubJobRepository repository) {
super(context, connectionServer, repository);
}
public LiveData<List<GithubJob>> getLiveData() {
return getRepository().getLiveData();
}
public LiveData<List<GithubJob>> searchLiveData(String keyword) {
return getRepository().searchLiveData(keyword.toLowerCase());
}
public LiveData<List<GithubJob>> getLiveDataMarked() {
return getRepository().getLiveDataMarked();
}
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) {
for (GithubJob item : response.body()) {
item.createdAt = Utils.dateFormatter(item.createdAt); // format date
getRepository().insert(item);
}
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 void searchJobFromServer(String keyword) {
getNavigator().showProgress();
getConnectionServer().searchJobList(keyword).enqueue(new Callback<List<GithubJob>>() {
@Override
public void onResponse(Call<List<GithubJob>> call, Response<List<GithubJob>> response) {
if (response.isSuccessful() && response.body().size() > 0) {
for (GithubJob item : response.body()) {
getRepository().insert(item);
}
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 void markJob(GithubJob githubJob) {
getRepository().updateMarkJob(githubJob);
getNavigator().onMark(githubJob.is_mark, githubJob.title);
}
public void itemClick(GithubJob githubJob) {
getNavigator().onItemClick(githubJob);
}
public static class ModelFactory implements ViewModelProvider.Factory {
private Context context;
private ConnectionServer server;
private GithubJobRepository repository;
public ModelFactory(Context context, ConnectionServer server, GithubJobRepository repository) {
this.context = context;
this.server = server;
this.repository = repository;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return (T) new MainViewModel(context, server, repository);
}
}
public interface Navigator {
void showProgress();
void hideProgress();
void onGetResult(boolean status, String message);
void onMark(int mark, String title);
void onItemClick(GithubJob githubJob);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment