Skip to content

Instantly share code, notes, and snippets.

@haerulmuttaqin
Last active March 20, 2021 13:16
Show Gist options
  • Save haerulmuttaqin/5105e0e03deba1bccb24b262cf56ebad to your computer and use it in GitHub Desktop.
Save haerulmuttaqin/5105e0e03deba1bccb24b262cf56ebad to your computer and use it in GitHub Desktop.
#4 — Job Finder App (https://youtu.be/wkOK3LFTZKM)
<?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:popupTheme="@style/Theme.JobFinder.PopupOverlay"
app:elevation="0dp"
app:layout_collapseMode="pin"
app:titleTextColor="@color/white"/>
</com.google.android.material.appbar.AppBarLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:fillViewport="true"
app:layout_anchor="@id/appbar"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<RelativeLayout
android:id="@+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_rounded_top">
<ImageView
android:layout_width="50dp"
android:layout_height="5dp"
android:background="@drawable/bg_strip_round"
android:layout_marginTop="8dp"
android:layout_centerHorizontal="true" />
<androidx.core.widget.NestedScrollView
android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="15dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.card.MaterialCardView
android:layout_width="160dp"
android:layout_height="160dp"
android:layout_marginTop="16sp"
android:layout_gravity="center_horizontal"
app:strokeWidth="0.5dp"
app:strokeColor="#f1f1f1"
app:cardElevation="0dp"
app:cardCornerRadius="8dp">
<ImageView
android:id="@+id/photoPreview"
android:scaleType="centerInside"
android:layout_margin="5dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/progress"
android:layout_width="160dp"
android:layout_height="160dp"
app:shimmer_auto_start="true"
app:shimmer_duration="800">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/gray_100"/>
</com.facebook.shimmer.ShimmerFrameLayout>
</com.google.android.material.card.MaterialCardView>
<TextView
android:text="@{item.title}"
android:textColor="@color/gray_500"
android:textSize="18sp"
android:fontFamily="@font/bold"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginStart="16sp"
android:layout_marginEnd="16sp"
android:gravity="center_horizontal"/>
<TextView
android:text="@{item.company}"
android:textColor="@color/gray_500"
android:textSize="14sp"
android:fontFamily="@font/book"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16sp"
android:layout_marginEnd="16sp"
android:gravity="center_horizontal"/>
<TextView
android:text="@{item.type}"
android:textColor="@color/green_300"
android:textSize="14sp"
android:fontFamily="@font/medium"
android:paddingEnd="6dp"
android:paddingStart="6dp"
android:paddingBottom="2dp"
android:paddingTop="2dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginStart="16sp"
android:layout_marginEnd="16sp"
android:layout_gravity="center_horizontal"
android:background="@drawable/bg_job_type"
android:gravity="center_horizontal"/>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="14dp"
android:layout_marginEnd="14dp"
android:layout_marginTop="14dp"
app:strokeWidth="0.5dp"
app:strokeColor="#f1f1f1"
app:cardElevation="0dp"
app:cardCornerRadius="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="16sp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:orientation="horizontal">
<ImageView
android:layout_width="21dp"
android:layout_height="21dp"
android:src="@drawable/ic_twotone_info_24"
app:tint="@color/navy_100"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="Job Description"
android:textColor="@color/navy_100"
android:fontFamily="@font/bold"
android:layout_marginStart="5dp"/>
</LinearLayout>
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/gray_400"
android:fontFamily="@font/book"
android:textIsSelectable="true"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="14dp"
android:layout_marginEnd="14dp"
android:layout_marginTop="14dp"
app:strokeWidth="0.5dp"
app:strokeColor="#f1f1f1"
app:cardElevation="0dp"
app:cardCornerRadius="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="16sp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:orientation="horizontal">
<ImageView
android:layout_width="21dp"
android:layout_height="21dp"
android:src="@drawable/ic_twotone_work_24"
app:tint="@color/navy_100"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="Company Information"
android:textColor="@color/navy_100"
android:fontFamily="@font/bold"
android:layout_marginStart="5dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iconCompany"
android:layout_width="17dp"
android:layout_height="17dp"
android:src="@drawable/ic_round_business_24"
app:tint="@color/gray_300"/>
<TextView
android:layout_toEndOf="@id/iconCompany"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@{item.company}"/>
</RelativeLayout>
<RelativeLayout
android:layout_marginTop="3dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iconLocation"
android:layout_width="17dp"
android:layout_height="17dp"
android:layout_marginTop="2dp"
android:src="@drawable/ic_outline_location_on_24"
app:tint="@color/gray_300"/>
<TextView
android:layout_toEndOf="@id/iconLocation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@{item.location}"/>
</RelativeLayout>
<RelativeLayout
android:layout_marginTop="3dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iconWebsite"
android:layout_width="17dp"
android:layout_height="17dp"
android:layout_marginTop="2dp"
android:src="@drawable/ic_baseline_public_24"
app:tint="@color/gray_300"/>
<TextView
android:layout_toEndOf="@id/iconWebsite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@{item.companyUrl}"
android:textIsSelectable="true"/>
</RelativeLayout>
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<View
android:layout_width="match_parent"
android:layout_height="100dp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<ImageView
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginTop="15dp"
android:src="@drawable/bg_shadow_up_to_down"/>
</RelativeLayout>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@drawable/bg_shadow_bottom_to_top_transparent"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<RelativeLayout
android:layout_marginBottom="10dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="6dp"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@drawable/bg_rounded_green_outline">
<ImageView
android:id="@+id/btnMark"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@{item.is_mark == 1 ? @drawable/ic_marked : @drawable/ic_mark}"
app:tint="@color/green_200"
android:layout_centerInParent="true"
android:layout_width="25dp"
android:layout_height="25dp"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/btnHowToApply"
android:clickable="true"
android:focusable="true"
android:layout_marginBottom="10dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="16dp"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/btn_round_green_selector">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="horizontal">
<ImageView
android:src="@drawable/ic_twotone_help_24"
app:tint="@color/white"
android:layout_width="21dp"
android:layout_height="21dp"/>
<TextView
android:text="How To Apply"
android:textColor="@color/white"
android:fontFamily="@font/bold"
android:layout_marginStart="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
</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 android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.Html;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
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 com.google.android.material.snackbar.Snackbar;
import com.haerul.bottomfluxdialog.BottomFluxDialog;
import javax.inject.Inject;
import id.haerulmuttaqin.jobfinder.R;
import id.haerulmuttaqin.jobfinder.base.BaseActivity;
import id.haerulmuttaqin.jobfinder.base.BaseViewModel;
import id.haerulmuttaqin.jobfinder.data.entity.GithubJob;
import id.haerulmuttaqin.jobfinder.data.storage.GithubJobRepository;
import id.haerulmuttaqin.jobfinder.databinding.ActivityDetailBinding;
public class DetailActivity extends BaseActivity<ActivityDetailBinding, BaseViewModel> {
@Inject
GithubJobRepository repository;
private ActivityDetailBinding binding;
@Override
public int getBindingVariable() {
return 0;
}
@Override
public int getLayoutId() {
return R.layout.activity_detail;
}
@Override
public BaseViewModel getViewModel() {
return null;
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = getViewDataBinding();
if (getIntent().getSerializableExtra("item") != null) {
GithubJob item = (GithubJob) getIntent().getSerializableExtra("item");
setupActionBar(item.title);
binding.setItem(item);
binding.description.setText(Html.fromHtml(item.description));
Glide.with(binding.photoPreview.getContext())
.load(item.companyLogo)
.error(getResources().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.btnMark.setOnClickListener(view -> {
repository.updateMarkJob(item);
binding.setItem(item);
Snackbar.make(binding.getRoot(),
item.is_mark == 0 ?
"\uD83D\uDE13 Unmark " + item.title :
"\uD83D\uDE0D Marked " + item.title, Snackbar.LENGTH_SHORT).show();
});
binding.btnHowToApply.setOnClickListener(view -> {
showDialogInfo(item.howToApply);
});
} else {
Toast.makeText(this, "Failed to get job detail!", Toast.LENGTH_SHORT).show();
finish();
}
}
private void showDialogInfo(String howToApply) {
BottomFluxDialog.infoDialog(this)
.setTextTitle("How to apply")
.setTextMessage(Html.fromHtml(howToApply).toString())
.setImageDialog(R.drawable.ic_twotone_work_24)
.setInfoButtonText("CLOSE")
.show();
}
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(); // or finish()
return true;
}
return super.onOptionsItemSelected(item);
}
}
<?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>
<import type="android.view.View" />
<variable
name="item"
type="id.haerulmuttaqin.jobfinder.data.entity.GithubJob" />
<variable
name="viewModel"
type="id.haerulmuttaqin.jobfinder.ui.MainViewModel" />
</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:onClick="@{()->viewModel.onItemClick(item)}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground">
<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:background="@drawable/ic_photo"
app:cardCornerRadius="8dp"
app:cardElevation="0dp"
app:strokeColor="#F1F1F1"
app:strokeWidth="0.5dp">
<ImageView
android:id="@+id/photoPreview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerInside"
android:layout_margin="5dp"/>
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/progress"
android:layout_width="60dp"
android:layout_height="60dp"
app:shimmer_auto_start="true"
app:shimmer_duration="800">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@color/gray_100"/>
</com.facebook.shimmer.ShimmerFrameLayout>
</com.google.android.material.card.MaterialCardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_toEndOf="@id/photoPreviewLayout">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{item.title}"
android:maxLines="2"
android:ellipsize="end"
android:textColor="@color/gray_500"
android:textSize="18sp"
android:layout_marginBottom="3dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="35dp"
android:fontFamily="@font/bold" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp">
<ImageView
android:id="@+id/iconCompany"
android:layout_width="15dp"
android:layout_height="16dp"
android:layout_marginEnd="5dp"
android:src="@drawable/ic_round_business_24"
app:tint="@color/gray_300"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/iconCompany"
android:text="@{item.company}"
android:textSize="12sp"
android:textColor="@color/gray_400"
android:singleLine="true"
android:ellipsize="end"
android:fontFamily="@font/book"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp">
<ImageView
android:id="@+id/iconLocation"
android:layout_width="15dp"
android:layout_height="16dp"
android:layout_marginEnd="5dp"
android:src="@drawable/ic_outline_location_on_24"
app:tint="@color/gray_300"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/iconLocation"
android:text="@{item.location}"
android:textSize="12sp"
android:textColor="@color/gray_400"
android:singleLine="true"
android:ellipsize="end"
android:fontFamily="@font/book"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginStart="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.type}"
android:textSize="10sp"
android:textColor="@color/green_200"
android:singleLine="true"
android:ellipsize="end"
android:fontFamily="@font/book"
android:background="@drawable/bg_job_type"
android:paddingStart="6dp"
android:paddingEnd="6dp"
android:paddingTop="2dp"
android:paddingBottom="2dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.formatDate(item.createdAt)}"
android:textSize="10sp"
android:layout_alignParentEnd="true"
android:textColor="@color/gray_300"
android:singleLine="true"
android:ellipsize="end"
android:fontFamily="@font/book"
android:paddingStart="6dp"
android:paddingEnd="6dp"
android:paddingTop="2dp"
android:paddingBottom="2dp"/>
</RelativeLayout>
</LinearLayout>
<ImageView
android:id="@+id/mark"
android:onClick="@{()->viewModel.markJob(item)}"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:layout_width="30dp"
android:layout_height="40dp"
android:src="@{item.is_mark == 1 ? @drawable/ic_marked : @drawable/ic_mark}"
android:layout_alignParentEnd="true"
android:padding="3dp"
app:tint="@color/gray_200"/>
</RelativeLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
<?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" />
<variable
name="viewModel"
type="id.haerulmuttaqin.jobfinder.ui.MainViewModel" />
</data>
<com.google.android.material.card.MaterialCardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="14dp"
android:layout_marginTop="8dp"
app:cardCornerRadius="10dp"
app:cardElevation="0dp"
app:strokeColor="#f1f1f1"
app:strokeWidth="1dp">
<LinearLayout
android:onClick="@{()->viewModel.onItemClick(item)}"
android:orientation="vertical"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:layout_width="200dp"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
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="14dp"
android:layout_marginTop="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_below="@id/photoPreviewLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp">
<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:layout_marginEnd="16dp"
android:text="@{item.title}"
android:textColor="@color/gray_500"
android:textSize="18sp"
android:layout_marginBottom="3dp"
android:fontFamily="@font/bold"/>
<TextView
android:id="@+id/lamp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textColor="@color/gray_400"
android:text="@{item.company}"
android:textSize="12sp"
android:singleLine="true"
android:ellipsize="end"
android:fontFamily="@font/book"/>
</LinearLayout>
<ImageView
android:onClick="@{()->viewModel.markJob(item)}"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:id="@+id/mark"
android:layout_width="30dp"
android:layout_height="40dp"
android:src="@{item.is_mark == 1 ? @drawable/ic_marked : @drawable/ic_mark}"
android:layout_alignParentEnd="true"
android:padding="3dp"
app:tint="@color/gray_200"/>
</RelativeLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.material.snackbar.Snackbar;
import com.haerul.bottomfluxdialog.BottomFluxDialog;
import javax.inject.Inject;
import id.haerulmuttaqin.jobfinder.Constants;
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.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;
boolean isDarkMode;
@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 -> {
if (githubJobs.size() > 0) {
binding.recyclerView.setAdapter(new MainAdapter(githubJobs, viewModel));
binding.emptyView.setVisibility(View.GONE);
} else {
binding.emptyView.setVisibility(View.VISIBLE);
binding.recyclerView.setVisibility(View.GONE);
}
});
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.search.setOnClickListener(view -> {
showDialogSearch();
});
binding.markedShowAll.setOnClickListener(view -> {
Intent intent = new Intent(MainActivity.this, ListActivity.class);
intent.putExtra("marked", "marked");
startActivity(intent);
});
binding.recommendedShowAll.setOnClickListener(view -> {
Intent intent = new Intent(MainActivity.this, ListActivity.class);
intent.putExtra("recommended", "recommended");
startActivity(intent);
});
isDarkMode = Utils.getBooleanPreference(this, Constants.DARK_MODE);
if (isDarkMode) {
binding.theme.setImageDrawable(getDrawable(R.drawable.ic_baseline_light_mode_24));
} else {
binding.theme.setImageDrawable(getDrawable(R.drawable.ic_round_dark_mode_24));
}
binding.theme.setOnClickListener(view -> switchTheme());
}
private void switchTheme() {
if (isDarkMode) {
binding.theme.setImageDrawable(getDrawable(R.drawable.ic_baseline_light_mode_24));
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
Utils.putPreference(this, Constants.DARK_MODE, false);
} else {
binding.theme.setImageDrawable(getDrawable(R.drawable.ic_round_dark_mode_24));
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
Utils.putPreference(this, Constants.DARK_MODE, true);
}
}
private void showDialogSearch() {
BottomFluxDialog.inputDialog(MainActivity.this)
.setTextTitle("Search")
.setTextMessage("What are you looking for?")
.setRightButtonText("SUBMIT")
.setInputListener(new BottomFluxDialog.OnInputListener() {
@Override
public void onSubmitInput(String text) {
Intent intent = new Intent(MainActivity.this, ListActivity.class);
intent.putExtra("search", text);
startActivity(intent);
}
@Override
public void onCancelInput() {}
})
.show();
}
@Override
public void showProgress() {
binding.swipeRefresh.setRefreshing(true);
binding.emptyView.setVisibility(View.GONE);
binding.shimmer.startShimmer();
binding.shimmer.setVisibility(View.VISIBLE);
binding.contentLayout.setVisibility(View.GONE);
}
@Override
public void hideProgress() {
binding.swipeRefresh.setRefreshing(false);
binding.shimmer.stopShimmer();
binding.shimmer.setVisibility(View.GONE);
binding.contentLayout.setVisibility(View.VISIBLE);
}
@Override
public void getResult(boolean status, String message) {
if (!status) {
binding.emptyView.setVisibility(View.VISIBLE);
binding.textEmptyErr.setText(message);
} 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 androidx.paging.PagedList;
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.entity.NetworkState;
import id.haerulmuttaqin.jobfinder.data.storage.GithubJobRepository;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class MainViewModel extends BaseViewModel<MainViewModel.Navigator> {
public MainViewModel(Context context, ConnectionServer connectionServer, GithubJobRepository repository) {
super(context, connectionServer, repository);
}
public LiveData<PagedList<GithubJob>> getPagedData(String keyword) {
return getRepository().getDataByPage(
getConnectionServer(),
getRepository(),
keyword);
}
public LiveData<NetworkState> getNetworkState() {
return getRepository().getNetworkState();
}
public LiveData<List<GithubJob>> getLiveData() {
return getRepository().getLiveData();
}
public LiveData<List<GithubJob>> getLiveDataMarked() {
return getRepository().getLiveDataMarked();
}
public LiveData<List<GithubJob>> searchLiveData(String keyword) {
return getRepository().searchLiveData(keyword);
}
public void markJob(GithubJob githubJob) {
getRepository().updateMarkJob(githubJob);
getNavigator().onMark(githubJob.is_mark, githubJob.title);
}
public String formatDate(String date) {
return Utils.dateToTimeFormat(date);
}
public void onItemClick(GithubJob githubJob) {
getNavigator().onItemClick(githubJob);
}
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);
getRepository().insert(item);
}
getNavigator().getResult(true, "Success");
} else {
getNavigator().getResult(false, response.message());
}
getNavigator().hideProgress();
}
@Override
public void onFailure(Call<List<GithubJob>> call, Throwable t) {
t.getLocalizedMessage();
getNavigator().getResult(false, Utils.errorMessageHandler(call, t));
getNavigator().hideProgress();
}
});
}
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()) {
item.createdAt = Utils.dateFormatter(item.createdAt);
getRepository().insert(item);
}
getNavigator().getResult(true, "Success");
} else {
getNavigator().getResult(false, response.message());
}
getNavigator().hideProgress();
}
@Override
public void onFailure(Call<List<GithubJob>> call, Throwable t) {
t.getLocalizedMessage();
getNavigator().getResult(false, Utils.errorMessageHandler(call, t));
getNavigator().hideProgress();
}
});
}
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 getResult(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