Last active
March 20, 2021 13:16
-
-
Save haerulmuttaqin/5105e0e03deba1bccb24b262cf56ebad to your computer and use it in GitHub Desktop.
#4 — Job Finder App (https://youtu.be/wkOK3LFTZKM)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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