From 7070c7cf0fcca952e5308744fa42322d4b090c02 Mon Sep 17 00:00:00 2001 From: WallE Date: Thu, 16 Mar 2023 02:22:20 +0530 Subject: [PATCH 01/50] refractor: Delete commented code --- .../vtucslab/Activity/DisplayActivity.java | 80 ------------------- .../vtucslab/Activity/ProgramActivity.java | 2 - .../vtucslab/Activity/RepositoryActivity.java | 2 - .../vtucslab/Loader/RawStreamLoader.java | 9 +-- .../shivam/vtucslab/Utility/FetchUtil.java | 5 +- 5 files changed, 3 insertions(+), 95 deletions(-) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/DisplayActivity.java b/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/DisplayActivity.java index c3abfa0..e2090c6 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/DisplayActivity.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/DisplayActivity.java @@ -36,11 +36,6 @@ public class DisplayActivity extends AppCompatActivity implements LoaderManager. private static final String LOG_TAG = DisplayActivity.class.getSimpleName(); - // private TextView displayTextView; -// private PageView mDisplayPageView; -// private TextView mEmptyTextView; -// private ProgressBar mProgressBar; - private Boolean mSucceeded = false; private String mUrl; private String mTitle; @@ -88,8 +83,6 @@ public boolean onOptionsItemSelected(MenuItem item) { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBinding = DataBindingUtil.setContentView(this, R.layout.activity_display); -// setContentView(R.layout.activity_display); -// logd("OnCreate Called"); ActionBar actionBar = getSupportActionBar(); initViews(); @@ -121,83 +114,19 @@ protected void onCreate(Bundle savedInstanceState) { if (networkInfo != null && networkInfo.isConnected()) { loaderManager.initLoader(LOADER_ID, null, DisplayActivity.this); } else { -// mProgressBar.setVisibility(View.GONE); mBinding.progressBarDisplay.setVisibility(View.GONE); -// mEmptyTextView.setVisibility(View.VISIBLE); mBinding.emptyTextViewDisplay.setVisibility(View.VISIBLE); -// mEmptyTextView.setText(R.string.no_internet_connection); mBinding.emptyTextViewDisplay.setText(R.string.no_internet_connection); mSucceeded = false; } } private void initViews() { -// mDisplayPageView = findViewById(R.id.display_page_view); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { -// mDisplayPageView.setLetterSpacing(0.1f); mBinding.displayTextView.setLetterSpacing(0.1f); } -// mEmptyTextView = findViewById(R.id.empty_text_view_display); -// mProgressBar = findViewById(R.id.progress_bar_display); - - //setupScrolling(); } -// private void setupScrolling() { -// mBinding.horizontalScroll.setOnTouchListener(new View.OnTouchListener() { -// @Override -// public boolean onTouch(View v, MotionEvent event) { -// return false; -// } -// }); -// mBinding.verticalScroll.setOnTouchListener(new View.OnTouchListener() { -// @Override -// public boolean onTouch(View v, MotionEvent event) { -// return false; -// } -// }); -// mBinding.displayTextView.setOnTouchListener(new View.OnTouchListener() { -// private float prevX, prevY, curX, curY; -// private boolean handled = false; -// -// @Override -// public boolean onTouch(View v, MotionEvent event) { -// Log.v("Ontouch Called", ""); -// mBinding.verticalScroll.requestDisallowInterceptTouchEvent(true); -// mBinding.horizontalScroll.requestDisallowInterceptTouchEvent(true); -// switch (event.getAction()) { -// case MotionEvent.ACTION_DOWN: -// handled = false; -// prevX = event.getX(); -// prevY = event.getY(); -// Log.v("Action Down", prevX + " " + prevY); -// break; -// case MotionEvent.ACTION_MOVE: -// handled = true; -// curX = event.getX(); -// curY = event.getY(); -// int dy = (int) (prevY - curY); -// int dx = (int) (prevX - curX); -// -// prevX = curX; -// prevY = curY; -// -// -// Log.v("Action Move Cur", curX + " " + curY); -//// Log.v("Action Move Delta", dx + " " + dy); -// -// mBinding.verticalScroll.scrollBy(0, dy); -// mBinding.horizontalScroll.scrollBy(dx, 0); -// break; -// case MotionEvent.ACTION_UP: -// Log.v("Action Up", event.getX() + " " + event.getY()); -// break; -// } -// return true; -// } -// }); -// } - @Override public Loader onCreateLoader(int i, Bundle bundle) { return new RawStreamLoader(DisplayActivity.this, mUrl); @@ -205,7 +134,6 @@ public Loader onCreateLoader(int i, Bundle bundle) { @Override public void onLoadFinished(Loader loader, String s) { -// mProgressBar.setVisibility(View.GONE); mBinding.progressBarDisplay.setVisibility(View.GONE); if (TextUtils.isEmpty(s)) { Toast.makeText(DisplayActivity.this, getString(R.string.error_occurred), Toast.LENGTH_LONG).show(); @@ -216,8 +144,6 @@ public void onLoadFinished(Loader loader, String s) { mSucceeded = true; mCode = s; s = s.replaceAll("\t", "\t\t"); -// displayTextView.setText(s); -// mDisplayPageView.setText(s); mBinding.displayTextView.setText(s); new Handler(getMainLooper()).postDelayed(new Runnable() { @Override @@ -231,8 +157,6 @@ public void run() { @Override public void onLoaderReset(Loader loader) { -// displayTextView.setText(null); -// mDisplayPageView.setText(null); mBinding.displayTextView.setText(null); } @@ -243,8 +167,4 @@ protected void onSaveInstanceState(Bundle outState) { outState.putInt(SCROLL_Y_KEY, mBinding.verticalScroll.getScrollY()); super.onSaveInstanceState(outState); } - -// private void logd(String str) { -// Log.d(LOG_TAG, str); -// } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/ProgramActivity.java b/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/ProgramActivity.java index 8edb2af..0847329 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/ProgramActivity.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/ProgramActivity.java @@ -144,8 +144,6 @@ public void onLoadFinished(Loader loader, LabResponse labResponse) mSucceeded = true; mProgramAdapter.clear(); mProgramAdapter.addAll(labResponse.getLabExperiments()); -// mLinkToRepo = labResponse.getLinkToRepo(); -// invalidateOptionsMenu(); } else { mSucceeded = false; showErrorMessage(labResponse.getInvalidationMessage()); diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/RepositoryActivity.java b/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/RepositoryActivity.java index 5164dea..2712ff2 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/RepositoryActivity.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/RepositoryActivity.java @@ -44,8 +44,6 @@ public class RepositoryActivity private static final int NAV_LOADER_ID = 1; private static final String SUCCEEDED_KEY = "succeeded_key"; private static final String URL = "https://raw.githubusercontent.com/vtucs/Index_v3/master/Index_v3.json"; -// private static final String URL = "https://raw.githubusercontent.com/vtucs/Test_Index/master/TestIndex.json"; - private DrawerLayout mDrawerLayout; private LoaderManager mLoaderManager; private NavigationAdapter mRepositoryAdapter; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/RawStreamLoader.java b/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/RawStreamLoader.java index ddf22ca..aac551e 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/RawStreamLoader.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/RawStreamLoader.java @@ -11,13 +11,10 @@ public class RawStreamLoader extends AsyncTaskLoader { private String mUrl; private String fetchedData; - // private SharedPreferences sharedPreferences; -// TODO: Implement Local Data Saving. + // TODO: Implement Local Data Saving. public RawStreamLoader(Context context, String url) { super(context); mUrl = url; -// sharedPreferences = context.getSharedPreferences(RAW_STREAM_LOADER, Context.MODE_PRIVATE); -// fetchedData = sharedPreferences.getString(mUrl, null); } @Override @@ -32,10 +29,6 @@ protected void onStartLoading() { @Override public String loadInBackground() { fetchedData = FetchUtil.fetchData(mUrl); -// TODO: If fetched data is not null. -// SharedPreferences.Editor editor = sharedPreferences.edit(); -// editor.putString(mUrl, fetchedData); -// editor.apply(); return fetchedData; } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Utility/FetchUtil.java b/app/src/main/java/com/nagpal/shivam/vtucslab/Utility/FetchUtil.java index 39889b3..a021f1c 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Utility/FetchUtil.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/Utility/FetchUtil.java @@ -1,5 +1,7 @@ package com.nagpal.shivam.vtucslab.Utility; +import static java.net.HttpURLConnection.HTTP_OK; + import android.util.Log; import java.io.BufferedReader; @@ -11,8 +13,6 @@ import java.net.URL; import java.nio.charset.Charset; -import static java.net.HttpURLConnection.HTTP_OK; - public class FetchUtil { private static final String LOG_TAG = "FetchUtils"; @@ -84,7 +84,6 @@ private static String readFromStream(InputStream inputStream) throws IOException BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String line = bufferedReader.readLine(); while (line != null) { -// line = line.replaceAll("\t", "\t\t"); //TODO: Temporary Solution to add padding with white spaces stringBuilder.append(" "); stringBuilder.append(line); From 817c4e68cc2a4164d8697cce5d725c8d0a4b1857 Mon Sep 17 00:00:00 2001 From: WallE Date: Thu, 16 Mar 2023 02:51:17 +0530 Subject: [PATCH 02/50] refractor: Resolve Lint errors --- .../vtucslab/Activity/DisplayActivity.java | 57 +++++++-------- .../vtucslab/Activity/ProgramActivity.java | 25 ++++--- .../vtucslab/Activity/RepositoryActivity.java | 69 ++++++++----------- .../vtucslab/Adapter/ContentAdapter.java | 31 ++++----- .../vtucslab/Adapter/MultipleFileAdapter.java | 8 +-- .../Adapter/MultipleSubPartAdapter.java | 8 +-- .../vtucslab/Adapter/NavigationAdapter.java | 12 ++-- .../shivam/vtucslab/Loader/InfoLoader.java | 5 +- .../vtucslab/Loader/RawStreamLoader.java | 3 +- .../shivam/vtucslab/Utility/FetchUtil.java | 4 +- 10 files changed, 94 insertions(+), 128 deletions(-) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/DisplayActivity.java b/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/DisplayActivity.java index e2090c6..09d568c 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/DisplayActivity.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/DisplayActivity.java @@ -14,6 +14,7 @@ import android.view.View; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.NavUtils; @@ -33,12 +34,8 @@ public class DisplayActivity extends AppCompatActivity implements LoaderManager. private static final String SCROLL_X_KEY = "scroll_x_key"; private static final String SCROLL_Y_KEY = "scroll_y_key"; - private static final String LOG_TAG = DisplayActivity.class.getSimpleName(); - - private Boolean mSucceeded = false; private String mUrl; - private String mTitle; private String mCode; @@ -58,23 +55,21 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - NavUtils.navigateUpFromSameTask(DisplayActivity.this); - return true; - - case R.id.display_menu_item_refresh: - recreate(); - return true; - - case R.id.menu_item_copy_display_activity: - ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - ClipData clipData = new ClipData(ClipData.newPlainText("Code", mCode)); - if (clipboardManager != null) { - clipboardManager.setPrimaryClip(clipData); - Toast.makeText(DisplayActivity.this, "Code copied to Clipboard.", Toast.LENGTH_SHORT).show(); - } - return true; + int itemId = item.getItemId(); + if (itemId == android.R.id.home) { + NavUtils.navigateUpFromSameTask(DisplayActivity.this); + return true; + } else if (itemId == R.id.display_menu_item_refresh) { + recreate(); + return true; + } else if (itemId == R.id.menu_item_copy_display_activity) { + ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + ClipData clipData = new ClipData(ClipData.newPlainText("Code", mCode)); + if (clipboardManager != null) { + clipboardManager.setPrimaryClip(clipData); + Toast.makeText(DisplayActivity.this, "Code copied to Clipboard.", Toast.LENGTH_SHORT).show(); + } + return true; } return super.onOptionsItemSelected(item); } @@ -94,14 +89,14 @@ protected void onCreate(Bundle savedInstanceState) { } Intent intent = getIntent(); - mTitle = intent.getStringExtra(ConstantVariables.title_intent_tag); - mUrl = intent.getStringExtra(ConstantVariables.url_intent_tag); + mUrl = intent.getStringExtra(ConstantVariables.url_intent_tag); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); } - DisplayActivity.this.setTitle(mTitle); + String title = intent.getStringExtra(ConstantVariables.title_intent_tag); + DisplayActivity.this.setTitle(title); ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectivityManager != null ? connectivityManager.getActiveNetworkInfo() : null; @@ -127,13 +122,14 @@ private void initViews() { } } + @NonNull @Override public Loader onCreateLoader(int i, Bundle bundle) { return new RawStreamLoader(DisplayActivity.this, mUrl); } @Override - public void onLoadFinished(Loader loader, String s) { + public void onLoadFinished(@NonNull Loader loader, String s) { mBinding.progressBarDisplay.setVisibility(View.GONE); if (TextUtils.isEmpty(s)) { Toast.makeText(DisplayActivity.this, getString(R.string.error_occurred), Toast.LENGTH_LONG).show(); @@ -145,18 +141,15 @@ public void onLoadFinished(Loader loader, String s) { mCode = s; s = s.replaceAll("\t", "\t\t"); mBinding.displayTextView.setText(s); - new Handler(getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - mBinding.horizontalScroll.setScrollX(mScrollX); - mBinding.verticalScroll.setScrollY(mScrollY); - } + new Handler(getMainLooper()).postDelayed(() -> { + mBinding.horizontalScroll.setScrollX(mScrollX); + mBinding.verticalScroll.setScrollY(mScrollY); }, 500); invalidateOptionsMenu(); } @Override - public void onLoaderReset(Loader loader) { + public void onLoaderReset(@NonNull Loader loader) { mBinding.displayTextView.setText(null); } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/ProgramActivity.java b/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/ProgramActivity.java index 0847329..e4b145f 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/ProgramActivity.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/ProgramActivity.java @@ -11,6 +11,7 @@ import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.NavUtils; import androidx.loader.app.LoaderManager; @@ -21,7 +22,6 @@ import com.nagpal.shivam.vtucslab.Adapter.ContentAdapter; import com.nagpal.shivam.vtucslab.Loader.InfoLoader; import com.nagpal.shivam.vtucslab.Model.ContentFile; -import com.nagpal.shivam.vtucslab.Model.LabExperiment; import com.nagpal.shivam.vtucslab.Model.LabResponse; import com.nagpal.shivam.vtucslab.Model.Laboratory; import com.nagpal.shivam.vtucslab.R; @@ -46,7 +46,6 @@ public class ProgramActivity private TextView mEmptyTextView; private boolean mSucceeded; private String mProgramBaseUrl; - private LoaderManager mLoaderManager; @Override protected void onCreate(Bundle savedInstanceState) { @@ -66,11 +65,9 @@ protected void onCreate(Bundle savedInstanceState) { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - NavUtils.navigateUpFromSameTask(ProgramActivity.this); - return true; - + if (item.getItemId() == android.R.id.home) { + NavUtils.navigateUpFromSameTask(ProgramActivity.this); + return true; } return super.onOptionsItemSelected(item); } @@ -86,7 +83,7 @@ private void initAndSetupViews() { } private void setupProgramAdapter() { - mProgramAdapter = new ContentAdapter(ProgramActivity.this, new ArrayList()); + mProgramAdapter = new ContentAdapter(ProgramActivity.this, new ArrayList<>()); mProgramRecyclerView.setAdapter(mProgramAdapter); mProgramAdapter.setItemClickHandler(this); } @@ -99,10 +96,10 @@ private void loadPrograms() { ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectivityManager != null ? connectivityManager.getActiveNetworkInfo() : null; - mLoaderManager = LoaderManager.getInstance(this); + LoaderManager loaderManager = LoaderManager.getInstance(this); if (!mSucceeded) { - mLoaderManager.destroyLoader(REPO_LOADER_ID); + loaderManager.destroyLoader(REPO_LOADER_ID); } mEmptyTextView.setVisibility(View.GONE); @@ -113,7 +110,7 @@ private void loadPrograms() { String url = laboratoryBaseUrl + "/" + laboratory.getFileName(); bundle.putString("URL", url); - mLoaderManager.initLoader(REPO_LOADER_ID, bundle, ProgramActivity.this); + loaderManager.initLoader(REPO_LOADER_ID, bundle, ProgramActivity.this); } else { mProgressBar.setVisibility(View.GONE); mSucceeded = false; @@ -121,13 +118,15 @@ private void loadPrograms() { } } + @NonNull @Override public Loader onCreateLoader(int id, Bundle args) { + assert args != null; return new InfoLoader(ProgramActivity.this, args.getString("URL")); } @Override - public void onLoadFinished(Loader loader, LabResponse labResponse) { + public void onLoadFinished(@NonNull Loader loader, LabResponse labResponse) { mProgressBar.setVisibility(View.GONE); if (labResponse == null) { @@ -151,7 +150,7 @@ public void onLoadFinished(Loader loader, LabResponse labResponse) } @Override - public void onLoaderReset(Loader loader) { + public void onLoaderReset(@NonNull Loader loader) { mProgramAdapter.clear(); } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/RepositoryActivity.java b/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/RepositoryActivity.java index 2712ff2..94060b3 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/RepositoryActivity.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/RepositoryActivity.java @@ -7,7 +7,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.view.Gravity; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -20,6 +19,7 @@ import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; +import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; @@ -45,14 +45,11 @@ public class RepositoryActivity private static final String SUCCEEDED_KEY = "succeeded_key"; private static final String URL = "https://raw.githubusercontent.com/vtucs/Index_v3/master/Index_v3.json"; private DrawerLayout mDrawerLayout; - private LoaderManager mLoaderManager; private NavigationAdapter mRepositoryAdapter; - private NavigationView mNavigationView; private ProgressBar mProgressBar; private RecyclerView mRepositoryRecyclerView; private TextView mEmptyTextView; private Toolbar mToolbar; - private View mHeaderView; private boolean mSucceeded; private String mLaboratoryBaseUrl; @@ -64,12 +61,11 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.main_menu_item_privacy: - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse("https://github.com/ShivamNagpal/Privacy_Policies/blob/master/VTU_CS_LAB_MANUAL.md")); - startActivity(intent); - return true; + if (item.getItemId() == R.id.main_menu_item_privacy) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse("https://github.com/ShivamNagpal/Privacy_Policies/blob/master/VTU_CS_LAB_MANUAL.md")); + startActivity(intent); + return true; } return super.onOptionsItemSelected(item); } @@ -78,14 +74,12 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) { boolean flag = false; - switch (menuItem.getItemId()) { - case R.id.menu_item_repository: - flag = true; - break; - case R.id.menu_item_exit: - exitApplication(); - flag = true; - break; + int itemId = menuItem.getItemId(); + if (itemId == R.id.menu_item_repository) { + flag = true; + } else if (itemId == R.id.menu_item_exit) { + exitApplication(); + flag = true; } if (flag) { closeNavigationDrawer(); @@ -114,14 +108,14 @@ protected void onCreate(Bundle savedInstanceState) { private void loadRepositories() { ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectivityManager != null ? connectivityManager.getActiveNetworkInfo() : null; - mLoaderManager = LoaderManager.getInstance(this); + LoaderManager loaderManager = LoaderManager.getInstance(this); if (!mSucceeded) { - mLoaderManager.destroyLoader(NAV_LOADER_ID); + loaderManager.destroyLoader(NAV_LOADER_ID); } if (networkInfo != null && networkInfo.isConnected()) { - mLoaderManager.initLoader(NAV_LOADER_ID, null, RepositoryActivity.this); + loaderManager.initLoader(NAV_LOADER_ID, null, RepositoryActivity.this); } else { mProgressBar.setVisibility(View.GONE); mSucceeded = false; @@ -135,19 +129,14 @@ private void initAndSetupViews() { mDrawerLayout = findViewById(R.id.activity_repository_drawer_layout); setUpDrawerToggle(); - mNavigationView = findViewById(R.id.activity_repository_navigation_view); - mNavigationView.setNavigationItemSelectedListener(this); - mNavigationView.getMenu().getItem(0).setChecked(true); + NavigationView navigationView = findViewById(R.id.activity_repository_navigation_view); + navigationView.setNavigationItemSelectedListener(this); + navigationView.getMenu().getItem(0).setChecked(true); - mHeaderView = mNavigationView.getHeaderView(0); + View headerView = navigationView.getHeaderView(0); - ImageButton navigationDrawerBackButton = mHeaderView.findViewById(R.id.activity_repository_image_button_close_drawer); - navigationDrawerBackButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - closeNavigationDrawer(); - } - }); + ImageButton navigationDrawerBackButton = headerView.findViewById(R.id.activity_repository_image_button_close_drawer); + navigationDrawerBackButton.setOnClickListener(view -> closeNavigationDrawer()); mRepositoryRecyclerView = findViewById(R.id.activity_repository_recycler_view_repository); mRepositoryRecyclerView.setLayoutManager(new LinearLayoutManager(RepositoryActivity.this, LinearLayoutManager.VERTICAL, false)); @@ -170,24 +159,20 @@ private void setUpDrawerToggle() { } private void closeNavigationDrawer() { - mDrawerLayout.closeDrawer(Gravity.START, true); + mDrawerLayout.closeDrawer(GravityCompat.START, true); } private void exitApplication() { this.finishAffinity(); - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - System.exit(0); - } - }, 1000); + new Handler(Looper.getMainLooper()).postDelayed(() -> System.exit(0), 1000); } private void setupRepositoryAdapter() { - mRepositoryAdapter = new NavigationAdapter(RepositoryActivity.this, new ArrayList()); + mRepositoryAdapter = new NavigationAdapter(RepositoryActivity.this, new ArrayList<>()); mRepositoryRecyclerView.setAdapter(mRepositoryAdapter); } + @NonNull @Override public Loader onCreateLoader(int id, Bundle args) { return new InfoLoader(RepositoryActivity.this, URL); @@ -195,7 +180,7 @@ public Loader onCreateLoader(int id, Bundle args) { } @Override - public void onLoadFinished(Loader loader, LabResponse labResponse) { + public void onLoadFinished(@NonNull Loader loader, LabResponse labResponse) { mProgressBar.setVisibility(View.GONE); if (labResponse == null) { @@ -219,7 +204,7 @@ public void onLoadFinished(Loader loader, LabResponse labResponse) } @Override - public void onLoaderReset(Loader loader) { + public void onLoaderReset(@NonNull Loader loader) { mRepositoryAdapter.clear(); } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/ContentAdapter.java b/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/ContentAdapter.java index 67e15f6..2888ca7 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/ContentAdapter.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/ContentAdapter.java @@ -1,5 +1,6 @@ package com.nagpal.shivam.vtucslab.Adapter; +import android.annotation.SuppressLint; import android.content.Context; import android.view.LayoutInflater; import android.view.View; @@ -29,8 +30,8 @@ public class ContentAdapter extends RecyclerView.Adapter mLabExperimentArrayList; + private final Context mContext; + private final ArrayList mLabExperimentArrayList; private ItemClickHandler mItemClickHandler; public ContentAdapter(Context context, ArrayList labExperimentArrayList) { @@ -42,12 +43,6 @@ public void setItemClickHandler(ItemClickHandler itemClickHandler) { mItemClickHandler = itemClickHandler; } - public void addAll(ArrayList labExperimentArrayList) { - int i = mLabExperimentArrayList.size(); - mLabExperimentArrayList.addAll(labExperimentArrayList); - notifyItemRangeInserted(i, labExperimentArrayList.size()); - } - public void addAll(@NonNull LabExperiment[] labExperiments) { int i = mLabExperimentArrayList.size(); mLabExperimentArrayList.addAll(Arrays.asList(labExperiments)); @@ -55,6 +50,7 @@ public void addAll(@NonNull LabExperiment[] labExperiments) { } + @SuppressLint("NotifyDataSetChanged") public void clear() { mLabExperimentArrayList.clear(); notifyDataSetChanged(); @@ -120,17 +116,16 @@ public int getItemViewType(int position) { LabExperiment experiment = mLabExperimentArrayList.get(position); int contentFileLength = 1; LabExperimentSubPart[] labExperimentSubParts = experiment.getLabExperimentSubParts(); - for (int i = 0, labExperimentSubPartsLength = labExperimentSubParts.length; i < labExperimentSubPartsLength; i++) { - LabExperimentSubPart subPart = labExperimentSubParts[i]; + for (LabExperimentSubPart subPart : labExperimentSubParts) { contentFileLength = Math.max(contentFileLength, subPart.getContentFiles().length); } if (labExperimentSubParts.length == 1 && contentFileLength == 1) return VIEW_TYPE_SSP_SF; else if (labExperimentSubParts.length > 1 && contentFileLength == 1) return VIEW_TYPE_MSP_SF; - else if (labExperimentSubParts.length == 1 && contentFileLength > 1) + else if (labExperimentSubParts.length == 1) return VIEW_TYPE_SSP_MF; - else if (labExperimentSubParts.length > 1 && contentFileLength > 1) + else if (labExperimentSubParts.length > 1) return VIEW_TYPE_MSP_MF; else return VIEW_TYPE_INVALID; @@ -149,6 +144,12 @@ public interface ItemClickHandler { void onContentFileClick(ContentFile file); } + static class InvalidViewHolder extends RecyclerView.ViewHolder { + InvalidViewHolder(@NonNull View itemView) { + super(itemView); + } + } + class SspSfViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { LayoutCardSeSspSfBinding mBinding; @@ -200,10 +201,4 @@ class MspMfViewHolder extends RecyclerView.ViewHolder { mBinding.subPartContainer.setHasFixedSize(true); } } - - class InvalidViewHolder extends RecyclerView.ViewHolder { - InvalidViewHolder(@NonNull View itemView) { - super(itemView); - } - } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/MultipleFileAdapter.java b/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/MultipleFileAdapter.java index 52b5161..dc4504b 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/MultipleFileAdapter.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/MultipleFileAdapter.java @@ -15,11 +15,11 @@ import com.nagpal.shivam.vtucslab.databinding.LayoutCardSingleFilesWithoutSubPartsBinding; public class MultipleFileAdapter extends RecyclerView.Adapter { - private Context mContext; - private ContentFile[] mContentFiles; - private ContentAdapter.ItemClickHandler mItemClickHandler; + private final Context mContext; + private final ContentFile[] mContentFiles; + private final ContentAdapter.ItemClickHandler mItemClickHandler; @LayoutRes - private int mLayoutId; + private final int mLayoutId; public MultipleFileAdapter(Context context, @LayoutRes int layoutId, ContentFile[] contentFiles, ContentAdapter.ItemClickHandler itemClickHandler) { mContext = context; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/MultipleSubPartAdapter.java b/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/MultipleSubPartAdapter.java index 1b94ac7..b093746 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/MultipleSubPartAdapter.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/MultipleSubPartAdapter.java @@ -18,10 +18,10 @@ import com.nagpal.shivam.vtucslab.databinding.LayoutCardSingleSubPartsWithoutFilesBinding; public class MultipleSubPartAdapter extends RecyclerView.Adapter { - private Context mContext; - private LabExperimentSubPart[] mSubParts; - private boolean mContainsMultipleFiles; - private ContentAdapter.ItemClickHandler mItemClickHandler; + private final Context mContext; + private final LabExperimentSubPart[] mSubParts; + private final boolean mContainsMultipleFiles; + private final ContentAdapter.ItemClickHandler mItemClickHandler; public MultipleSubPartAdapter(Context context, LabExperimentSubPart[] subParts, boolean containsMultipleFiles, ContentAdapter.ItemClickHandler itemClickHandler) { mContext = context; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/NavigationAdapter.java b/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/NavigationAdapter.java index cfc3a57..8e26780 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/NavigationAdapter.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/NavigationAdapter.java @@ -1,5 +1,6 @@ package com.nagpal.shivam.vtucslab.Adapter; +import android.annotation.SuppressLint; import android.content.Context; import android.view.LayoutInflater; import android.view.View; @@ -17,8 +18,8 @@ public class NavigationAdapter extends RecyclerView.Adapter { - private Context mContext; - private ArrayList mLaboratoryArrayList; + private final Context mContext; + private final ArrayList mLaboratoryArrayList; private NavigationAdapterItemClickHandler mNavigationAdapterItemClickHandler; public NavigationAdapter(@NonNull Context context, @NonNull ArrayList laboratoryArrayList) { @@ -26,18 +27,13 @@ public NavigationAdapter(@NonNull Context context, @NonNull ArrayList laboratoryArrayList) { - int i = mLaboratoryArrayList.size(); - mLaboratoryArrayList.addAll(laboratoryArrayList); - notifyItemRangeInserted(i, laboratoryArrayList.size()); - } - public void addAll(Laboratory[] laboratories) { int i = mLaboratoryArrayList.size(); mLaboratoryArrayList.addAll(Arrays.asList(laboratories)); notifyItemRangeInserted(i, laboratories.length); } + @SuppressLint("NotifyDataSetChanged") public void clear() { mLaboratoryArrayList.clear(); notifyDataSetChanged(); diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/InfoLoader.java b/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/InfoLoader.java index b0786d3..0541a4d 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/InfoLoader.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/InfoLoader.java @@ -11,7 +11,7 @@ import com.nagpal.shivam.vtucslab.Utility.FetchUtil; public class InfoLoader extends AsyncTaskLoader { - private String mUrl; + private final String mUrl; private LabResponse mLabResponse; public InfoLoader(Context context, String url) { @@ -44,7 +44,6 @@ private LabResponse extractFeaturesFromJson(String jsonResponse) { return null; } Gson gson = new Gson(); - LabResponse labResponse = gson.fromJson(jsonResponse, LabResponse.class); - return labResponse; + return gson.fromJson(jsonResponse, LabResponse.class); } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/RawStreamLoader.java b/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/RawStreamLoader.java index aac551e..924a4a3 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/RawStreamLoader.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/RawStreamLoader.java @@ -7,8 +7,7 @@ import com.nagpal.shivam.vtucslab.Utility.FetchUtil; public class RawStreamLoader extends AsyncTaskLoader { - private static final String RAW_STREAM_LOADER = "raw_stream_loader"; - private String mUrl; + private final String mUrl; private String fetchedData; // TODO: Implement Local Data Saving. diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Utility/FetchUtil.java b/app/src/main/java/com/nagpal/shivam/vtucslab/Utility/FetchUtil.java index a021f1c..bd0c64a 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Utility/FetchUtil.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/Utility/FetchUtil.java @@ -11,7 +11,7 @@ import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; public class FetchUtil { @@ -80,7 +80,7 @@ private static String makeHttpRequest(URL url) throws IOException { private static String readFromStream(InputStream inputStream) throws IOException { StringBuilder stringBuilder = new StringBuilder(); if (inputStream != null) { - InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8")); + InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String line = bufferedReader.readLine(); while (line != null) { From a386f2af88eb4d967d528478b0a2fc7c29d78699 Mon Sep 17 00:00:00 2001 From: WallE Date: Thu, 16 Mar 2023 02:55:23 +0530 Subject: [PATCH 03/50] refractor: Rename packages to follow conventions --- app/src/main/AndroidManifest.xml | 14 +++++++------- .../{Activity => activities}/DisplayActivity.java | 6 +++--- .../{Activity => activities}/ProgramActivity.java | 14 +++++++------- .../RepositoryActivity.java | 10 +++++----- .../{Adapter => adapters}/ContentAdapter.java | 10 +++++----- .../{Adapter => adapters}/MultipleFileAdapter.java | 6 +++--- .../MultipleSubPartAdapter.java | 8 ++++---- .../{Adapter => adapters}/NavigationAdapter.java | 4 ++-- .../vtucslab/{Loader => loaders}/InfoLoader.java | 6 +++--- .../{Loader => loaders}/RawStreamLoader.java | 4 ++-- .../vtucslab/{Model => models}/ContentFile.java | 2 +- .../vtucslab/{Model => models}/LabExperiment.java | 2 +- .../{Model => models}/LabExperimentSubPart.java | 2 +- .../vtucslab/{Model => models}/LabResponse.java | 2 +- .../vtucslab/{Model => models}/Laboratory.java | 2 +- .../{Utility => utilities}/ConstantVariables.java | 2 +- .../vtucslab/{Utility => utilities}/FetchUtil.java | 2 +- .../{Utility => utilities}/StaticMethods.java | 2 +- app/src/main/res/layout/activity_display.xml | 2 +- app/src/main/res/layout/activity_program.xml | 2 +- app/src/main/res/layout/activity_repository.xml | 2 +- app/src/main/res/layout/content_repository.xml | 2 +- app/src/main/res/layout/coordinator_repository.xml | 2 +- 23 files changed, 54 insertions(+), 54 deletions(-) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Activity => activities}/DisplayActivity.java (97%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Activity => activities}/ProgramActivity.java (93%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Activity => activities}/RepositoryActivity.java (96%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Adapter => adapters}/ContentAdapter.java (96%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Adapter => adapters}/MultipleFileAdapter.java (94%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Adapter => adapters}/MultipleSubPartAdapter.java (95%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Adapter => adapters}/NavigationAdapter.java (96%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Loader => loaders}/InfoLoader.java (87%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Loader => loaders}/RawStreamLoader.java (87%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Model => models}/ContentFile.java (83%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Model => models}/LabExperiment.java (93%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Model => models}/LabExperimentSubPart.java (92%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Model => models}/LabResponse.java (97%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Model => models}/Laboratory.java (90%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Utility => utilities}/ConstantVariables.java (76%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Utility => utilities}/FetchUtil.java (98%) rename app/src/main/java/com/nagpal/shivam/vtucslab/{Utility => utilities}/StaticMethods.java (76%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6186b9e..20b4e4e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,7 +20,7 @@ android:value="false" /> @@ -32,20 +32,20 @@ + android:parentActivityName=".activities.RepositoryActivity"> + android:value=".activities.RepositoryActivity" /> + android:name=".activities.DisplayActivity" + android:parentActivityName=".activities.ProgramActivity"> + android:value=".activities.ProgramActivity" /> diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/DisplayActivity.java b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DisplayActivity.java similarity index 97% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Activity/DisplayActivity.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/activities/DisplayActivity.java index 09d568c..a3c9ce4 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/DisplayActivity.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DisplayActivity.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Activity; +package com.nagpal.shivam.vtucslab.activities; import android.content.ClipData; import android.content.ClipboardManager; @@ -22,10 +22,10 @@ import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; -import com.nagpal.shivam.vtucslab.Loader.RawStreamLoader; import com.nagpal.shivam.vtucslab.R; -import com.nagpal.shivam.vtucslab.Utility.ConstantVariables; import com.nagpal.shivam.vtucslab.databinding.ActivityDisplayBinding; +import com.nagpal.shivam.vtucslab.loaders.RawStreamLoader; +import com.nagpal.shivam.vtucslab.utilities.ConstantVariables; public class DisplayActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/ProgramActivity.java b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/ProgramActivity.java similarity index 93% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Activity/ProgramActivity.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/activities/ProgramActivity.java index e4b145f..749ab8f 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/ProgramActivity.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/ProgramActivity.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Activity; +package com.nagpal.shivam.vtucslab.activities; import android.content.Intent; @@ -19,13 +19,13 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.nagpal.shivam.vtucslab.Adapter.ContentAdapter; -import com.nagpal.shivam.vtucslab.Loader.InfoLoader; -import com.nagpal.shivam.vtucslab.Model.ContentFile; -import com.nagpal.shivam.vtucslab.Model.LabResponse; -import com.nagpal.shivam.vtucslab.Model.Laboratory; import com.nagpal.shivam.vtucslab.R; -import com.nagpal.shivam.vtucslab.Utility.ConstantVariables; +import com.nagpal.shivam.vtucslab.adapters.ContentAdapter; +import com.nagpal.shivam.vtucslab.loaders.InfoLoader; +import com.nagpal.shivam.vtucslab.models.ContentFile; +import com.nagpal.shivam.vtucslab.models.LabResponse; +import com.nagpal.shivam.vtucslab.models.Laboratory; +import com.nagpal.shivam.vtucslab.utilities.ConstantVariables; import java.util.ArrayList; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/RepositoryActivity.java b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/RepositoryActivity.java similarity index 96% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Activity/RepositoryActivity.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/activities/RepositoryActivity.java index 94060b3..dca53aa 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Activity/RepositoryActivity.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/RepositoryActivity.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Activity; +package com.nagpal.shivam.vtucslab.activities; import android.content.Intent; import android.net.ConnectivityManager; @@ -27,11 +27,11 @@ import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.navigation.NavigationView; -import com.nagpal.shivam.vtucslab.Adapter.NavigationAdapter; -import com.nagpal.shivam.vtucslab.Loader.InfoLoader; -import com.nagpal.shivam.vtucslab.Model.LabResponse; -import com.nagpal.shivam.vtucslab.Model.Laboratory; import com.nagpal.shivam.vtucslab.R; +import com.nagpal.shivam.vtucslab.adapters.NavigationAdapter; +import com.nagpal.shivam.vtucslab.loaders.InfoLoader; +import com.nagpal.shivam.vtucslab.models.LabResponse; +import com.nagpal.shivam.vtucslab.models.Laboratory; import java.util.ArrayList; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/ContentAdapter.java b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/ContentAdapter.java similarity index 96% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/ContentAdapter.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/adapters/ContentAdapter.java index 2888ca7..2c43fb4 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/ContentAdapter.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/ContentAdapter.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Adapter; +package com.nagpal.shivam.vtucslab.adapters; import android.annotation.SuppressLint; import android.content.Context; @@ -11,14 +11,14 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.nagpal.shivam.vtucslab.Model.ContentFile; -import com.nagpal.shivam.vtucslab.Model.LabExperiment; -import com.nagpal.shivam.vtucslab.Model.LabExperimentSubPart; import com.nagpal.shivam.vtucslab.R; -import com.nagpal.shivam.vtucslab.Utility.StaticMethods; import com.nagpal.shivam.vtucslab.databinding.LayoutCardSeMspBinding; import com.nagpal.shivam.vtucslab.databinding.LayoutCardSeSspMfBinding; import com.nagpal.shivam.vtucslab.databinding.LayoutCardSeSspSfBinding; +import com.nagpal.shivam.vtucslab.models.ContentFile; +import com.nagpal.shivam.vtucslab.models.LabExperiment; +import com.nagpal.shivam.vtucslab.models.LabExperimentSubPart; +import com.nagpal.shivam.vtucslab.utilities.StaticMethods; import java.util.ArrayList; import java.util.Arrays; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/MultipleFileAdapter.java b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleFileAdapter.java similarity index 94% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/MultipleFileAdapter.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleFileAdapter.java index dc4504b..866bf61 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/MultipleFileAdapter.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleFileAdapter.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Adapter; +package com.nagpal.shivam.vtucslab.adapters; import android.content.Context; import android.view.LayoutInflater; @@ -10,9 +10,9 @@ import androidx.databinding.DataBindingUtil; import androidx.recyclerview.widget.RecyclerView; -import com.nagpal.shivam.vtucslab.Model.ContentFile; -import com.nagpal.shivam.vtucslab.Utility.StaticMethods; import com.nagpal.shivam.vtucslab.databinding.LayoutCardSingleFilesWithoutSubPartsBinding; +import com.nagpal.shivam.vtucslab.models.ContentFile; +import com.nagpal.shivam.vtucslab.utilities.StaticMethods; public class MultipleFileAdapter extends RecyclerView.Adapter { private final Context mContext; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/MultipleSubPartAdapter.java b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleSubPartAdapter.java similarity index 95% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/MultipleSubPartAdapter.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleSubPartAdapter.java index b093746..be16263 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/MultipleSubPartAdapter.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleSubPartAdapter.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Adapter; +package com.nagpal.shivam.vtucslab.adapters; import android.content.Context; import android.view.LayoutInflater; @@ -10,12 +10,12 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.nagpal.shivam.vtucslab.Model.ContentFile; -import com.nagpal.shivam.vtucslab.Model.LabExperimentSubPart; import com.nagpal.shivam.vtucslab.R; -import com.nagpal.shivam.vtucslab.Utility.StaticMethods; import com.nagpal.shivam.vtucslab.databinding.LayoutCardSingleSubPartsWithFilesBinding; import com.nagpal.shivam.vtucslab.databinding.LayoutCardSingleSubPartsWithoutFilesBinding; +import com.nagpal.shivam.vtucslab.models.ContentFile; +import com.nagpal.shivam.vtucslab.models.LabExperimentSubPart; +import com.nagpal.shivam.vtucslab.utilities.StaticMethods; public class MultipleSubPartAdapter extends RecyclerView.Adapter { private final Context mContext; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/NavigationAdapter.java b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/NavigationAdapter.java similarity index 96% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/NavigationAdapter.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/adapters/NavigationAdapter.java index 8e26780..2561b6f 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Adapter/NavigationAdapter.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/NavigationAdapter.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Adapter; +package com.nagpal.shivam.vtucslab.adapters; import android.annotation.SuppressLint; import android.content.Context; @@ -10,8 +10,8 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; -import com.nagpal.shivam.vtucslab.Model.Laboratory; import com.nagpal.shivam.vtucslab.R; +import com.nagpal.shivam.vtucslab.models.Laboratory; import java.util.ArrayList; import java.util.Arrays; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/InfoLoader.java b/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/InfoLoader.java similarity index 87% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Loader/InfoLoader.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/loaders/InfoLoader.java index 0541a4d..38e2905 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/InfoLoader.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/InfoLoader.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Loader; +package com.nagpal.shivam.vtucslab.loaders; import android.content.Context; @@ -7,8 +7,8 @@ import androidx.loader.content.AsyncTaskLoader; import com.google.gson.Gson; -import com.nagpal.shivam.vtucslab.Model.LabResponse; -import com.nagpal.shivam.vtucslab.Utility.FetchUtil; +import com.nagpal.shivam.vtucslab.models.LabResponse; +import com.nagpal.shivam.vtucslab.utilities.FetchUtil; public class InfoLoader extends AsyncTaskLoader { private final String mUrl; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/RawStreamLoader.java b/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/RawStreamLoader.java similarity index 87% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Loader/RawStreamLoader.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/loaders/RawStreamLoader.java index 924a4a3..a2e81c9 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Loader/RawStreamLoader.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/RawStreamLoader.java @@ -1,10 +1,10 @@ -package com.nagpal.shivam.vtucslab.Loader; +package com.nagpal.shivam.vtucslab.loaders; import android.content.Context; import androidx.loader.content.AsyncTaskLoader; -import com.nagpal.shivam.vtucslab.Utility.FetchUtil; +import com.nagpal.shivam.vtucslab.utilities.FetchUtil; public class RawStreamLoader extends AsyncTaskLoader { private final String mUrl; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Model/ContentFile.java b/app/src/main/java/com/nagpal/shivam/vtucslab/models/ContentFile.java similarity index 83% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Model/ContentFile.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/models/ContentFile.java index e6e087d..4dce005 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Model/ContentFile.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/models/ContentFile.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Model; +package com.nagpal.shivam.vtucslab.models; public class ContentFile { private String fileName; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Model/LabExperiment.java b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperiment.java similarity index 93% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Model/LabExperiment.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperiment.java index 236555b..1a04d5a 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Model/LabExperiment.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperiment.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Model; +package com.nagpal.shivam.vtucslab.models; public class LabExperiment { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Model/LabExperimentSubPart.java b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperimentSubPart.java similarity index 92% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Model/LabExperimentSubPart.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperimentSubPart.java index 61683a0..eef8030 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Model/LabExperimentSubPart.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperimentSubPart.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Model; +package com.nagpal.shivam.vtucslab.models; public class LabExperimentSubPart { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Model/LabResponse.java b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabResponse.java similarity index 97% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Model/LabResponse.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/models/LabResponse.java index e7421d3..2af9474 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Model/LabResponse.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabResponse.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Model; +package com.nagpal.shivam.vtucslab.models; public class LabResponse { private String context; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Model/Laboratory.java b/app/src/main/java/com/nagpal/shivam/vtucslab/models/Laboratory.java similarity index 90% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Model/Laboratory.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/models/Laboratory.java index 7ae3a03..8cd086c 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Model/Laboratory.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/models/Laboratory.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Model; +package com.nagpal.shivam.vtucslab.models; import java.io.Serializable; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Utility/ConstantVariables.java b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/ConstantVariables.java similarity index 76% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Utility/ConstantVariables.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/utilities/ConstantVariables.java index c1738c5..06172c1 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Utility/ConstantVariables.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/ConstantVariables.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Utility; +package com.nagpal.shivam.vtucslab.utilities; public class ConstantVariables { public static final String title_intent_tag = "title"; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Utility/FetchUtil.java b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/FetchUtil.java similarity index 98% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Utility/FetchUtil.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/utilities/FetchUtil.java index bd0c64a..36ced38 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Utility/FetchUtil.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/FetchUtil.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Utility; +package com.nagpal.shivam.vtucslab.utilities; import static java.net.HttpURLConnection.HTTP_OK; diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/Utility/StaticMethods.java b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.java similarity index 76% rename from app/src/main/java/com/nagpal/shivam/vtucslab/Utility/StaticMethods.java rename to app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.java index e466ed7..18c7fad 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/Utility/StaticMethods.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.java @@ -1,4 +1,4 @@ -package com.nagpal.shivam.vtucslab.Utility; +package com.nagpal.shivam.vtucslab.utilities; public class StaticMethods { diff --git a/app/src/main/res/layout/activity_display.xml b/app/src/main/res/layout/activity_display.xml index f351598..ab852d8 100644 --- a/app/src/main/res/layout/activity_display.xml +++ b/app/src/main/res/layout/activity_display.xml @@ -7,7 +7,7 @@ android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false" - tools:context="com.nagpal.shivam.vtucslab.Activity.DisplayActivity"> + tools:context="com.nagpal.shivam.vtucslab.activities.DisplayActivity"> + tools:context=".activities.ProgramActivity"> diff --git a/app/src/main/res/layout/content_repository.xml b/app/src/main/res/layout/content_repository.xml index 69b70d3..5afeda9 100644 --- a/app/src/main/res/layout/content_repository.xml +++ b/app/src/main/res/layout/content_repository.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" - tools:context=".Activity.RepositoryActivity" + tools:context=".activities.RepositoryActivity" tools:showIn="@layout/activity_repository"> + tools:context=".activities.RepositoryActivity"> Date: Thu, 16 Mar 2023 03:03:26 +0530 Subject: [PATCH 04/50] chore: Disable Jetifier in the Gradle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 40edf30..3233031 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -android.enableJetifier=true +android.enableJetifier=false android.useAndroidX=true org.gradle.jvmargs=-Xmx1024m From f34aa551250a97aeedc6ee53b0c4ce1700202cc2 Mon Sep 17 00:00:00 2001 From: WallE Date: Thu, 16 Mar 2023 03:16:57 +0530 Subject: [PATCH 05/50] chore: Integrate Kotlin --- app/build.gradle | 1 + build.gradle | 1 + 2 files changed, 2 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index ce0e08a..061fb86 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -50,3 +50,4 @@ dependencies { } apply plugin: 'com.google.gms.google-services' +apply plugin: 'org.jetbrains.kotlin.android' diff --git a/build.gradle b/build.gradle index e454feb..1c5b393 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,7 @@ buildscript { classpath 'com.android.tools.build:gradle:7.4.2' classpath 'com.google.gms:google-services:4.3.15' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.4' + classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } From 5f4f9a406ff427504e3e209f1764319aff78e235 Mon Sep 17 00:00:00 2001 From: WallE Date: Thu, 16 Mar 2023 17:02:07 +0530 Subject: [PATCH 06/50] feature: Integrate Retrofit to replace the URLConnection being used to fetch the Data --- app/build.gradle | 9 ++ app/src/main/AndroidManifest.xml | 4 +- .../vtucslab/activities/DisplayActivity.java | 6 +- .../vtucslab/activities/ProgramActivity.java | 6 +- .../shivam/vtucslab/loaders/InfoLoader.java | 23 +++-- .../vtucslab/loaders/RawStreamLoader.java | 19 +++- .../vtucslab/services/VtuCsLabService.kt | 25 +++++ .../vtucslab/utilities/ConstantVariables.java | 6 -- .../shivam/vtucslab/utilities/Constants.kt | 7 ++ .../shivam/vtucslab/utilities/FetchUtil.java | 97 ------------------- .../vtucslab/utilities/StaticMethods.java | 8 -- .../vtucslab/utilities/StaticMethods.kt | 18 ++++ .../shivam/vtucslab/ExampleUnitTest.java | 4 +- 13 files changed, 96 insertions(+), 136 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/utilities/ConstantVariables.java create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/utilities/FetchUtil.java delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.java create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt diff --git a/app/build.gradle b/app/build.gradle index 061fb86..1bc71e4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,6 +25,9 @@ android { } dependencies { + def retrofitVersion = '2.9.0' + + implementation 'androidx.core:core-ktx:1.9.0' implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'androidx.appcompat:appcompat:1.6.1' @@ -44,6 +47,12 @@ dependencies { } implementation 'com.google.firebase:firebase-crashlytics:18.3.5' +// Retrofit + + implementation 'com.squareup.retrofit2:retrofit:' + retrofitVersion + implementation 'com.squareup.retrofit2:converter-gson:' + retrofitVersion + implementation 'com.squareup.retrofit2:converter-scalars:' + retrofitVersion + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.5.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 20b4e4e..00c0469 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,9 +21,9 @@ + android:theme="@style/AppTheme.NoActionBar"> diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DisplayActivity.java b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DisplayActivity.java index a3c9ce4..b8e14a4 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DisplayActivity.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DisplayActivity.java @@ -25,7 +25,7 @@ import com.nagpal.shivam.vtucslab.R; import com.nagpal.shivam.vtucslab.databinding.ActivityDisplayBinding; import com.nagpal.shivam.vtucslab.loaders.RawStreamLoader; -import com.nagpal.shivam.vtucslab.utilities.ConstantVariables; +import com.nagpal.shivam.vtucslab.utilities.Constants; public class DisplayActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks { @@ -90,12 +90,12 @@ protected void onCreate(Bundle savedInstanceState) { Intent intent = getIntent(); - mUrl = intent.getStringExtra(ConstantVariables.url_intent_tag); + mUrl = intent.getStringExtra(Constants.URL_INTENT_TAG); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); } - String title = intent.getStringExtra(ConstantVariables.title_intent_tag); + String title = intent.getStringExtra(Constants.TITLE_INTENT_TAG); DisplayActivity.this.setTitle(title); ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/ProgramActivity.java b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/ProgramActivity.java index 749ab8f..fd8022a 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/ProgramActivity.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/ProgramActivity.java @@ -25,7 +25,7 @@ import com.nagpal.shivam.vtucslab.models.ContentFile; import com.nagpal.shivam.vtucslab.models.LabResponse; import com.nagpal.shivam.vtucslab.models.Laboratory; -import com.nagpal.shivam.vtucslab.utilities.ConstantVariables; +import com.nagpal.shivam.vtucslab.utilities.Constants; import java.util.ArrayList; @@ -163,8 +163,8 @@ protected void onSaveInstanceState(Bundle outState) { @Override public void onContentFileClick(ContentFile file) { Intent intent = new Intent(ProgramActivity.this, DisplayActivity.class); - intent.putExtra(ConstantVariables.title_intent_tag, file.getFileName()); - intent.putExtra(ConstantVariables.url_intent_tag, mProgramBaseUrl + "/" + file.getFileName()); + intent.putExtra(Constants.TITLE_INTENT_TAG, file.getFileName()); + intent.putExtra(Constants.URL_INTENT_TAG, mProgramBaseUrl + "/" + file.getFileName()); startActivity(intent); } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/InfoLoader.java b/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/InfoLoader.java index 38e2905..ef50a66 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/InfoLoader.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/InfoLoader.java @@ -2,13 +2,15 @@ import android.content.Context; -import android.text.TextUtils; import androidx.loader.content.AsyncTaskLoader; -import com.google.gson.Gson; import com.nagpal.shivam.vtucslab.models.LabResponse; -import com.nagpal.shivam.vtucslab.utilities.FetchUtil; +import com.nagpal.shivam.vtucslab.services.VtuCsLabService; + +import java.io.IOException; + +import retrofit2.Call; public class InfoLoader extends AsyncTaskLoader { private final String mUrl; @@ -34,16 +36,13 @@ public LabResponse loadInBackground() { if (mUrl == null) { return null; } - String jsonResponse = FetchUtil.fetchData(mUrl); - mLabResponse = extractFeaturesFromJson(jsonResponse); - return mLabResponse; - } - - private LabResponse extractFeaturesFromJson(String jsonResponse) { - if (TextUtils.isEmpty(jsonResponse)) { + VtuCsLabService vtuCSLabService = VtuCsLabService.Companion.getInstance(); + Call labResponseCall = vtuCSLabService.getLabResponse(mUrl); + try { + mLabResponse = labResponseCall.execute().body(); + return mLabResponse; + } catch (IOException e) { return null; } - Gson gson = new Gson(); - return gson.fromJson(jsonResponse, LabResponse.class); } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/RawStreamLoader.java b/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/RawStreamLoader.java index a2e81c9..c34daf6 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/RawStreamLoader.java +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/RawStreamLoader.java @@ -4,7 +4,11 @@ import androidx.loader.content.AsyncTaskLoader; -import com.nagpal.shivam.vtucslab.utilities.FetchUtil; +import com.nagpal.shivam.vtucslab.services.VtuCsLabService; + +import java.io.IOException; + +import retrofit2.Call; public class RawStreamLoader extends AsyncTaskLoader { private final String mUrl; @@ -27,7 +31,16 @@ protected void onStartLoading() { @Override public String loadInBackground() { - fetchedData = FetchUtil.fetchData(mUrl); - return fetchedData; + if (mUrl == null) { + return null; + } + VtuCsLabService vtuCSLabService = VtuCsLabService.Companion.getInstance(); + Call stringCall = vtuCSLabService.fetchRawResponse(mUrl); + try { + fetchedData = stringCall.execute().body(); + return fetchedData; + } catch (IOException e) { + throw new RuntimeException(e); + } } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt new file mode 100644 index 0000000..67b2432 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt @@ -0,0 +1,25 @@ +package com.nagpal.shivam.vtucslab.services + +import com.nagpal.shivam.vtucslab.models.LabResponse +import com.nagpal.shivam.vtucslab.utilities.Constants +import com.nagpal.shivam.vtucslab.utilities.StaticMethods +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Url + +interface VtuCsLabService { + @GET + fun getLabResponse(@Url url: String): Call + + @GET + fun fetchRawResponse(@Url url: String): Call + + companion object { + val instance: VtuCsLabService by lazy { + val retrofit = StaticMethods.getRetrofitBuilder() + .baseUrl(Constants.GITHUB_RAW_BASE_URL) + .build() + return@lazy retrofit.create(VtuCsLabService::class.java) + } + } +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/ConstantVariables.java b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/ConstantVariables.java deleted file mode 100644 index 06172c1..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/ConstantVariables.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.nagpal.shivam.vtucslab.utilities; - -public class ConstantVariables { - public static final String title_intent_tag = "title"; - public static final String url_intent_tag = "url"; -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt new file mode 100644 index 0000000..0d1dfda --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt @@ -0,0 +1,7 @@ +package com.nagpal.shivam.vtucslab.utilities + +object Constants { + const val TITLE_INTENT_TAG = "title" + const val URL_INTENT_TAG = "url" + const val GITHUB_RAW_BASE_URL = "https://raw.githubusercontent.com" +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/FetchUtil.java b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/FetchUtil.java deleted file mode 100644 index 36ced38..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/FetchUtil.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.nagpal.shivam.vtucslab.utilities; - -import static java.net.HttpURLConnection.HTTP_OK; - -import android.util.Log; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.charset.StandardCharsets; - -public class FetchUtil { - - private static final String LOG_TAG = "FetchUtils"; - - public FetchUtil() { - } - - public static String fetchData(String requestUrl) { - Log.i(LOG_TAG, "Fetching Data."); - URL url = createUrl(requestUrl); - String jsonResponse = ""; - try { - jsonResponse = makeHttpRequest(url); - } catch (IOException e) { - Log.e(LOG_TAG, "Error while making HTTP request.", e); - } - return jsonResponse; - } - - private static URL createUrl(String stringUrl) { - URL url = null; - try { - url = new URL(stringUrl); - } catch (MalformedURLException e) { - Log.e(LOG_TAG, "Error with url creation", e); - } - return url; - } - - private static String makeHttpRequest(URL url) throws IOException { - String jsonResponse = ""; - if (url == null) { - return jsonResponse; - } - - HttpURLConnection urlConnection = null; - InputStream inputStream = null; - try { - urlConnection = (HttpURLConnection) url.openConnection(); - urlConnection.setReadTimeout(10000); - urlConnection.setConnectTimeout(15000); - urlConnection.setRequestMethod("GET"); - urlConnection.connect(); - - int responseCode = urlConnection.getResponseCode(); - if (responseCode == HTTP_OK) { - inputStream = urlConnection.getInputStream(); - jsonResponse = readFromStream(inputStream); - } else { - Log.e(LOG_TAG, "Error Response Code: " + responseCode); - } - } catch (IOException e) { - Log.e(LOG_TAG, "Problem retrieving JSON response\n"); - } finally { - if (urlConnection != null) { - urlConnection.disconnect(); - } - if (inputStream != null) { - inputStream.close(); - } - } - return jsonResponse; - } - - private static String readFromStream(InputStream inputStream) throws IOException { - StringBuilder stringBuilder = new StringBuilder(); - if (inputStream != null) { - InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); - BufferedReader bufferedReader = new BufferedReader(inputStreamReader); - String line = bufferedReader.readLine(); - while (line != null) { - //TODO: Temporary Solution to add padding with white spaces - stringBuilder.append(" "); - stringBuilder.append(line); - stringBuilder.append(" \n"); - line = bufferedReader.readLine(); - } - } - return stringBuilder.toString(); - } - -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.java b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.java deleted file mode 100644 index 18c7fad..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.nagpal.shivam.vtucslab.utilities; - -public class StaticMethods { - - public static String formatProgramName(String programName) { - return programName.replace('_', ' '); - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt new file mode 100644 index 0000000..7b838ec --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt @@ -0,0 +1,18 @@ +package com.nagpal.shivam.vtucslab.utilities + +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.scalars.ScalarsConverterFactory + +object StaticMethods { + @JvmStatic + fun formatProgramName(programName: String): String { + return programName.replace('_', ' ') + } + + fun getRetrofitBuilder(): Retrofit.Builder { + return Retrofit.Builder() + .addConverterFactory(ScalarsConverterFactory.create()) + .addConverterFactory(GsonConverterFactory.create()) + } +} diff --git a/app/src/test/java/com/nagpal/shivam/vtucslab/ExampleUnitTest.java b/app/src/test/java/com/nagpal/shivam/vtucslab/ExampleUnitTest.java index 37d7a91..d40a51e 100644 --- a/app/src/test/java/com/nagpal/shivam/vtucslab/ExampleUnitTest.java +++ b/app/src/test/java/com/nagpal/shivam/vtucslab/ExampleUnitTest.java @@ -1,8 +1,8 @@ package com.nagpal.shivam.vtucslab; -import org.junit.Test; +import static org.junit.Assert.assertEquals; -import static org.junit.Assert.*; +import org.junit.Test; /** * Example local unit test, which will execute on the development machine (host). From 70773d47bd010fe3e641fe1d42fcabfce7b33ac4 Mon Sep 17 00:00:00 2001 From: WallE Date: Fri, 17 Mar 2023 02:27:27 +0530 Subject: [PATCH 07/50] chore: Integrate Firebase BOM --- app/build.gradle | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 1bc71e4..5a01c15 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,6 +28,7 @@ dependencies { def retrofitVersion = '2.9.0' implementation 'androidx.core:core-ktx:1.9.0' + implementation platform('com.google.firebase:firebase-bom:31.2.3') implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'androidx.appcompat:appcompat:1.6.1' @@ -39,13 +40,8 @@ dependencies { implementation 'com.google.code.gson:gson:2.8.9' // Firebase SDK - implementation('com.google.firebase:firebase-core:21.1.1') { - exclude module: 'support-v4' - } - implementation('com.google.firebase:firebase-messaging:23.1.2') { - exclude module: 'support-v4' - } - implementation 'com.google.firebase:firebase-crashlytics:18.3.5' + implementation 'com.google.firebase:firebase-messaging-ktx' + implementation 'com.google.firebase:firebase-crashlytics-ktx' // Retrofit From 1211f058906d3cf8dceddaceaf258ab56d215359 Mon Sep 17 00:00:00 2001 From: WallE Date: Fri, 17 Mar 2023 03:07:20 +0530 Subject: [PATCH 08/50] feature: Setup Navigation, Main Activity and Repository Screen --- app/build.gradle | 18 +++++---- app/src/main/AndroidManifest.xml | 11 ++++++ .../vtucslab/activities/MainActivity.kt | 15 ++++++++ .../screens/repository/RepositoryFragment.kt | 16 ++++++++ app/src/main/res/layout/activity_main.xml | 22 +++++++++++ app/src/main/res/layout/content_main.xml | 22 +++++++++++ app/src/main/res/layout/coordinator_main.xml | 26 +++++++++++++ .../main/res/layout/fragment_repository.xml | 13 +++++++ app/src/main/res/layout/header_main.xml | 37 +++++++++++++++++++ .../res/navigation/main_navigation_graph.xml | 12 ++++++ app/src/main/res/values/strings.xml | 1 + 11 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/layout/content_main.xml create mode 100644 app/src/main/res/layout/coordinator_main.xml create mode 100644 app/src/main/res/layout/fragment_repository.xml create mode 100644 app/src/main/res/layout/header_main.xml create mode 100644 app/src/main/res/navigation/main_navigation_graph.xml diff --git a/app/build.gradle b/app/build.gradle index 5a01c15..7b90a77 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,6 +26,7 @@ android { dependencies { def retrofitVersion = '2.9.0' + def navigationVersion = "2.5.3" implementation 'androidx.core:core-ktx:1.9.0' implementation platform('com.google.firebase:firebase-bom:31.2.3') @@ -36,18 +37,21 @@ dependencies { implementation 'com.google.android.material:material:1.8.0' implementation "androidx.multidex:multidex:2.0.1" -// GSON + // Navigation + implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion" + implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion" + + // GSON implementation 'com.google.code.gson:gson:2.8.9' -// Firebase SDK + // Firebase SDK implementation 'com.google.firebase:firebase-messaging-ktx' implementation 'com.google.firebase:firebase-crashlytics-ktx' -// Retrofit - - implementation 'com.squareup.retrofit2:retrofit:' + retrofitVersion - implementation 'com.squareup.retrofit2:converter-gson:' + retrofitVersion - implementation 'com.squareup.retrofit2:converter-scalars:' + retrofitVersion + // Retrofit + implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" + implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion" + implementation "com.squareup.retrofit2:converter-scalars:$retrofitVersion" testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.5.2' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 00c0469..0d46171 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,6 +14,17 @@ android:supportsRtl="true" android:theme="@style/AppTheme" tools:ignore="GoogleAppIndexingWarning"> + + + + + + + (R.id.toolbar) + setSupportActionBar(toolbar) + } +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt new file mode 100644 index 0000000..31d75bf --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt @@ -0,0 +1,16 @@ +package com.nagpal.shivam.vtucslab.screens.repository + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.nagpal.shivam.vtucslab.R + +class RepositoryFragment : Fragment() { + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_repository, container, false) + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..4c2d448 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml new file mode 100644 index 0000000..53bf179 --- /dev/null +++ b/app/src/main/res/layout/content_main.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/app/src/main/res/layout/coordinator_main.xml b/app/src/main/res/layout/coordinator_main.xml new file mode 100644 index 0000000..486bd55 --- /dev/null +++ b/app/src/main/res/layout/coordinator_main.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_repository.xml b/app/src/main/res/layout/fragment_repository.xml new file mode 100644 index 0000000..4e6c510 --- /dev/null +++ b/app/src/main/res/layout/fragment_repository.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/layout/header_main.xml b/app/src/main/res/layout/header_main.xml new file mode 100644 index 0000000..a46cc54 --- /dev/null +++ b/app/src/main/res/layout/header_main.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/navigation/main_navigation_graph.xml b/app/src/main/res/navigation/main_navigation_graph.xml new file mode 100644 index 0000000..243f508 --- /dev/null +++ b/app/src/main/res/navigation/main_navigation_graph.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 93f666d..e095698 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,6 @@ VTU CS Lab + VTU CS Lab New Version No Internet Connection. Some Error Occurred. Copy From 2686b45747d51c4cb98c2a94d02f328676069c97 Mon Sep 17 00:00:00 2001 From: WallE Date: Fri, 17 Mar 2023 03:33:57 +0530 Subject: [PATCH 09/50] feature: Integrate ViewBinding --- app/build.gradle | 3 ++- .../nagpal/shivam/vtucslab/activities/MainActivity.kt | 10 +++++----- app/src/main/res/layout/activity_main.xml | 4 +++- app/src/main/res/layout/coordinator_main.xml | 4 +++- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7b90a77..899a0bc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,7 +19,8 @@ android { } } buildFeatures { - dataBinding true + dataBinding true // TODO: Remove DataBinding post cleanup + viewBinding true } namespace 'com.nagpal.shivam.vtucslab' } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt index 125bc66..96607a9 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt @@ -2,14 +2,14 @@ package com.nagpal.shivam.vtucslab.activities import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.widget.Toolbar -import com.nagpal.shivam.vtucslab.R +import com.nagpal.shivam.vtucslab.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { + private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - val toolbar = findViewById(R.id.toolbar) - setSupportActionBar(toolbar) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + setSupportActionBar(binding.coordinator.toolbar) } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 4c2d448..8cd32dd 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,7 +8,9 @@ tools:context="com.nagpal.shivam.vtucslab.activities.MainActivity" tools:openDrawer="start"> - + - + From d59629a0cafee93446a38fcd89a78c564ec09a86 Mon Sep 17 00:00:00 2001 From: WallE Date: Fri, 17 Mar 2023 03:36:43 +0530 Subject: [PATCH 10/50] refractor: Remove unused layout files --- .../layout/layout_content_main_activity.xml | 36 ------------------- .../layout_coordinator_main_activity.xml | 20 ----------- 2 files changed, 56 deletions(-) delete mode 100644 app/src/main/res/layout/layout_content_main_activity.xml delete mode 100644 app/src/main/res/layout/layout_coordinator_main_activity.xml diff --git a/app/src/main/res/layout/layout_content_main_activity.xml b/app/src/main/res/layout/layout_content_main_activity.xml deleted file mode 100644 index b8f8617..0000000 --- a/app/src/main/res/layout/layout_content_main_activity.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/layout/layout_coordinator_main_activity.xml b/app/src/main/res/layout/layout_coordinator_main_activity.xml deleted file mode 100644 index d08577a..0000000 --- a/app/src/main/res/layout/layout_coordinator_main_activity.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - \ No newline at end of file From 6d0da932daf501acd91f73a1d41a88d7ecb0f4d1 Mon Sep 17 00:00:00 2001 From: WallE Date: Fri, 17 Mar 2023 14:00:46 +0530 Subject: [PATCH 11/50] feature: Setup Drawer Layout and Navigation View --- .../vtucslab/activities/MainActivity.kt | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt index 96607a9..7f3e52f 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt @@ -1,15 +1,77 @@ package com.nagpal.shivam.vtucslab.activities import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.view.MenuItem +import android.widget.ImageButton +import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.GravityCompat +import com.google.android.material.navigation.NavigationView +import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.databinding.ActivityMainBinding +import kotlin.system.exitProcess -class MainActivity : AppCompatActivity() { +class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) setSupportActionBar(binding.coordinator.toolbar) + setupDrawerToggle() + setupNavigationView() + } + + private fun setupNavigationView() { + val navigationView = binding.navigationView + navigationView.setNavigationItemSelectedListener(this) + navigationView.menu.getItem(0).isChecked = true + + val headerView = navigationView.getHeaderView(0) + val navigationDrawerBackButton = + headerView.findViewById(R.id.close_drawer) + navigationDrawerBackButton.setOnClickListener { closeNavigationDrawer() } + } + + private fun setupDrawerToggle() { + val actionBarDrawerToggle = ActionBarDrawerToggle( + this, + binding.drawerLayout, + binding.coordinator.toolbar, + R.string.drawer_open, + R.string.drawer_close + ) + binding.drawerLayout.addDrawerListener(actionBarDrawerToggle) + actionBarDrawerToggle.syncState() + } + + override fun onNavigationItemSelected(item: MenuItem): Boolean { + var flag = false + val itemId: Int = item.itemId + if (itemId == R.id.menu_item_repository) { + flag = true + } else if (itemId == R.id.menu_item_exit) { + exitApplication() + flag = true + } + if (flag) { + closeNavigationDrawer() + } + return flag + } + + private fun exitApplication() { + finishAffinity() + Handler(Looper.getMainLooper()).postDelayed({ + exitProcess( + 0 + ) + }, 1000) + } + + private fun closeNavigationDrawer() { + binding.drawerLayout.closeDrawer(GravityCompat.START, true) } } From 351f7d5f6e4fae8569ecf010164e8205c78e72dd Mon Sep 17 00:00:00 2001 From: WallE Date: Fri, 17 Mar 2023 23:47:38 +0530 Subject: [PATCH 12/50] feature: Integrate back button with the drawerlayout --- .../vtucslab/activities/MainActivity.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt index 7f3e52f..7043eda 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt @@ -4,10 +4,13 @@ import android.os.Bundle import android.os.Handler import android.os.Looper import android.view.MenuItem +import android.view.View import android.widget.ImageButton +import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.AppCompatActivity import androidx.core.view.GravityCompat +import androidx.drawerlayout.widget.DrawerLayout.DrawerListener import com.google.android.material.navigation.NavigationView import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.databinding.ActivityMainBinding @@ -45,6 +48,28 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte ) binding.drawerLayout.addDrawerListener(actionBarDrawerToggle) actionBarDrawerToggle.syncState() + + val drawerBackPressedCallback = object : OnBackPressedCallback(false) { + override fun handleOnBackPressed() { + closeNavigationDrawer() + } + } + onBackPressedDispatcher.addCallback(this, drawerBackPressedCallback) + binding.drawerLayout.addDrawerListener(object : DrawerListener { + override fun onDrawerSlide(drawerView: View, slideOffset: Float) { + } + + override fun onDrawerOpened(drawerView: View) { + drawerBackPressedCallback.isEnabled = true + } + + override fun onDrawerClosed(drawerView: View) { + drawerBackPressedCallback.isEnabled = false + } + + override fun onDrawerStateChanged(newState: Int) { + } + }) } override fun onNavigationItemSelected(item: MenuItem): Boolean { From 713e44b9dfc9393f5b2bd4fd9d29f97aa277978d Mon Sep 17 00:00:00 2001 From: WallE Date: Sat, 18 Mar 2023 02:16:06 +0530 Subject: [PATCH 13/50] feature: Add Menu Provider For the Repository Fragment --- .../screens/repository/RepositoryFragment.kt | 42 ++++++++++++++++--- .../vtucslab/services/VtuCsLabService.kt | 4 +- .../shivam/vtucslab/utilities/Constants.kt | 2 + .../vtucslab/utilities/StaticMethods.kt | 4 +- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt index 31d75bf..82931b9 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt @@ -1,16 +1,46 @@ package com.nagpal.shivam.vtucslab.screens.repository +import android.content.Intent +import android.net.Uri import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.view.* +import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment import com.nagpal.shivam.vtucslab.R +import com.nagpal.shivam.vtucslab.databinding.FragmentRepositoryBinding +import com.nagpal.shivam.vtucslab.utilities.Constants class RepositoryFragment : Fragment() { + private var _binding: FragmentRepositoryBinding? = null - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fragment_repository, container, false) + private val binding get() = _binding!! + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentRepositoryBinding.inflate(inflater, container, false) + requireActivity().addMenuProvider(object : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + menuInflater.inflate(R.menu.menu_main_activity, menu) + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + return when (menuItem.itemId) { + R.id.main_menu_item_privacy -> { + val intent = Intent(Intent.ACTION_VIEW) + intent.data = Uri.parse(Constants.PRIVACY_POLICY_LINK) + startActivity(intent) + true + } + else -> false + } + } + }) + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt index 67b2432..56e3a6c 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt @@ -17,8 +17,8 @@ interface VtuCsLabService { companion object { val instance: VtuCsLabService by lazy { val retrofit = StaticMethods.getRetrofitBuilder() - .baseUrl(Constants.GITHUB_RAW_BASE_URL) - .build() + .baseUrl(Constants.GITHUB_RAW_BASE_URL) + .build() return@lazy retrofit.create(VtuCsLabService::class.java) } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt index 0d1dfda..e1ca2cf 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt @@ -4,4 +4,6 @@ object Constants { const val TITLE_INTENT_TAG = "title" const val URL_INTENT_TAG = "url" const val GITHUB_RAW_BASE_URL = "https://raw.githubusercontent.com" + const val PRIVACY_POLICY_LINK = + "https://github.com/ShivamNagpal/Privacy_Policies/blob/master/VTU_CS_LAB_MANUAL.md" } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt index 7b838ec..0ce4ac9 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt @@ -12,7 +12,7 @@ object StaticMethods { fun getRetrofitBuilder(): Retrofit.Builder { return Retrofit.Builder() - .addConverterFactory(ScalarsConverterFactory.create()) - .addConverterFactory(GsonConverterFactory.create()) + .addConverterFactory(ScalarsConverterFactory.create()) + .addConverterFactory(GsonConverterFactory.create()) } } From aa7879166514ffde3566c12878b5ac036e22e4f5 Mon Sep 17 00:00:00 2001 From: WallE Date: Sat, 18 Mar 2023 02:32:08 +0530 Subject: [PATCH 14/50] feature: Integrate Core KTX and use the extension method to handle back button for drawer layout --- app/build.gradle | 1 + .../nagpal/shivam/vtucslab/activities/MainActivity.kt | 9 +++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 899a0bc..3b93b5f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -33,6 +33,7 @@ dependencies { implementation platform('com.google.firebase:firebase-bom:31.2.3') implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'com.google.android.material:material:1.8.0' diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt index 7043eda..2b150cc 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt @@ -6,7 +6,7 @@ import android.os.Looper import android.view.MenuItem import android.view.View import android.widget.ImageButton -import androidx.activity.OnBackPressedCallback +import androidx.activity.addCallback import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.AppCompatActivity import androidx.core.view.GravityCompat @@ -49,12 +49,9 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte binding.drawerLayout.addDrawerListener(actionBarDrawerToggle) actionBarDrawerToggle.syncState() - val drawerBackPressedCallback = object : OnBackPressedCallback(false) { - override fun handleOnBackPressed() { - closeNavigationDrawer() - } + val drawerBackPressedCallback = onBackPressedDispatcher.addCallback(this, false) { + closeNavigationDrawer() } - onBackPressedDispatcher.addCallback(this, drawerBackPressedCallback) binding.drawerLayout.addDrawerListener(object : DrawerListener { override fun onDrawerSlide(drawerView: View, slideOffset: Float) { } From b85d741c7934a908299fec188d997349c946afe3 Mon Sep 17 00:00:00 2001 From: WallE Date: Sat, 18 Mar 2023 03:59:22 +0530 Subject: [PATCH 15/50] feature: Integrate RepositoryViewModel and Retrofit to fetch the repositories --- .../screens/repository/RepositoryFragment.kt | 66 ++++++++++++++++++- .../screens/repository/RepositoryViewModel.kt | 45 +++++++++++++ .../vtucslab/ui/state/NetworkRequestState.kt | 9 +++ .../shivam/vtucslab/utilities/Constants.kt | 2 + .../shivam/vtucslab/utilities/Stages.kt | 7 ++ .../main/res/layout/fragment_repository.xml | 33 ++++++++-- 6 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/ui/state/NetworkRequestState.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Stages.kt diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt index 82931b9..a9ad8a7 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt @@ -6,19 +6,84 @@ import android.os.Bundle import android.view.* import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.recyclerview.widget.LinearLayoutManager import com.nagpal.shivam.vtucslab.R +import com.nagpal.shivam.vtucslab.adapters.NavigationAdapter import com.nagpal.shivam.vtucslab.databinding.FragmentRepositoryBinding import com.nagpal.shivam.vtucslab.utilities.Constants +import com.nagpal.shivam.vtucslab.utilities.Stages +import kotlinx.coroutines.launch class RepositoryFragment : Fragment() { private var _binding: FragmentRepositoryBinding? = null + private lateinit var navigationAdapter: NavigationAdapter private val binding get() = _binding!! + private val viewModel: RepositoryViewModel by viewModels() + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentRepositoryBinding.inflate(inflater, container, false) + setupMenuProvider() + setupViews() + setupRepositoryAdapter() + + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.uiState.collect { + binding.progressBar.visibility = View.GONE + binding.emptyTextView.visibility = View.GONE + when (it.stage) { + Stages.LOADING -> { + binding.progressBar.visibility = View.VISIBLE + } + Stages.SUCCEEDED -> { + if (it.labResponse!!.isValid) { + navigationAdapter.clear() + navigationAdapter.addAll(it.labResponse.laboratories) + } else { + showErrorMessage(it.labResponse.invalidationMessage) + } + } + Stages.FAILED -> { + showErrorMessage(getString(R.string.error_occurred)) + } + } + } + } + } + return binding.root + } + + private fun showErrorMessage(message: String) { + binding.emptyTextView.visibility = View.VISIBLE + binding.emptyTextView.text = message + } + + private fun setupViews() { + binding.repositoryRecyclerView.layoutManager = + LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) + binding.repositoryRecyclerView.setHasFixedSize(true) + } + + override fun onResume() { + super.onResume() + viewModel.loadRepositories() + } + + private fun setupRepositoryAdapter() { + navigationAdapter = NavigationAdapter(requireContext(), ArrayList()) + binding.repositoryRecyclerView.adapter = navigationAdapter + } + + private fun setupMenuProvider() { requireActivity().addMenuProvider(object : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { menuInflater.inflate(R.menu.menu_main_activity, menu) @@ -36,7 +101,6 @@ class RepositoryFragment : Fragment() { } } }) - return binding.root } override fun onDestroyView() { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt new file mode 100644 index 0000000..4178b66 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -0,0 +1,45 @@ +package com.nagpal.shivam.vtucslab.screens.repository + +import androidx.lifecycle.ViewModel +import com.nagpal.shivam.vtucslab.models.LabResponse +import com.nagpal.shivam.vtucslab.services.VtuCsLabService +import com.nagpal.shivam.vtucslab.ui.state.LabResponseState +import com.nagpal.shivam.vtucslab.utilities.Constants +import com.nagpal.shivam.vtucslab.utilities.Stages +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + + +class RepositoryViewModel : ViewModel() { + private val initialState = LabResponseState(Stages.LOADING, null, null) + private val _uiState = + MutableStateFlow(initialState) + val uiState: StateFlow = _uiState.asStateFlow() + + fun loadRepositories() { + if (_uiState.value.stage == Stages.SUCCEEDED) { + return + } + _uiState.update { initialState } + VtuCsLabService.instance.getLabResponse(Constants.INDEX_REPOSITORY_URL) + .enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + _uiState.update { + LabResponseState(Stages.SUCCEEDED, response.body(), null) + } + } + + override fun onFailure(call: Call, t: Throwable) { + _uiState.update { + LabResponseState(Stages.FAILED, null, null) + } + } + }) + } + +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/ui/state/NetworkRequestState.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/ui/state/NetworkRequestState.kt new file mode 100644 index 0000000..0a692b4 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/ui/state/NetworkRequestState.kt @@ -0,0 +1,9 @@ +package com.nagpal.shivam.vtucslab.ui.state + +import com.nagpal.shivam.vtucslab.models.LabResponse + +data class LabResponseState( + val stage: String, + val labResponse: LabResponse?, + val message: String?, +) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt index e1ca2cf..38e2ab8 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt @@ -6,4 +6,6 @@ object Constants { const val GITHUB_RAW_BASE_URL = "https://raw.githubusercontent.com" const val PRIVACY_POLICY_LINK = "https://github.com/ShivamNagpal/Privacy_Policies/blob/master/VTU_CS_LAB_MANUAL.md" + const val INDEX_REPOSITORY_URL = + "https://raw.githubusercontent.com/vtucs/Index_v3/master/Index_v3.json" } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Stages.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Stages.kt new file mode 100644 index 0000000..29d2b07 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Stages.kt @@ -0,0 +1,7 @@ +package com.nagpal.shivam.vtucslab.utilities + +object Stages { + const val LOADING = "LOADING" + const val SUCCEEDED = "SUCCEEDED" + const val FAILED = "FAILED" +} diff --git a/app/src/main/res/layout/fragment_repository.xml b/app/src/main/res/layout/fragment_repository.xml index 4e6c510..7003205 100644 --- a/app/src/main/res/layout/fragment_repository.xml +++ b/app/src/main/res/layout/fragment_repository.xml @@ -1,13 +1,38 @@ - - + android:clipToPadding="false" + android:paddingTop="6dp" + android:paddingBottom="6dp" + tools:listitem="@layout/layout_card_repository" /> + + + + - + From f507a5c8fd1c734938c133aa58395413172aefb4 Mon Sep 17 00:00:00 2001 From: WallE Date: Sat, 18 Mar 2023 05:07:06 +0530 Subject: [PATCH 16/50] feature: Check for Internet Connectivity before making API call --- app/src/main/AndroidManifest.xml | 4 +- .../shivam/vtucslab/VTUCSLabApplication.kt | 5 +++ .../screens/repository/RepositoryFragment.kt | 12 ++++-- .../screens/repository/RepositoryViewModel.kt | 15 ++++++-- .../shivam/vtucslab/utilities/Constants.kt | 1 + .../shivam/vtucslab/utilities/NetworkUtils.kt | 37 +++++++++++++++++++ 6 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/utilities/NetworkUtils.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0d46171..05d1bd0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,7 +6,7 @@ diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt new file mode 100644 index 0000000..2c844c0 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt @@ -0,0 +1,5 @@ +package com.nagpal.shivam.vtucslab + +import androidx.multidex.MultiDexApplication + +class VTUCSLabApplication : MultiDexApplication() diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt index a9ad8a7..96470cc 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt @@ -6,8 +6,8 @@ import android.os.Bundle import android.view.* import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.LinearLayoutManager @@ -23,7 +23,7 @@ class RepositoryFragment : Fragment() { private lateinit var navigationAdapter: NavigationAdapter private val binding get() = _binding!! - private val viewModel: RepositoryViewModel by viewModels() + private lateinit var viewModel: RepositoryViewModel override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -34,6 +34,8 @@ class RepositoryFragment : Fragment() { setupViews() setupRepositoryAdapter() + viewModel = ViewModelProvider(this)[RepositoryViewModel::class.java] + viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { @@ -53,7 +55,11 @@ class RepositoryFragment : Fragment() { } } Stages.FAILED -> { - showErrorMessage(getString(R.string.error_occurred)) + if (it.message == Constants.NO_ACTIVE_NETWORK) { + showErrorMessage(getString(R.string.no_internet_connection)) + } else { + showErrorMessage(getString(R.string.error_occurred)) + } } } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt index 4178b66..cf687b1 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -1,10 +1,13 @@ package com.nagpal.shivam.vtucslab.screens.repository -import androidx.lifecycle.ViewModel +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import com.nagpal.shivam.vtucslab.VTUCSLabApplication import com.nagpal.shivam.vtucslab.models.LabResponse import com.nagpal.shivam.vtucslab.services.VtuCsLabService import com.nagpal.shivam.vtucslab.ui.state.LabResponseState import com.nagpal.shivam.vtucslab.utilities.Constants +import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -15,16 +18,20 @@ import retrofit2.Callback import retrofit2.Response -class RepositoryViewModel : ViewModel() { +class RepositoryViewModel(app: Application) : AndroidViewModel(app) { private val initialState = LabResponseState(Stages.LOADING, null, null) - private val _uiState = - MutableStateFlow(initialState) + private val _uiState = MutableStateFlow(initialState) + private val application = getApplication() val uiState: StateFlow = _uiState.asStateFlow() fun loadRepositories() { if (_uiState.value.stage == Stages.SUCCEEDED) { return } + if (!NetworkUtils.isNetworkConnected(application)) { + _uiState.update { LabResponseState(Stages.FAILED, null, Constants.NO_ACTIVE_NETWORK) } + return + } _uiState.update { initialState } VtuCsLabService.instance.getLabResponse(Constants.INDEX_REPOSITORY_URL) .enqueue(object : Callback { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt index 38e2ab8..b4e8c43 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt @@ -8,4 +8,5 @@ object Constants { "https://github.com/ShivamNagpal/Privacy_Policies/blob/master/VTU_CS_LAB_MANUAL.md" const val INDEX_REPOSITORY_URL = "https://raw.githubusercontent.com/vtucs/Index_v3/master/Index_v3.json" + const val NO_ACTIVE_NETWORK = "NO_ACTIVE_NETWORK" } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/NetworkUtils.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/NetworkUtils.kt new file mode 100644 index 0000000..3a8f086 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/NetworkUtils.kt @@ -0,0 +1,37 @@ +package com.nagpal.shivam.vtucslab.utilities + +import android.content.Context +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build + +object NetworkUtils { + fun isNetworkConnected(context: Context): Boolean { + val connectivityManager = + context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + val networkCapabilities = connectivityManager.activeNetwork ?: return false + val activeNetwork = + connectivityManager.getNetworkCapabilities(networkCapabilities) ?: return false + return when { + activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true + activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true + activeNetwork.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true + else -> false + } + } else { //TODO: Remove this code post upgrading the minSdkVersion to 23 (https://stackoverflow.com/a/61152364) + connectivityManager.run { + connectivityManager.activeNetworkInfo?.run { + return when (type) { + ConnectivityManager.TYPE_WIFI -> true + ConnectivityManager.TYPE_MOBILE -> true + ConnectivityManager.TYPE_ETHERNET -> true + else -> false + } + + } + } + } + return false + } +} From 7b634b3d107c8bb778f420294d2857dd30695ba9 Mon Sep 17 00:00:00 2001 From: WallE Date: Sat, 18 Mar 2023 18:05:55 +0530 Subject: [PATCH 17/50] feature: Create Details Activity and Pass the safeArgs on navigation --- app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 4 ++++ .../vtucslab/activities/DetailsActivity.kt | 23 +++++++++++++++++++ .../screens/repository/RepositoryFragment.kt | 10 ++++++++ .../screens/repository/RepositoryViewModel.kt | 22 ++++++++++++++---- .../vtucslab/ui/state/NetworkRequestState.kt | 1 + .../vtucslab/utilities/StaticMethods.kt | 5 ++++ app/src/main/res/layout/activity_details.xml | 8 +++++++ .../res/navigation/main_navigation_graph.xml | 21 ++++++++++++++++- build.gradle | 2 ++ 10 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt create mode 100644 app/src/main/res/layout/activity_details.xml diff --git a/app/build.gradle b/app/build.gradle index 3b93b5f..a066821 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'com.android.application' apply plugin: 'com.google.firebase.crashlytics' +apply plugin: 'androidx.navigation.safeargs' android { compileSdkVersion 33 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 05d1bd0..61fef25 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,6 +26,10 @@ + + diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt new file mode 100644 index 0000000..640bfee --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt @@ -0,0 +1,23 @@ +package com.nagpal.shivam.vtucslab.activities + +import android.os.Bundle +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.navigation.navArgs +import com.nagpal.shivam.vtucslab.R + +class DetailsActivity : AppCompatActivity() { + private val detailsActivityArgs by navArgs() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_details) + + Toast.makeText( + this, + "${detailsActivityArgs.title}\n${detailsActivityArgs.fileName}\n${detailsActivityArgs.baseUrl}", + Toast.LENGTH_LONG + ).show() + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt index 96470cc..c46b0cf 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt @@ -10,6 +10,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.adapters.NavigationAdapter @@ -86,6 +87,15 @@ class RepositoryFragment : Fragment() { private fun setupRepositoryAdapter() { navigationAdapter = NavigationAdapter(requireContext(), ArrayList()) + navigationAdapter.setNavigationAdapterItemClickHandler { laboratory, _ -> + val actionRepositoryFragmentToDetailsActivity = + RepositoryFragmentDirections.actionRepositoryFragmentToDetailsActivity( + viewModel.uiState.value.baseUrl!!, + laboratory.fileName, + laboratory.title + ) + findNavController().navigate(actionRepositoryFragmentToDetailsActivity) + } binding.repositoryRecyclerView.adapter = navigationAdapter } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt index cf687b1..e50b50f 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -9,6 +9,7 @@ import com.nagpal.shivam.vtucslab.ui.state.LabResponseState import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages +import com.nagpal.shivam.vtucslab.utilities.StaticMethods import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -19,7 +20,7 @@ import retrofit2.Response class RepositoryViewModel(app: Application) : AndroidViewModel(app) { - private val initialState = LabResponseState(Stages.LOADING, null, null) + private val initialState = LabResponseState(Stages.LOADING, null, null, null) private val _uiState = MutableStateFlow(initialState) private val application = getApplication() val uiState: StateFlow = _uiState.asStateFlow() @@ -29,7 +30,14 @@ class RepositoryViewModel(app: Application) : AndroidViewModel(app) { return } if (!NetworkUtils.isNetworkConnected(application)) { - _uiState.update { LabResponseState(Stages.FAILED, null, Constants.NO_ACTIVE_NETWORK) } + _uiState.update { + LabResponseState( + Stages.FAILED, + null, + Constants.NO_ACTIVE_NETWORK, + null + ) + } return } _uiState.update { initialState } @@ -37,13 +45,19 @@ class RepositoryViewModel(app: Application) : AndroidViewModel(app) { .enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { _uiState.update { - LabResponseState(Stages.SUCCEEDED, response.body(), null) + val labResponse = response.body()!! + LabResponseState( + Stages.SUCCEEDED, + labResponse, + null, + StaticMethods.getBaseURL(labResponse) + ) } } override fun onFailure(call: Call, t: Throwable) { _uiState.update { - LabResponseState(Stages.FAILED, null, null) + LabResponseState(Stages.FAILED, null, null, null) } } }) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/ui/state/NetworkRequestState.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/ui/state/NetworkRequestState.kt index 0a692b4..a8eb305 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/ui/state/NetworkRequestState.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/ui/state/NetworkRequestState.kt @@ -6,4 +6,5 @@ data class LabResponseState( val stage: String, val labResponse: LabResponse?, val message: String?, + val baseUrl: String? ) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt index 0ce4ac9..6c7c0fc 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt @@ -1,5 +1,6 @@ package com.nagpal.shivam.vtucslab.utilities +import com.nagpal.shivam.vtucslab.models.LabResponse import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.scalars.ScalarsConverterFactory @@ -15,4 +16,8 @@ object StaticMethods { .addConverterFactory(ScalarsConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) } + + fun getBaseURL(labResponse: LabResponse): String { + return "${labResponse.github_raw_content}/${labResponse.organization}/${labResponse.repository}/${labResponse.branch}" + } } diff --git a/app/src/main/res/layout/activity_details.xml b/app/src/main/res/layout/activity_details.xml new file mode 100644 index 0000000..0012666 --- /dev/null +++ b/app/src/main/res/layout/activity_details.xml @@ -0,0 +1,8 @@ + + + + diff --git a/app/src/main/res/navigation/main_navigation_graph.xml b/app/src/main/res/navigation/main_navigation_graph.xml index 243f508..725a627 100644 --- a/app/src/main/res/navigation/main_navigation_graph.xml +++ b/app/src/main/res/navigation/main_navigation_graph.xml @@ -8,5 +8,24 @@ android:id="@+id/repositoryFragment" android:name="com.nagpal.shivam.vtucslab.screens.repository.RepositoryFragment" android:label="fragment_repository" - tools:layout="@layout/fragment_repository" /> + tools:layout="@layout/fragment_repository"> + + + + + + + diff --git a/build.gradle b/build.gradle index 1c5b393..c9ccf43 100644 --- a/build.gradle +++ b/build.gradle @@ -7,10 +7,12 @@ buildscript { mavenCentral() } dependencies { + def navigationVersion = "2.5.3" classpath 'com.android.tools.build:gradle:7.4.2' classpath 'com.google.gms:google-services:4.3.15' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.4' classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0' + classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } From fd345cccf79fb7dea81ca8edf6fa719153a555a3 Mon Sep 17 00:00:00 2001 From: WallE Date: Sun, 19 Mar 2023 02:18:30 +0530 Subject: [PATCH 18/50] feature: Pass data from Activity to the NavHostFragment --- .../vtucslab/activities/DetailsActivity.kt | 26 +++++++++----- .../screens/programs/ProgramFragment.kt | 34 +++++++++++++++++++ app/src/main/res/layout/activity_details.xml | 12 +++++++ app/src/main/res/layout/fragment_program.xml | 12 +++++++ .../navigation/details_navigation_graph.xml | 23 +++++++++++++ 5 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt create mode 100644 app/src/main/res/layout/fragment_program.xml create mode 100644 app/src/main/res/navigation/details_navigation_graph.xml diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt index 640bfee..b23229b 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt @@ -1,23 +1,33 @@ package com.nagpal.shivam.vtucslab.activities import android.os.Bundle -import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.navigation.fragment.NavHostFragment import androidx.navigation.navArgs import com.nagpal.shivam.vtucslab.R +import com.nagpal.shivam.vtucslab.databinding.ActivityDetailsBinding +import com.nagpal.shivam.vtucslab.screens.programs.ProgramFragmentArgs class DetailsActivity : AppCompatActivity() { + private lateinit var binding: ActivityDetailsBinding private val detailsActivityArgs by navArgs() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_details) + binding = ActivityDetailsBinding.inflate(layoutInflater) + setContentView(binding.root) - Toast.makeText( - this, - "${detailsActivityArgs.title}\n${detailsActivityArgs.fileName}\n${detailsActivityArgs.baseUrl}", - Toast.LENGTH_LONG - ).show() + val navHostFragment = + supportFragmentManager.findFragmentById(R.id.navigation_host_fragment) as NavHostFragment + val programFragmentArgs = ProgramFragmentArgs.Builder( + detailsActivityArgs.baseUrl, + detailsActivityArgs.fileName, + detailsActivityArgs.title + ).build() + navHostFragment.navController.setGraph( + R.navigation.details_navigation_graph, + programFragmentArgs.toBundle() + ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt new file mode 100644 index 0000000..88238b7 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt @@ -0,0 +1,34 @@ +package com.nagpal.shivam.vtucslab.screens.programs + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.navArgs +import com.nagpal.shivam.vtucslab.databinding.FragmentProgramBinding + + +class ProgramFragment : Fragment() { + private var _binding: FragmentProgramBinding? = null + private val binding get() = _binding!! + + private val programFragmentArgs by navArgs() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentProgramBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + Toast.makeText( + requireContext(), + "${programFragmentArgs.title}\n${programFragmentArgs.fileName}\n${programFragmentArgs.baseUrl}", + Toast.LENGTH_LONG + ).show() + } +} diff --git a/app/src/main/res/layout/activity_details.xml b/app/src/main/res/layout/activity_details.xml index 0012666..c347d31 100644 --- a/app/src/main/res/layout/activity_details.xml +++ b/app/src/main/res/layout/activity_details.xml @@ -1,8 +1,20 @@ + + diff --git a/app/src/main/res/layout/fragment_program.xml b/app/src/main/res/layout/fragment_program.xml new file mode 100644 index 0000000..710e55b --- /dev/null +++ b/app/src/main/res/layout/fragment_program.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/app/src/main/res/navigation/details_navigation_graph.xml b/app/src/main/res/navigation/details_navigation_graph.xml new file mode 100644 index 0000000..dbe4e05 --- /dev/null +++ b/app/src/main/res/navigation/details_navigation_graph.xml @@ -0,0 +1,23 @@ + + + + + + + + + From 2267565f3ca48ff96203a1563ec03ce4bc93cd52 Mon Sep 17 00:00:00 2001 From: WallE Date: Sun, 19 Mar 2023 02:54:46 +0530 Subject: [PATCH 19/50] feature: Implement Program Fragment --- .../vtucslab/activities/DetailsActivity.kt | 11 +++ .../vtucslab/screens/LabResponseViewModel.kt | 66 +++++++++++++++ .../screens/programs/ProgramFragment.kt | 81 +++++++++++++++++-- .../screens/programs/ProgramViewModel.kt | 6 ++ .../screens/repository/RepositoryFragment.kt | 2 +- .../screens/repository/RepositoryViewModel.kt | 64 +-------------- app/src/main/res/layout/fragment_program.xml | 35 ++++++-- 7 files changed, 190 insertions(+), 75 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/LabResponseViewModel.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt index b23229b..3763f51 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt @@ -1,6 +1,7 @@ package com.nagpal.shivam.vtucslab.activities import android.os.Bundle +import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity import androidx.navigation.fragment.NavHostFragment import androidx.navigation.navArgs @@ -17,6 +18,8 @@ class DetailsActivity : AppCompatActivity() { binding = ActivityDetailsBinding.inflate(layoutInflater) setContentView(binding.root) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + val navHostFragment = supportFragmentManager.findFragmentById(R.id.navigation_host_fragment) as NavHostFragment @@ -30,4 +33,12 @@ class DetailsActivity : AppCompatActivity() { programFragmentArgs.toBundle() ) } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + this.onBackPressedDispatcher.onBackPressed() + return true + } + return super.onOptionsItemSelected(item) + } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/LabResponseViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/LabResponseViewModel.kt new file mode 100644 index 0000000..740bc35 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/LabResponseViewModel.kt @@ -0,0 +1,66 @@ +package com.nagpal.shivam.vtucslab.screens + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import com.nagpal.shivam.vtucslab.VTUCSLabApplication +import com.nagpal.shivam.vtucslab.models.LabResponse +import com.nagpal.shivam.vtucslab.services.VtuCsLabService +import com.nagpal.shivam.vtucslab.ui.state.LabResponseState +import com.nagpal.shivam.vtucslab.utilities.Constants +import com.nagpal.shivam.vtucslab.utilities.NetworkUtils +import com.nagpal.shivam.vtucslab.utilities.Stages +import com.nagpal.shivam.vtucslab.utilities.StaticMethods +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + + +open class LabResponseViewModel(app: Application) : AndroidViewModel(app) { + private val initialState = LabResponseState(Stages.LOADING, null, null, null) + private val _uiState = MutableStateFlow(initialState) + private val application = this.getApplication() + val uiState: StateFlow = _uiState.asStateFlow() + + fun loadContent(url: String) { + if (_uiState.value.stage == Stages.SUCCEEDED) { + return + } + if (!NetworkUtils.isNetworkConnected(application)) { + _uiState.update { + LabResponseState( + Stages.FAILED, + null, + Constants.NO_ACTIVE_NETWORK, + null + ) + } + return + } + _uiState.update { initialState } + VtuCsLabService.instance.getLabResponse(url) + .enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + _uiState.update { + val labResponse = response.body()!! + LabResponseState( + Stages.SUCCEEDED, + labResponse, + null, + StaticMethods.getBaseURL(labResponse) + ) + } + } + + override fun onFailure(call: Call, t: Throwable) { + _uiState.update { + LabResponseState(Stages.FAILED, null, null, null) + } + } + }) + } + +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt index 88238b7..595b795 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt @@ -4,16 +4,28 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Toast import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.navArgs +import androidx.recyclerview.widget.LinearLayoutManager +import com.nagpal.shivam.vtucslab.R +import com.nagpal.shivam.vtucslab.adapters.ContentAdapter import com.nagpal.shivam.vtucslab.databinding.FragmentProgramBinding +import com.nagpal.shivam.vtucslab.utilities.Constants +import com.nagpal.shivam.vtucslab.utilities.Stages +import kotlinx.coroutines.launch class ProgramFragment : Fragment() { private var _binding: FragmentProgramBinding? = null private val binding get() = _binding!! + private lateinit var viewModel: ProgramViewModel + private lateinit var contentAdapter: ContentAdapter + private val programFragmentArgs by navArgs() override fun onCreateView( @@ -21,14 +33,69 @@ class ProgramFragment : Fragment() { savedInstanceState: Bundle? ): View { _binding = FragmentProgramBinding.inflate(inflater, container, false) + setupViews() + setupRepositoryAdapter() + + viewModel = ViewModelProvider(this)[ProgramViewModel::class.java] + + requireActivity().title = programFragmentArgs.title + + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.uiState.collect { + binding.progressBar.visibility = View.GONE + binding.emptyTextView.visibility = View.GONE + when (it.stage) { + Stages.LOADING -> { + binding.progressBar.visibility = View.VISIBLE + } + Stages.SUCCEEDED -> { + if (it.labResponse!!.isValid) { + contentAdapter.clear() + contentAdapter.addAll(it.labResponse.labExperiments) + } else { + showErrorMessage(it.labResponse.invalidationMessage) + } + } + Stages.FAILED -> { + if (it.message == Constants.NO_ACTIVE_NETWORK) { + showErrorMessage(getString(R.string.no_internet_connection)) + } else { + showErrorMessage(getString(R.string.error_occurred)) + } + } + } + } + } + } + return binding.root } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - Toast.makeText( - requireContext(), - "${programFragmentArgs.title}\n${programFragmentArgs.fileName}\n${programFragmentArgs.baseUrl}", - Toast.LENGTH_LONG - ).show() + private fun showErrorMessage(message: String) { + binding.emptyTextView.visibility = View.VISIBLE + binding.emptyTextView.text = message + } + + private fun setupRepositoryAdapter() { + contentAdapter = ContentAdapter(requireContext(), ArrayList()) + binding.recyclerView.adapter = contentAdapter + } + + private fun setupViews() { + binding.recyclerView.layoutManager = + LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) + binding.recyclerView.setHasFixedSize(true) + } + + override fun onResume() { + super.onResume() + viewModel.loadContent("${programFragmentArgs.baseUrl}/${programFragmentArgs.fileName}") + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt new file mode 100644 index 0000000..b83303a --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt @@ -0,0 +1,6 @@ +package com.nagpal.shivam.vtucslab.screens.programs + +import android.app.Application +import com.nagpal.shivam.vtucslab.screens.LabResponseViewModel + +class ProgramViewModel(app: Application) : LabResponseViewModel(app) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt index c46b0cf..46b9aa4 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt @@ -82,7 +82,7 @@ class RepositoryFragment : Fragment() { override fun onResume() { super.onResume() - viewModel.loadRepositories() + viewModel.loadContent(Constants.INDEX_REPOSITORY_URL) } private fun setupRepositoryAdapter() { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt index e50b50f..f71b6df 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -1,66 +1,6 @@ package com.nagpal.shivam.vtucslab.screens.repository import android.app.Application -import androidx.lifecycle.AndroidViewModel -import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.models.LabResponse -import com.nagpal.shivam.vtucslab.services.VtuCsLabService -import com.nagpal.shivam.vtucslab.ui.state.LabResponseState -import com.nagpal.shivam.vtucslab.utilities.Constants -import com.nagpal.shivam.vtucslab.utilities.NetworkUtils -import com.nagpal.shivam.vtucslab.utilities.Stages -import com.nagpal.shivam.vtucslab.utilities.StaticMethods -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response +import com.nagpal.shivam.vtucslab.screens.LabResponseViewModel - -class RepositoryViewModel(app: Application) : AndroidViewModel(app) { - private val initialState = LabResponseState(Stages.LOADING, null, null, null) - private val _uiState = MutableStateFlow(initialState) - private val application = getApplication() - val uiState: StateFlow = _uiState.asStateFlow() - - fun loadRepositories() { - if (_uiState.value.stage == Stages.SUCCEEDED) { - return - } - if (!NetworkUtils.isNetworkConnected(application)) { - _uiState.update { - LabResponseState( - Stages.FAILED, - null, - Constants.NO_ACTIVE_NETWORK, - null - ) - } - return - } - _uiState.update { initialState } - VtuCsLabService.instance.getLabResponse(Constants.INDEX_REPOSITORY_URL) - .enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - _uiState.update { - val labResponse = response.body()!! - LabResponseState( - Stages.SUCCEEDED, - labResponse, - null, - StaticMethods.getBaseURL(labResponse) - ) - } - } - - override fun onFailure(call: Call, t: Throwable) { - _uiState.update { - LabResponseState(Stages.FAILED, null, null, null) - } - } - }) - } - -} +class RepositoryViewModel(app: Application) : LabResponseViewModel(app) diff --git a/app/src/main/res/layout/fragment_program.xml b/app/src/main/res/layout/fragment_program.xml index 710e55b..2df3c19 100644 --- a/app/src/main/res/layout/fragment_program.xml +++ b/app/src/main/res/layout/fragment_program.xml @@ -1,12 +1,37 @@ - + tools:context=".activities.ProgramActivity"> - + android:layout_height="match_parent" + android:clipToPadding="false" + android:paddingTop="6dp" + android:paddingBottom="6dp" + tools:listitem="@layout/layout_card_se_ssp_sf" /> + + + + - + From 38740ee56a23c55264e4e3f8c0085c82d8e67a6d Mon Sep 17 00:00:00 2001 From: WallE Date: Sun, 19 Mar 2023 21:09:23 +0530 Subject: [PATCH 20/50] feature: Implement Program click handler, create ContentActivity and wireframe safeArgs --- app/src/main/AndroidManifest.xml | 4 +- .../vtucslab/activities/ContentActivity.kt | 45 +++++++++++++++++++ .../screens/display/DisplayFragment.kt | 35 +++++++++++++++ .../screens/programs/ProgramFragment.kt | 10 +++++ app/src/main/res/layout/activity_content.xml | 20 +++++++++ app/src/main/res/layout/fragment_display.xml | 13 ++++++ .../navigation/content_navigation_graph.xml | 23 ++++++++++ .../navigation/details_navigation_graph.xml | 18 ++++++++ 8 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/activities/ContentActivity.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt create mode 100644 app/src/main/res/layout/activity_content.xml create mode 100644 app/src/main/res/layout/fragment_display.xml create mode 100644 app/src/main/res/navigation/content_navigation_graph.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 61fef25..70ee8f7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -25,10 +25,12 @@ - + () + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityContentBinding.inflate(layoutInflater) + setContentView(binding.root) + + supportActionBar?.setDisplayHomeAsUpEnabled(true) + + val navHostFragment = + supportFragmentManager.findFragmentById(R.id.navigation_host_fragment) as NavHostFragment + + val displayFragmentArgs = DisplayFragmentArgs.Builder( + contentActivityArgs.baseUrl, + contentActivityArgs.fileName, + contentActivityArgs.title + ).build() + + navHostFragment.navController.setGraph( + R.navigation.content_navigation_graph, + displayFragmentArgs.toBundle() + ) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + this.onBackPressedDispatcher.onBackPressed() + return true + } + return super.onOptionsItemSelected(item) + } +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt new file mode 100644 index 0000000..a59326e --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt @@ -0,0 +1,35 @@ +package com.nagpal.shivam.vtucslab.screens.display + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.navArgs +import com.nagpal.shivam.vtucslab.databinding.FragmentDisplayBinding + + +class DisplayFragment : Fragment() { + + private var _binding: FragmentDisplayBinding? = null + private val binding get() = _binding!! + private val displayFragmentArgs by navArgs() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentDisplayBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + Toast.makeText( + requireContext(), + "${displayFragmentArgs.baseUrl}/${displayFragmentArgs.fileName}", + Toast.LENGTH_LONG + ).show() + } +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt index 595b795..c5e30fb 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt @@ -9,6 +9,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager import com.nagpal.shivam.vtucslab.R @@ -80,6 +81,15 @@ class ProgramFragment : Fragment() { private fun setupRepositoryAdapter() { contentAdapter = ContentAdapter(requireContext(), ArrayList()) + contentAdapter.setItemClickHandler { + val actionProgramFragmentToContentActivity = + ProgramFragmentDirections.actionProgramFragmentToContentActivity( + viewModel.uiState.value.baseUrl!!, + it.fileName, + it.fileName + ) + findNavController().navigate(actionProgramFragmentToContentActivity) + } binding.recyclerView.adapter = contentAdapter } diff --git a/app/src/main/res/layout/activity_content.xml b/app/src/main/res/layout/activity_content.xml new file mode 100644 index 0000000..c347d31 --- /dev/null +++ b/app/src/main/res/layout/activity_content.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/app/src/main/res/layout/fragment_display.xml b/app/src/main/res/layout/fragment_display.xml new file mode 100644 index 0000000..08be93b --- /dev/null +++ b/app/src/main/res/layout/fragment_display.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/app/src/main/res/navigation/content_navigation_graph.xml b/app/src/main/res/navigation/content_navigation_graph.xml new file mode 100644 index 0000000..a805a30 --- /dev/null +++ b/app/src/main/res/navigation/content_navigation_graph.xml @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/app/src/main/res/navigation/details_navigation_graph.xml b/app/src/main/res/navigation/details_navigation_graph.xml index dbe4e05..77c0e3a 100644 --- a/app/src/main/res/navigation/details_navigation_graph.xml +++ b/app/src/main/res/navigation/details_navigation_graph.xml @@ -19,5 +19,23 @@ + + + + + + From 08f7ddaaf60ad071ca2733ecb1fee7a00ca82ad3 Mon Sep 17 00:00:00 2001 From: WallE Date: Mon, 20 Mar 2023 20:19:44 +0530 Subject: [PATCH 21/50] Implement the Display Fragment and ViewModel --- .../screens/display/DisplayFragment.kt | 119 ++++++++++++++++-- .../screens/display/DisplayViewModel.kt | 72 +++++++++++ .../shivam/vtucslab/utilities/Constants.kt | 1 + app/src/main/res/layout/fragment_display.xml | 56 ++++++++- app/src/main/res/values/strings.xml | 1 + 5 files changed, 233 insertions(+), 16 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt index a59326e..51037f1 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt @@ -1,13 +1,25 @@ package com.nagpal.shivam.vtucslab.screens.display +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.os.Handler +import android.os.Looper +import android.view.* import android.widget.Toast +import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.navArgs +import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.databinding.FragmentDisplayBinding +import com.nagpal.shivam.vtucslab.utilities.Constants +import com.nagpal.shivam.vtucslab.utilities.Stages +import kotlinx.coroutines.launch class DisplayFragment : Fragment() { @@ -15,21 +27,108 @@ class DisplayFragment : Fragment() { private var _binding: FragmentDisplayBinding? = null private val binding get() = _binding!! private val displayFragmentArgs by navArgs() + private lateinit var viewModel: DisplayViewModel override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentDisplayBinding.inflate(inflater, container, false) + setupMenuProvider() + + viewModel = ViewModelProvider(this)[DisplayViewModel::class.java] + + requireActivity().title = displayFragmentArgs.title + + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.uiState.collect { + binding.progressBar.visibility = View.GONE + binding.emptyTextView.visibility = View.GONE + when (it.stage) { + Stages.LOADING -> { + binding.progressBar.visibility = View.VISIBLE + } + Stages.SUCCEEDED -> { + binding.displayTextView.text = it.response + requireActivity().invalidateOptionsMenu() + Handler(Looper.getMainLooper()).postDelayed({ + binding.horizontalScroll.scrollX = viewModel.scrollX + binding.verticalScroll.scrollY = viewModel.scrollY + }, 500) + } + Stages.FAILED -> { + if (it.message == Constants.NO_ACTIVE_NETWORK) { + showErrorMessage(getString(R.string.no_internet_connection)) + } else { + showErrorMessage(getString(R.string.error_occurred)) + } + } + } + } + } + } + return binding.root } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - Toast.makeText( - requireContext(), - "${displayFragmentArgs.baseUrl}/${displayFragmentArgs.fileName}", - Toast.LENGTH_LONG - ).show() + private fun showErrorMessage(message: String) { + binding.emptyTextView.visibility = View.VISIBLE + binding.emptyTextView.text = message + } + + private fun setupMenuProvider() { + requireActivity().addMenuProvider(object : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + menuInflater.inflate(R.menu.menu_display_activity, menu) + if (viewModel.uiState.value.stage == Stages.SUCCEEDED) { + menu.findItem(R.id.menu_item_copy_display_activity).isEnabled = true + } + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + return when (menuItem.itemId) { + R.id.display_menu_item_refresh -> { + viewModel.resetState() + loadContent() + return true + } + R.id.menu_item_copy_display_activity -> { + + val clipboard = + requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clipData = ClipData( + ClipData.newPlainText( + Constants.LABEL_CODE, + viewModel.uiState.value.response + ) + ) + clipboard.setPrimaryClip(clipData) + Toast.makeText( + requireContext(), + getString(R.string.code_copied_to_clipboard), + Toast.LENGTH_SHORT + ).show() + return true + } + else -> false + } + } + }) + } + + override fun onResume() { + super.onResume() + loadContent() + } + + override fun onSaveInstanceState(outState: Bundle) { + viewModel.scrollX = binding.horizontalScroll.scrollX + viewModel.scrollY = binding.verticalScroll.scrollY + super.onSaveInstanceState(outState) + } + + private fun loadContent() { + viewModel.loadContent("${displayFragmentArgs.baseUrl}/${displayFragmentArgs.fileName}") } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt new file mode 100644 index 0000000..618a7e5 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt @@ -0,0 +1,72 @@ +package com.nagpal.shivam.vtucslab.screens.display + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import com.nagpal.shivam.vtucslab.VTUCSLabApplication +import com.nagpal.shivam.vtucslab.services.VtuCsLabService +import com.nagpal.shivam.vtucslab.utilities.Constants +import com.nagpal.shivam.vtucslab.utilities.NetworkUtils +import com.nagpal.shivam.vtucslab.utilities.Stages +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +data class DisplayResponseState( + val stage: String, + val response: String?, + val message: String?, +) + +class DisplayViewModel(app: Application) : AndroidViewModel(app) { + var scrollX = 0 + var scrollY = 0 + private val initialState = DisplayResponseState(Stages.LOADING, null, null) + + private val _uiState = MutableStateFlow(initialState) + val uiState = _uiState.asStateFlow() + + private val application = this.getApplication() + + fun loadContent(url: String) { + if (_uiState.value.stage == Stages.SUCCEEDED) { + return + } + if (!NetworkUtils.isNetworkConnected(application)) { + _uiState.update { + DisplayResponseState( + Stages.FAILED, + null, + Constants.NO_ACTIVE_NETWORK, + ) + } + return + } + _uiState.update { initialState } + VtuCsLabService.instance.fetchRawResponse(url) + .enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + _uiState.update { + val stringResponse = response.body()!!.replace("\t", "\t\t") + DisplayResponseState( + Stages.SUCCEEDED, + stringResponse, + null, + ) + } + } + + override fun onFailure(call: Call, t: Throwable) { + _uiState.update { + DisplayResponseState(Stages.FAILED, null, null) + } + } + }) + } + + fun resetState() { + _uiState.update { initialState } + } +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt index b4e8c43..6a5be96 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt @@ -9,4 +9,5 @@ object Constants { const val INDEX_REPOSITORY_URL = "https://raw.githubusercontent.com/vtucs/Index_v3/master/Index_v3.json" const val NO_ACTIVE_NETWORK = "NO_ACTIVE_NETWORK" + const val LABEL_CODE = "Code" } diff --git a/app/src/main/res/layout/fragment_display.xml b/app/src/main/res/layout/fragment_display.xml index 08be93b..1f2e179 100644 --- a/app/src/main/res/layout/fragment_display.xml +++ b/app/src/main/res/layout/fragment_display.xml @@ -1,13 +1,57 @@ - + android:clipChildren="false" + android:clipToPadding="false" + tools:context="com.nagpal.shivam.vtucslab.activities.DisplayActivity"> - - + android:layout_height="match_parent" + android:orientation="vertical" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + + + + + + + + - + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e095698..3fe0874 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -14,4 +14,5 @@ Repository Exit Privacy Policy + Code copied to Clipboard. From bba5a5187aa127125a0351fe47917bb48606d0d7 Mon Sep 17 00:00:00 2001 From: WallE Date: Tue, 21 Mar 2023 03:58:55 +0530 Subject: [PATCH 22/50] feature: Remove the custom Toolbar implementation and use the Default ActionBar with the Navigation Component --- app/src/main/AndroidManifest.xml | 2 +- .../vtucslab/activities/MainActivity.kt | 86 ++++++++++++------- app/src/main/res/layout/activity_main.xml | 20 +++-- app/src/main/res/layout/content_main.xml | 22 ----- app/src/main/res/layout/coordinator_main.xml | 28 ------ app/src/main/res/layout/header_main.xml | 37 -------- .../res/menu/menu_main_navigation_drawer.xml | 13 +++ .../res/navigation/main_navigation_graph.xml | 2 +- 8 files changed, 82 insertions(+), 128 deletions(-) delete mode 100644 app/src/main/res/layout/content_main.xml delete mode 100644 app/src/main/res/layout/coordinator_main.xml delete mode 100644 app/src/main/res/layout/header_main.xml create mode 100644 app/src/main/res/menu/menu_main_navigation_drawer.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 70ee8f7..1334f1e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,7 +19,7 @@ android:name=".activities.MainActivity" android:exported="true" android:label="@string/app_name_new" - android:theme="@style/AppTheme.NoActionBar"> + android:theme="@style/AppTheme"> diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt index 2b150cc..916ae17 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt @@ -5,44 +5,64 @@ import android.os.Handler import android.os.Looper import android.view.MenuItem import android.view.View -import android.widget.ImageButton import androidx.activity.addCallback import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.AppCompatActivity import androidx.core.view.GravityCompat -import androidx.drawerlayout.widget.DrawerLayout.DrawerListener -import com.google.android.material.navigation.NavigationView +import androidx.drawerlayout.widget.DrawerLayout +import androidx.navigation.NavController +import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.ui.* import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.databinding.ActivityMainBinding import kotlin.system.exitProcess -class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener { +class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding + private lateinit var navController: NavController + private lateinit var appBarConfiguration: AppBarConfiguration + private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) - setSupportActionBar(binding.coordinator.toolbar) - setupDrawerToggle() + val navHostFragment = + supportFragmentManager.findFragmentById(R.id.navigation_host_fragment) as NavHostFragment + navController = navHostFragment.navController + + setupActionBar() setupNavigationView() + setupDrawerToggle() } - private fun setupNavigationView() { - val navigationView = binding.navigationView - navigationView.setNavigationItemSelectedListener(this) - navigationView.menu.getItem(0).isChecked = true + private fun setupActionBar() { + appBarConfiguration = + AppBarConfiguration(setOf(R.id.repositoryFragment), binding.drawerLayout) + setupActionBarWithNavController(navController, appBarConfiguration) + } - val headerView = navigationView.getHeaderView(0) - val navigationDrawerBackButton = - headerView.findViewById(R.id.close_drawer) - navigationDrawerBackButton.setOnClickListener { closeNavigationDrawer() } + private fun setupNavigationView() { + binding.navigationView.setupWithNavController(navController) + binding.navigationView.setNavigationItemSelectedListener { + var flag = false + val itemId: Int = it.itemId + if (itemId == R.id.repositoryFragment) { + flag = true + } else if (itemId == R.id.menu_item_exit) { + exitApplication() + flag = true + } + if (flag) { + closeNavigationDrawer() + } + return@setNavigationItemSelectedListener flag + } } private fun setupDrawerToggle() { - val actionBarDrawerToggle = ActionBarDrawerToggle( + actionBarDrawerToggle = ActionBarDrawerToggle( this, binding.drawerLayout, - binding.coordinator.toolbar, R.string.drawer_open, R.string.drawer_close ) @@ -52,7 +72,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte val drawerBackPressedCallback = onBackPressedDispatcher.addCallback(this, false) { closeNavigationDrawer() } - binding.drawerLayout.addDrawerListener(object : DrawerListener { + binding.drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener { override fun onDrawerSlide(drawerView: View, slideOffset: Float) { } @@ -69,27 +89,29 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte }) } - override fun onNavigationItemSelected(item: MenuItem): Boolean { - var flag = false - val itemId: Int = item.itemId - if (itemId == R.id.menu_item_repository) { - flag = true - } else if (itemId == R.id.menu_item_exit) { - exitApplication() - flag = true - } - if (flag) { - closeNavigationDrawer() + override fun onSupportNavigateUp(): Boolean { + return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp() + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home && appBarConfiguration.topLevelDestinations.contains( + navController.currentDestination?.id + ) + ) { + actionBarDrawerToggle.syncState() + if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) { + binding.drawerLayout.closeDrawer(GravityCompat.START) + return true + } } - return flag + return item.onNavDestinationSelected(navController) || super.onOptionsItemSelected(item) } + private fun exitApplication() { finishAffinity() Handler(Looper.getMainLooper()).postDelayed({ - exitProcess( - 0 - ) + exitProcess(0) }, 1000) } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 8cd32dd..6bb1cb4 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -5,20 +5,26 @@ android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" tools:context="com.nagpal.shivam.vtucslab.activities.MainActivity" tools:openDrawer="start"> - + + - - + android:fitsSystemWindows="true" + app:menu="@menu/menu_main_navigation_drawer" /> diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml deleted file mode 100644 index 53bf179..0000000 --- a/app/src/main/res/layout/content_main.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/app/src/main/res/layout/coordinator_main.xml b/app/src/main/res/layout/coordinator_main.xml deleted file mode 100644 index b890068..0000000 --- a/app/src/main/res/layout/coordinator_main.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/header_main.xml b/app/src/main/res/layout/header_main.xml deleted file mode 100644 index a46cc54..0000000 --- a/app/src/main/res/layout/header_main.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/menu/menu_main_navigation_drawer.xml b/app/src/main/res/menu/menu_main_navigation_drawer.xml new file mode 100644 index 0000000..9e58fd0 --- /dev/null +++ b/app/src/main/res/menu/menu_main_navigation_drawer.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/app/src/main/res/navigation/main_navigation_graph.xml b/app/src/main/res/navigation/main_navigation_graph.xml index 725a627..75b1a01 100644 --- a/app/src/main/res/navigation/main_navigation_graph.xml +++ b/app/src/main/res/navigation/main_navigation_graph.xml @@ -7,7 +7,7 @@ Date: Tue, 21 Mar 2023 21:33:46 +0530 Subject: [PATCH 23/50] feature: Implement Single Activity Architecture --- app/src/main/AndroidManifest.xml | 6 --- .../vtucslab/activities/ContentActivity.kt | 45 ------------------- .../vtucslab/activities/DetailsActivity.kt | 44 ------------------ .../vtucslab/activities/MainActivity.kt | 12 +++++ .../screens/display/DisplayFragment.kt | 4 +- .../screens/programs/ProgramFragment.kt | 9 ++-- .../screens/repository/RepositoryFragment.kt | 9 ++-- app/src/main/res/layout/activity_content.xml | 20 --------- app/src/main/res/layout/activity_details.xml | 20 --------- .../navigation/content_navigation_graph.xml | 23 ---------- .../navigation/details_navigation_graph.xml | 41 ----------------- .../res/navigation/main_navigation_graph.xml | 35 +++++++++++---- 12 files changed, 47 insertions(+), 221 deletions(-) delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/activities/ContentActivity.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt delete mode 100644 app/src/main/res/layout/activity_content.xml delete mode 100644 app/src/main/res/layout/activity_details.xml delete mode 100644 app/src/main/res/navigation/content_navigation_graph.xml delete mode 100644 app/src/main/res/navigation/details_navigation_graph.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1334f1e..b7b44c8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -25,12 +25,6 @@ - - () - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityContentBinding.inflate(layoutInflater) - setContentView(binding.root) - - supportActionBar?.setDisplayHomeAsUpEnabled(true) - - val navHostFragment = - supportFragmentManager.findFragmentById(R.id.navigation_host_fragment) as NavHostFragment - - val displayFragmentArgs = DisplayFragmentArgs.Builder( - contentActivityArgs.baseUrl, - contentActivityArgs.fileName, - contentActivityArgs.title - ).build() - - navHostFragment.navController.setGraph( - R.navigation.content_navigation_graph, - displayFragmentArgs.toBundle() - ) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == android.R.id.home) { - this.onBackPressedDispatcher.onBackPressed() - return true - } - return super.onOptionsItemSelected(item) - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt deleted file mode 100644 index 3763f51..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DetailsActivity.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.nagpal.shivam.vtucslab.activities - -import android.os.Bundle -import android.view.MenuItem -import androidx.appcompat.app.AppCompatActivity -import androidx.navigation.fragment.NavHostFragment -import androidx.navigation.navArgs -import com.nagpal.shivam.vtucslab.R -import com.nagpal.shivam.vtucslab.databinding.ActivityDetailsBinding -import com.nagpal.shivam.vtucslab.screens.programs.ProgramFragmentArgs - -class DetailsActivity : AppCompatActivity() { - private lateinit var binding: ActivityDetailsBinding - private val detailsActivityArgs by navArgs() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityDetailsBinding.inflate(layoutInflater) - setContentView(binding.root) - - supportActionBar?.setDisplayHomeAsUpEnabled(true) - - val navHostFragment = - supportFragmentManager.findFragmentById(R.id.navigation_host_fragment) as NavHostFragment - - val programFragmentArgs = ProgramFragmentArgs.Builder( - detailsActivityArgs.baseUrl, - detailsActivityArgs.fileName, - detailsActivityArgs.title - ).build() - navHostFragment.navController.setGraph( - R.navigation.details_navigation_graph, - programFragmentArgs.toBundle() - ) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == android.R.id.home) { - this.onBackPressedDispatcher.onBackPressed() - return true - } - return super.onOptionsItemSelected(item) - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt index 916ae17..f6c98d5 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/MainActivity.kt @@ -10,6 +10,8 @@ import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.AppCompatActivity import androidx.core.view.GravityCompat import androidx.drawerlayout.widget.DrawerLayout +import androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED +import androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_UNLOCKED import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.* @@ -87,6 +89,16 @@ class MainActivity : AppCompatActivity() { override fun onDrawerStateChanged(newState: Int) { } }) + navController.addOnDestinationChangedListener { _, destination, _ -> + if (appBarConfiguration.topLevelDestinations.contains(destination.id)) { + binding.drawerLayout.setDrawerLockMode(LOCK_MODE_UNLOCKED, GravityCompat.START) + } else { + binding.drawerLayout.setDrawerLockMode( + LOCK_MODE_LOCKED_CLOSED, + GravityCompat.START + ) + } + } } override fun onSupportNavigateUp(): Boolean { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt index 51037f1..d4bd946 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt @@ -38,8 +38,6 @@ class DisplayFragment : Fragment() { viewModel = ViewModelProvider(this)[DisplayViewModel::class.java] - requireActivity().title = displayFragmentArgs.title - viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.uiState.collect { @@ -114,7 +112,7 @@ class DisplayFragment : Fragment() { else -> false } } - }) + }, viewLifecycleOwner) } override fun onResume() { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt index c5e30fb..4443012 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt @@ -39,9 +39,6 @@ class ProgramFragment : Fragment() { viewModel = ViewModelProvider(this)[ProgramViewModel::class.java] - requireActivity().title = programFragmentArgs.title - - viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.uiState.collect { @@ -82,13 +79,13 @@ class ProgramFragment : Fragment() { private fun setupRepositoryAdapter() { contentAdapter = ContentAdapter(requireContext(), ArrayList()) contentAdapter.setItemClickHandler { - val actionProgramFragmentToContentActivity = - ProgramFragmentDirections.actionProgramFragmentToContentActivity( + val actionProgramFragmentToDisplayFragment = + ProgramFragmentDirections.actionProgramFragmentToDisplayFragment( viewModel.uiState.value.baseUrl!!, it.fileName, it.fileName ) - findNavController().navigate(actionProgramFragmentToContentActivity) + findNavController().navigate(actionProgramFragmentToDisplayFragment) } binding.recyclerView.adapter = contentAdapter } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt index 46b9aa4..8943900 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt @@ -37,7 +37,6 @@ class RepositoryFragment : Fragment() { viewModel = ViewModelProvider(this)[RepositoryViewModel::class.java] - viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.uiState.collect { @@ -88,13 +87,13 @@ class RepositoryFragment : Fragment() { private fun setupRepositoryAdapter() { navigationAdapter = NavigationAdapter(requireContext(), ArrayList()) navigationAdapter.setNavigationAdapterItemClickHandler { laboratory, _ -> - val actionRepositoryFragmentToDetailsActivity = - RepositoryFragmentDirections.actionRepositoryFragmentToDetailsActivity( + val actionRepositoryFragmentToProgramFragment = + RepositoryFragmentDirections.actionRepositoryFragmentToProgramFragment( viewModel.uiState.value.baseUrl!!, laboratory.fileName, laboratory.title ) - findNavController().navigate(actionRepositoryFragmentToDetailsActivity) + findNavController().navigate(actionRepositoryFragmentToProgramFragment) } binding.repositoryRecyclerView.adapter = navigationAdapter } @@ -116,7 +115,7 @@ class RepositoryFragment : Fragment() { else -> false } } - }) + }, viewLifecycleOwner) } override fun onDestroyView() { diff --git a/app/src/main/res/layout/activity_content.xml b/app/src/main/res/layout/activity_content.xml deleted file mode 100644 index c347d31..0000000 --- a/app/src/main/res/layout/activity_content.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/activity_details.xml b/app/src/main/res/layout/activity_details.xml deleted file mode 100644 index c347d31..0000000 --- a/app/src/main/res/layout/activity_details.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - diff --git a/app/src/main/res/navigation/content_navigation_graph.xml b/app/src/main/res/navigation/content_navigation_graph.xml deleted file mode 100644 index a805a30..0000000 --- a/app/src/main/res/navigation/content_navigation_graph.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/navigation/details_navigation_graph.xml b/app/src/main/res/navigation/details_navigation_graph.xml deleted file mode 100644 index 77c0e3a..0000000 --- a/app/src/main/res/navigation/details_navigation_graph.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/navigation/main_navigation_graph.xml b/app/src/main/res/navigation/main_navigation_graph.xml index 75b1a01..4b7afe2 100644 --- a/app/src/main/res/navigation/main_navigation_graph.xml +++ b/app/src/main/res/navigation/main_navigation_graph.xml @@ -10,14 +10,33 @@ android:label="@string/app_name" tools:layout="@layout/fragment_repository"> + android:id="@+id/action_repositoryFragment_to_programFragment" + app:destination="@id/programFragment" /> - + + + + + + + + @@ -27,5 +46,5 @@ - + From a0365c28b56b4332f512952b570513272d193fb4 Mon Sep 17 00:00:00 2001 From: WallE Date: Tue, 21 Mar 2023 22:30:52 +0530 Subject: [PATCH 24/50] feature: Disable the old Activity from Launcher --- app/src/main/AndroidManifest.xml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b7b44c8..07ddf58 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,11 +14,9 @@ android:supportsRtl="true" android:theme="@style/AppTheme" tools:ignore="GoogleAppIndexingWarning"> - @@ -34,13 +32,7 @@ android:name=".activities.RepositoryActivity" android:exported="true" android:launchMode="singleTask" - android:theme="@style/AppTheme.NoActionBar"> - - - - - - + android:theme="@style/AppTheme.NoActionBar"/> Date: Wed, 22 Mar 2023 18:11:27 +0530 Subject: [PATCH 25/50] cleanup: Remove the leftover old files post the MVVM migration --- app/src/main/AndroidManifest.xml | 25 -- .../vtucslab/activities/DisplayActivity.java | 163 ------------- .../vtucslab/activities/ProgramActivity.java | 175 ------------- .../activities/RepositoryActivity.java | 230 ------------------ .../shivam/vtucslab/loaders/InfoLoader.java | 48 ---- .../vtucslab/loaders/RawStreamLoader.java | 46 ---- .../screens/display/DisplayFragment.kt | 2 +- .../screens/repository/RepositoryFragment.kt | 2 +- .../drawable-hdpi/ic_action_arrow_back.png | Bin 278 -> 0 bytes .../drawable-mdpi/ic_action_arrow_back.png | Bin 181 -> 0 bytes .../drawable-xhdpi/ic_action_arrow_back.png | Bin 265 -> 0 bytes .../drawable-xxhdpi/ic_action_arrow_back.png | Bin 466 -> 0 bytes app/src/main/res/layout/activity_display.xml | 59 ----- app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/layout/activity_program.xml | 37 --- .../main/res/layout/activity_repository.xml | 22 -- .../main/res/layout/content_repository.xml | 39 --- .../res/layout/coordinator_repository.xml | 26 -- app/src/main/res/layout/fragment_display.xml | 2 +- app/src/main/res/layout/fragment_program.xml | 2 +- app/src/main/res/layout/header_repository.xml | 37 --- ...activity.xml => menu_display_fragment.xml} | 0 ...in_activity.xml => menu_main_fragment.xml} | 0 .../res/menu/menu_main_navigation_drawer.xml | 13 - .../main/res/menu/menu_navigation_drawer.xml | 5 +- 25 files changed, 7 insertions(+), 928 deletions(-) delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/activities/DisplayActivity.java delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/activities/ProgramActivity.java delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/activities/RepositoryActivity.java delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/loaders/InfoLoader.java delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/loaders/RawStreamLoader.java delete mode 100644 app/src/main/res/drawable-hdpi/ic_action_arrow_back.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_action_arrow_back.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_action_arrow_back.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_action_arrow_back.png delete mode 100644 app/src/main/res/layout/activity_display.xml delete mode 100644 app/src/main/res/layout/activity_program.xml delete mode 100644 app/src/main/res/layout/activity_repository.xml delete mode 100644 app/src/main/res/layout/content_repository.xml delete mode 100644 app/src/main/res/layout/coordinator_repository.xml delete mode 100644 app/src/main/res/layout/header_repository.xml rename app/src/main/res/menu/{menu_display_activity.xml => menu_display_fragment.xml} (100%) rename app/src/main/res/menu/{menu_main_activity.xml => menu_main_fragment.xml} (100%) delete mode 100644 app/src/main/res/menu/menu_main_navigation_drawer.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 07ddf58..fb387c3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -27,30 +27,5 @@ - - - - - - - - - - - - diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DisplayActivity.java b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DisplayActivity.java deleted file mode 100644 index b8e14a4..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/DisplayActivity.java +++ /dev/null @@ -1,163 +0,0 @@ -package com.nagpal.shivam.vtucslab.activities; - -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Intent; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.text.TextUtils; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.app.NavUtils; -import androidx.databinding.DataBindingUtil; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.Loader; - -import com.nagpal.shivam.vtucslab.R; -import com.nagpal.shivam.vtucslab.databinding.ActivityDisplayBinding; -import com.nagpal.shivam.vtucslab.loaders.RawStreamLoader; -import com.nagpal.shivam.vtucslab.utilities.Constants; - -public class DisplayActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks { - - private static final int LOADER_ID = 1; - private static final String SUCCEEDED_KEY = "succeeded_key"; - private static final String SCROLL_X_KEY = "scroll_x_key"; - private static final String SCROLL_Y_KEY = "scroll_y_key"; - - private Boolean mSucceeded = false; - private String mUrl; - private String mCode; - - - private ActivityDisplayBinding mBinding; - private int mScrollX; - private int mScrollY; - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.menu_display_activity, menu); - if (mSucceeded) { - MenuItem copyMenuItem = menu.findItem(R.id.menu_item_copy_display_activity); - copyMenuItem.setEnabled(true); - } - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int itemId = item.getItemId(); - if (itemId == android.R.id.home) { - NavUtils.navigateUpFromSameTask(DisplayActivity.this); - return true; - } else if (itemId == R.id.display_menu_item_refresh) { - recreate(); - return true; - } else if (itemId == R.id.menu_item_copy_display_activity) { - ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - ClipData clipData = new ClipData(ClipData.newPlainText("Code", mCode)); - if (clipboardManager != null) { - clipboardManager.setPrimaryClip(clipData); - Toast.makeText(DisplayActivity.this, "Code copied to Clipboard.", Toast.LENGTH_SHORT).show(); - } - return true; - } - return super.onOptionsItemSelected(item); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mBinding = DataBindingUtil.setContentView(this, R.layout.activity_display); - ActionBar actionBar = getSupportActionBar(); - - initViews(); - - if (savedInstanceState != null) { - mSucceeded = savedInstanceState.getBoolean(SUCCEEDED_KEY, false); - mScrollX = savedInstanceState.getInt(SCROLL_X_KEY, 0); - mScrollY = savedInstanceState.getInt(SCROLL_Y_KEY, 0); - } - - Intent intent = getIntent(); - - mUrl = intent.getStringExtra(Constants.URL_INTENT_TAG); - if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(true); - } - - String title = intent.getStringExtra(Constants.TITLE_INTENT_TAG); - DisplayActivity.this.setTitle(title); - - ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); - NetworkInfo networkInfo = connectivityManager != null ? connectivityManager.getActiveNetworkInfo() : null; - LoaderManager loaderManager = LoaderManager.getInstance(this); - - if (!mSucceeded) { - loaderManager.destroyLoader(LOADER_ID); - } - - if (networkInfo != null && networkInfo.isConnected()) { - loaderManager.initLoader(LOADER_ID, null, DisplayActivity.this); - } else { - mBinding.progressBarDisplay.setVisibility(View.GONE); - mBinding.emptyTextViewDisplay.setVisibility(View.VISIBLE); - mBinding.emptyTextViewDisplay.setText(R.string.no_internet_connection); - mSucceeded = false; - } - } - - private void initViews() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - mBinding.displayTextView.setLetterSpacing(0.1f); - } - } - - @NonNull - @Override - public Loader onCreateLoader(int i, Bundle bundle) { - return new RawStreamLoader(DisplayActivity.this, mUrl); - } - - @Override - public void onLoadFinished(@NonNull Loader loader, String s) { - mBinding.progressBarDisplay.setVisibility(View.GONE); - if (TextUtils.isEmpty(s)) { - Toast.makeText(DisplayActivity.this, getString(R.string.error_occurred), Toast.LENGTH_LONG).show(); - mSucceeded = false; - return; - } - - mSucceeded = true; - mCode = s; - s = s.replaceAll("\t", "\t\t"); - mBinding.displayTextView.setText(s); - new Handler(getMainLooper()).postDelayed(() -> { - mBinding.horizontalScroll.setScrollX(mScrollX); - mBinding.verticalScroll.setScrollY(mScrollY); - }, 500); - invalidateOptionsMenu(); - } - - @Override - public void onLoaderReset(@NonNull Loader loader) { - mBinding.displayTextView.setText(null); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - outState.putBoolean(SUCCEEDED_KEY, mSucceeded); - outState.putInt(SCROLL_X_KEY, mBinding.horizontalScroll.getScrollX()); - outState.putInt(SCROLL_Y_KEY, mBinding.verticalScroll.getScrollY()); - super.onSaveInstanceState(outState); - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/ProgramActivity.java b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/ProgramActivity.java deleted file mode 100644 index fd8022a..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/ProgramActivity.java +++ /dev/null @@ -1,175 +0,0 @@ -package com.nagpal.shivam.vtucslab.activities; - - -import android.content.Intent; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.Bundle; -import android.view.MenuItem; -import android.view.View; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.app.NavUtils; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.Loader; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.nagpal.shivam.vtucslab.R; -import com.nagpal.shivam.vtucslab.adapters.ContentAdapter; -import com.nagpal.shivam.vtucslab.loaders.InfoLoader; -import com.nagpal.shivam.vtucslab.models.ContentFile; -import com.nagpal.shivam.vtucslab.models.LabResponse; -import com.nagpal.shivam.vtucslab.models.Laboratory; -import com.nagpal.shivam.vtucslab.utilities.Constants; - -import java.util.ArrayList; - -public class ProgramActivity - extends AppCompatActivity - implements LoaderManager.LoaderCallbacks, - ContentAdapter.ItemClickHandler { - - public static final String INTENT_LABORATORY = "PROGRAM_INTENT_LABORATORY"; - public static final String INTENT_LABORATORY_BASE_URL = "PROGRAM_INTENT_LABORATORY_BASE_URL"; - - private static final int REPO_LOADER_ID = 2; - private static final String SUCCEEDED_KEY = "succeeded_key"; - - private ContentAdapter mProgramAdapter; - private ProgressBar mProgressBar; - private RecyclerView mProgramRecyclerView; - private TextView mEmptyTextView; - private boolean mSucceeded; - private String mProgramBaseUrl; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_program); - - if (savedInstanceState != null) { - mSucceeded = savedInstanceState.getBoolean(SUCCEEDED_KEY, false); - } - - initAndSetupViews(); - - setupProgramAdapter(); - - loadPrograms(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home) { - NavUtils.navigateUpFromSameTask(ProgramActivity.this); - return true; - } - return super.onOptionsItemSelected(item); - } - - private void initAndSetupViews() { - mProgramRecyclerView = findViewById(R.id.activity_program_recycler_view_program); - mProgramRecyclerView.setLayoutManager(new LinearLayoutManager(ProgramActivity.this, LinearLayoutManager.VERTICAL, false)); - mProgramRecyclerView.setHasFixedSize(true); - - mEmptyTextView = findViewById(R.id.activity_program_text_view_empty); - - mProgressBar = findViewById(R.id.activity_program_progress_bar); - } - - private void setupProgramAdapter() { - mProgramAdapter = new ContentAdapter(ProgramActivity.this, new ArrayList<>()); - mProgramRecyclerView.setAdapter(mProgramAdapter); - mProgramAdapter.setItemClickHandler(this); - } - - private void loadPrograms() { - Intent intent = getIntent(); - - Laboratory laboratory = (Laboratory) intent.getSerializableExtra(INTENT_LABORATORY); - String laboratoryBaseUrl = intent.getStringExtra(INTENT_LABORATORY_BASE_URL); - - ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); - NetworkInfo networkInfo = connectivityManager != null ? connectivityManager.getActiveNetworkInfo() : null; - LoaderManager loaderManager = LoaderManager.getInstance(this); - - if (!mSucceeded) { - loaderManager.destroyLoader(REPO_LOADER_ID); - } - - mEmptyTextView.setVisibility(View.GONE); - - setTitle(laboratory.getTitle()); - if (networkInfo != null && networkInfo.isConnected()) { - Bundle bundle = new Bundle(); - String url = laboratoryBaseUrl + "/" + laboratory.getFileName(); - - bundle.putString("URL", url); - loaderManager.initLoader(REPO_LOADER_ID, bundle, ProgramActivity.this); - } else { - mProgressBar.setVisibility(View.GONE); - mSucceeded = false; - showErrorMessage(getString(R.string.no_internet_connection)); - } - } - - @NonNull - @Override - public Loader onCreateLoader(int id, Bundle args) { - assert args != null; - return new InfoLoader(ProgramActivity.this, args.getString("URL")); - } - - @Override - public void onLoadFinished(@NonNull Loader loader, LabResponse labResponse) { - mProgressBar.setVisibility(View.GONE); - - if (labResponse == null) { - mSucceeded = false; - Toast.makeText(ProgramActivity.this, getString(R.string.error_occurred), Toast.LENGTH_LONG).show(); - return; - } - - if (labResponse.isValid()) { - mProgramBaseUrl = labResponse.getGithub_raw_content() + "/" + - labResponse.getOrganization() + "/" + - labResponse.getRepository() + "/" + - labResponse.getBranch(); - mSucceeded = true; - mProgramAdapter.clear(); - mProgramAdapter.addAll(labResponse.getLabExperiments()); - } else { - mSucceeded = false; - showErrorMessage(labResponse.getInvalidationMessage()); - } - } - - @Override - public void onLoaderReset(@NonNull Loader loader) { - mProgramAdapter.clear(); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - outState.putBoolean(SUCCEEDED_KEY, mSucceeded); - super.onSaveInstanceState(outState); - } - - @Override - public void onContentFileClick(ContentFile file) { - Intent intent = new Intent(ProgramActivity.this, DisplayActivity.class); - intent.putExtra(Constants.TITLE_INTENT_TAG, file.getFileName()); - intent.putExtra(Constants.URL_INTENT_TAG, mProgramBaseUrl + "/" + file.getFileName()); - startActivity(intent); - } - - private void showErrorMessage(String error) { - mEmptyTextView.setVisibility(View.VISIBLE); - mEmptyTextView.setText(error); - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/RepositoryActivity.java b/app/src/main/java/com/nagpal/shivam/vtucslab/activities/RepositoryActivity.java deleted file mode 100644 index dca53aa..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/activities/RepositoryActivity.java +++ /dev/null @@ -1,230 +0,0 @@ -package com.nagpal.shivam.vtucslab.activities; - -import android.content.Intent; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.ImageButton; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.ActionBarDrawerToggle; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import androidx.core.view.GravityCompat; -import androidx.drawerlayout.widget.DrawerLayout; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.Loader; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.google.android.material.navigation.NavigationView; -import com.nagpal.shivam.vtucslab.R; -import com.nagpal.shivam.vtucslab.adapters.NavigationAdapter; -import com.nagpal.shivam.vtucslab.loaders.InfoLoader; -import com.nagpal.shivam.vtucslab.models.LabResponse; -import com.nagpal.shivam.vtucslab.models.Laboratory; - -import java.util.ArrayList; - -public class RepositoryActivity - extends AppCompatActivity - implements NavigationView.OnNavigationItemSelectedListener, - LoaderManager.LoaderCallbacks, - NavigationAdapter.NavigationAdapterItemClickHandler { - - private static final int NAV_LOADER_ID = 1; - private static final String SUCCEEDED_KEY = "succeeded_key"; - private static final String URL = "https://raw.githubusercontent.com/vtucs/Index_v3/master/Index_v3.json"; - private DrawerLayout mDrawerLayout; - private NavigationAdapter mRepositoryAdapter; - private ProgressBar mProgressBar; - private RecyclerView mRepositoryRecyclerView; - private TextView mEmptyTextView; - private Toolbar mToolbar; - private boolean mSucceeded; - private String mLaboratoryBaseUrl; - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.menu_main_activity, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == R.id.main_menu_item_privacy) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse("https://github.com/ShivamNagpal/Privacy_Policies/blob/master/VTU_CS_LAB_MANUAL.md")); - startActivity(intent); - return true; - } - return super.onOptionsItemSelected(item); - } - - - @Override - public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) { - boolean flag = false; - int itemId = menuItem.getItemId(); - if (itemId == R.id.menu_item_repository) { - flag = true; - } else if (itemId == R.id.menu_item_exit) { - exitApplication(); - flag = true; - } - if (flag) { - closeNavigationDrawer(); - } - return flag; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_repository); - mToolbar = findViewById(R.id.toolbar); - setSupportActionBar(mToolbar); - - if (savedInstanceState != null) { - mSucceeded = savedInstanceState.getBoolean(SUCCEEDED_KEY, false); - } - - initAndSetupViews(); - - setupRepositoryAdapter(); - - loadRepositories(); - } - - private void loadRepositories() { - ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); - NetworkInfo networkInfo = connectivityManager != null ? connectivityManager.getActiveNetworkInfo() : null; - LoaderManager loaderManager = LoaderManager.getInstance(this); - - if (!mSucceeded) { - loaderManager.destroyLoader(NAV_LOADER_ID); - } - - if (networkInfo != null && networkInfo.isConnected()) { - loaderManager.initLoader(NAV_LOADER_ID, null, RepositoryActivity.this); - } else { - mProgressBar.setVisibility(View.GONE); - mSucceeded = false; - showErrorMessage(getString(R.string.no_internet_connection)); - } - - mRepositoryAdapter.setNavigationAdapterItemClickHandler(this); - } - - private void initAndSetupViews() { - mDrawerLayout = findViewById(R.id.activity_repository_drawer_layout); - setUpDrawerToggle(); - - NavigationView navigationView = findViewById(R.id.activity_repository_navigation_view); - navigationView.setNavigationItemSelectedListener(this); - navigationView.getMenu().getItem(0).setChecked(true); - - View headerView = navigationView.getHeaderView(0); - - ImageButton navigationDrawerBackButton = headerView.findViewById(R.id.activity_repository_image_button_close_drawer); - navigationDrawerBackButton.setOnClickListener(view -> closeNavigationDrawer()); - - mRepositoryRecyclerView = findViewById(R.id.activity_repository_recycler_view_repository); - mRepositoryRecyclerView.setLayoutManager(new LinearLayoutManager(RepositoryActivity.this, LinearLayoutManager.VERTICAL, false)); - mRepositoryRecyclerView.setHasFixedSize(true); - - mEmptyTextView = findViewById(R.id.empty_text_view_main); - - mProgressBar = findViewById(R.id.progress_bar_main); - } - - private void setUpDrawerToggle() { - ActionBarDrawerToggle actionBarDrawerToggle = - new ActionBarDrawerToggle(RepositoryActivity.this, - mDrawerLayout, - mToolbar, - R.string.drawer_open, - R.string.drawer_close); - mDrawerLayout.addDrawerListener(actionBarDrawerToggle); - actionBarDrawerToggle.syncState(); - } - - private void closeNavigationDrawer() { - mDrawerLayout.closeDrawer(GravityCompat.START, true); - } - - private void exitApplication() { - this.finishAffinity(); - new Handler(Looper.getMainLooper()).postDelayed(() -> System.exit(0), 1000); - } - - private void setupRepositoryAdapter() { - mRepositoryAdapter = new NavigationAdapter(RepositoryActivity.this, new ArrayList<>()); - mRepositoryRecyclerView.setAdapter(mRepositoryAdapter); - } - - @NonNull - @Override - public Loader onCreateLoader(int id, Bundle args) { - return new InfoLoader(RepositoryActivity.this, URL); - - } - - @Override - public void onLoadFinished(@NonNull Loader loader, LabResponse labResponse) { - mProgressBar.setVisibility(View.GONE); - - if (labResponse == null) { - mSucceeded = false; - Toast.makeText(RepositoryActivity.this, getString(R.string.error_occurred), Toast.LENGTH_LONG).show(); - return; - } - - if (labResponse.isValid()) { - mLaboratoryBaseUrl = labResponse.getGithub_raw_content() + "/" + - labResponse.getOrganization() + "/" + - labResponse.getRepository() + "/" + - labResponse.getBranch(); - - mRepositoryAdapter.clear(); - mRepositoryAdapter.addAll(labResponse.getLaboratories()); - } else { - mSucceeded = false; - showErrorMessage(labResponse.getInvalidationMessage()); - } - } - - @Override - public void onLoaderReset(@NonNull Loader loader) { - mRepositoryAdapter.clear(); - } - - @Override - public void onNavigationAdapterItemClick(Laboratory laboratory, int i) { - Intent intent = new Intent(RepositoryActivity.this, ProgramActivity.class); - intent.putExtra(ProgramActivity.INTENT_LABORATORY, laboratory); - intent.putExtra(ProgramActivity.INTENT_LABORATORY_BASE_URL, mLaboratoryBaseUrl); - startActivity(intent); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - outState.putBoolean(SUCCEEDED_KEY, mSucceeded); - super.onSaveInstanceState(outState); - } - - private void showErrorMessage(String error) { - mEmptyTextView.setVisibility(View.VISIBLE); - mEmptyTextView.setText(error); - } - -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/InfoLoader.java b/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/InfoLoader.java deleted file mode 100644 index ef50a66..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/InfoLoader.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.nagpal.shivam.vtucslab.loaders; - - -import android.content.Context; - -import androidx.loader.content.AsyncTaskLoader; - -import com.nagpal.shivam.vtucslab.models.LabResponse; -import com.nagpal.shivam.vtucslab.services.VtuCsLabService; - -import java.io.IOException; - -import retrofit2.Call; - -public class InfoLoader extends AsyncTaskLoader { - private final String mUrl; - private LabResponse mLabResponse; - - public InfoLoader(Context context, String url) { - super(context); - mUrl = url; - } - - @Override - protected void onStartLoading() { - if (mLabResponse != null) { - deliverResult(mLabResponse); - } else { - forceLoad(); - } - - } - - @Override - public LabResponse loadInBackground() { - if (mUrl == null) { - return null; - } - VtuCsLabService vtuCSLabService = VtuCsLabService.Companion.getInstance(); - Call labResponseCall = vtuCSLabService.getLabResponse(mUrl); - try { - mLabResponse = labResponseCall.execute().body(); - return mLabResponse; - } catch (IOException e) { - return null; - } - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/RawStreamLoader.java b/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/RawStreamLoader.java deleted file mode 100644 index c34daf6..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/loaders/RawStreamLoader.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.nagpal.shivam.vtucslab.loaders; - -import android.content.Context; - -import androidx.loader.content.AsyncTaskLoader; - -import com.nagpal.shivam.vtucslab.services.VtuCsLabService; - -import java.io.IOException; - -import retrofit2.Call; - -public class RawStreamLoader extends AsyncTaskLoader { - private final String mUrl; - private String fetchedData; - - // TODO: Implement Local Data Saving. - public RawStreamLoader(Context context, String url) { - super(context); - mUrl = url; - } - - @Override - protected void onStartLoading() { - if (fetchedData != null) { - deliverResult(fetchedData); - } else { - forceLoad(); - } - } - - @Override - public String loadInBackground() { - if (mUrl == null) { - return null; - } - VtuCsLabService vtuCSLabService = VtuCsLabService.Companion.getInstance(); - Call stringCall = vtuCSLabService.fetchRawResponse(mUrl); - try { - fetchedData = stringCall.execute().body(); - return fetchedData; - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt index d4bd946..115dfde 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt @@ -78,7 +78,7 @@ class DisplayFragment : Fragment() { private fun setupMenuProvider() { requireActivity().addMenuProvider(object : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { - menuInflater.inflate(R.menu.menu_display_activity, menu) + menuInflater.inflate(R.menu.menu_display_fragment, menu) if (viewModel.uiState.value.stage == Stages.SUCCEEDED) { menu.findItem(R.id.menu_item_copy_display_activity).isEnabled = true } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt index 8943900..53d8a78 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt @@ -101,7 +101,7 @@ class RepositoryFragment : Fragment() { private fun setupMenuProvider() { requireActivity().addMenuProvider(object : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { - menuInflater.inflate(R.menu.menu_main_activity, menu) + menuInflater.inflate(R.menu.menu_main_fragment, menu) } override fun onMenuItemSelected(menuItem: MenuItem): Boolean { diff --git a/app/src/main/res/drawable-hdpi/ic_action_arrow_back.png b/app/src/main/res/drawable-hdpi/ic_action_arrow_back.png deleted file mode 100644 index e2ff5363f089ce6c120c2f16ca6552b087000d35..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 278 zcmV+x0qOpUP)YL;M!b&Vrh+}9RK+d?wRehdAcYX>Do$neJ@{aj^OBE3lVuFUFbty< z=UfSP78lRK9y~oV=avd|%eNkxx0bXCmlrNzX-S)K`7NJ>6GHzY4?_=s~c{xcFa9_#A-@}AMYKvF6J+d zKQ1&_GPEm)F>kaM*gdiH>j_Wx7kwJbqm^VzkE~E`o2C4TZILHi@e#*e<|i-67#d9L eZwhqKWngG`tVn&8|92749SokXelF{r5}E+LzeThF diff --git a/app/src/main/res/drawable-xhdpi/ic_action_arrow_back.png b/app/src/main/res/drawable-xhdpi/ic_action_arrow_back.png deleted file mode 100644 index 7b692827253a3b4cf90b4fdfdc9df2a4d2a0ea8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=7d%}YLn>~)on^>%$U%fvd<*BK z?vC~ok!x%^U6pQd=RGVuV!gKhpaaJwlXbJ-%x=8CWVU9V!VycLR)&WC!cI4CYNb}@ z?>^}&abmyS?&ImJKK-rvD5&=2_uMH@t#6A}KAGq)hai4VJ!PM}_U)X)-!Y%OBNyw; z*Kx?LKEW0d*`oGTa89>C|AVV#CwDl%GCvs8QqgI?wEm$glVFDfi;_Sihl>J)`A6f> z#i{39Sk`}8e#4@v@6U9Os!xkKW_<#=Xu?eM6DRWyi!6QO2jY6V`njxgN@xNA5&Lee diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_arrow_back.png b/app/src/main/res/drawable-xxhdpi/ic_action_arrow_back.png deleted file mode 100644 index 4d205b2d3e86e8997c9239b2eb9394c6341b412c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 466 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7Ro>V4Ugc;uunK>+Ky|Kc+;H*2I>K zhw(jn4(7>Eg>-a#HWn$Xi5xq3=-9b?Uc%CfZ#dsi=b7l#vEba?#^=6%M?;?9d*3=? z=F1YGd0=qB=GfzpZDOBJy?519dOmTTTh_fltNu7XSu6$>=OvXB(s^@V(EpN@7vVX1R zt%b4|_}(^@Ww38?wq786i}5Xxi-OLD%PAjE{T)5)@3A{fn_J4f?kVZ(o(oRd-g_%@ z>J-H{TYatBCajTV{&ASGMEQF5uD`v37oJJ+E7mgvt3Nz8z2KjKm@Vh~iPy!R?-aCm zu3xyg{et+$WP0J~KiCa>jY}0-;xlxv>&_ OAQ4YjKbLh*2~7aMwazvG diff --git a/app/src/main/res/layout/activity_display.xml b/app/src/main/res/layout/activity_display.xml deleted file mode 100644 index ab852d8..0000000 --- a/app/src/main/res/layout/activity_display.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 6bb1cb4..4909472 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -26,5 +26,5 @@ android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="true" - app:menu="@menu/menu_main_navigation_drawer" /> + app:menu="@menu/menu_navigation_drawer" /> diff --git a/app/src/main/res/layout/activity_program.xml b/app/src/main/res/layout/activity_program.xml deleted file mode 100644 index a839c5c..0000000 --- a/app/src/main/res/layout/activity_program.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_repository.xml b/app/src/main/res/layout/activity_repository.xml deleted file mode 100644 index bdfe4ff..0000000 --- a/app/src/main/res/layout/activity_repository.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/layout/content_repository.xml b/app/src/main/res/layout/content_repository.xml deleted file mode 100644 index 5afeda9..0000000 --- a/app/src/main/res/layout/content_repository.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/coordinator_repository.xml b/app/src/main/res/layout/coordinator_repository.xml deleted file mode 100644 index 950d67f..0000000 --- a/app/src/main/res/layout/coordinator_repository.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_display.xml b/app/src/main/res/layout/fragment_display.xml index 1f2e179..5961aa8 100644 --- a/app/src/main/res/layout/fragment_display.xml +++ b/app/src/main/res/layout/fragment_display.xml @@ -6,7 +6,7 @@ android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false" - tools:context="com.nagpal.shivam.vtucslab.activities.DisplayActivity"> + tools:context=".screens.display.DisplayFragment"> + tools:context=".screens.programs.ProgramFragment"> - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/menu_display_activity.xml b/app/src/main/res/menu/menu_display_fragment.xml similarity index 100% rename from app/src/main/res/menu/menu_display_activity.xml rename to app/src/main/res/menu/menu_display_fragment.xml diff --git a/app/src/main/res/menu/menu_main_activity.xml b/app/src/main/res/menu/menu_main_fragment.xml similarity index 100% rename from app/src/main/res/menu/menu_main_activity.xml rename to app/src/main/res/menu/menu_main_fragment.xml diff --git a/app/src/main/res/menu/menu_main_navigation_drawer.xml b/app/src/main/res/menu/menu_main_navigation_drawer.xml deleted file mode 100644 index 9e58fd0..0000000 --- a/app/src/main/res/menu/menu_main_navigation_drawer.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/menu/menu_navigation_drawer.xml b/app/src/main/res/menu/menu_navigation_drawer.xml index 21ff825..9e58fd0 100644 --- a/app/src/main/res/menu/menu_navigation_drawer.xml +++ b/app/src/main/res/menu/menu_navigation_drawer.xml @@ -4,11 +4,10 @@ tools:showIn="navigation_view"> - - \ No newline at end of file + From 278a6b7045bcd13ac1423ff90dbd5ed6a7eb8329 Mon Sep 17 00:00:00 2001 From: WallE Date: Thu, 23 Mar 2023 23:20:27 +0530 Subject: [PATCH 26/50] feature: Disable Firebase Crashlytics for the Debug Flavor --- .../com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt index 2c844c0..8c71608 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt @@ -1,5 +1,13 @@ package com.nagpal.shivam.vtucslab import androidx.multidex.MultiDexApplication +import com.google.firebase.crashlytics.FirebaseCrashlytics -class VTUCSLabApplication : MultiDexApplication() +class VTUCSLabApplication : MultiDexApplication() { + override fun onCreate() { + super.onCreate() + if (BuildConfig.DEBUG) { + FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false) + } + } +} From feb8585cc70b28d08ca8a6ac7e9e48c523dfd609 Mon Sep 17 00:00:00 2001 From: WallE Date: Wed, 22 Mar 2023 19:45:12 +0530 Subject: [PATCH 27/50] feature: Convert the Java Files to Kotlin and Replace GSON Converter with Jackson Converter to detect Missing Kotlin Paramters while converting --- app/build.gradle | 6 +- .../vtucslab/adapters/ContentAdapter.java | 204 ---------------- .../vtucslab/adapters/ContentAdapter.kt | 230 ++++++++++++++++++ .../adapters/MultipleFileAdapter.java | 74 ------ .../vtucslab/adapters/MultipleFileAdapter.kt | 57 +++++ .../adapters/MultipleSubPartAdapter.java | 103 -------- .../adapters/MultipleSubPartAdapter.kt | 100 ++++++++ .../vtucslab/adapters/NavigationAdapter.java | 88 ------- .../vtucslab/adapters/NavigationAdapter.kt | 74 ++++++ .../shivam/vtucslab/models/ContentFile.java | 13 - .../shivam/vtucslab/models/ContentFile.kt | 5 + .../shivam/vtucslab/models/LabExperiment.java | 25 -- .../shivam/vtucslab/models/LabExperiment.kt | 6 + .../vtucslab/models/LabExperimentSubPart.java | 25 -- .../vtucslab/models/LabExperimentSubPart.kt | 6 + .../shivam/vtucslab/models/LabResponse.java | 87 ------- .../shivam/vtucslab/models/Laboratory.java | 24 -- .../shivam/vtucslab/models/Laboratory.kt | 6 + .../models/LaboratoryExperimentResponse.kt | 14 ++ .../vtucslab/models/LaboratoryResponse.kt | 14 ++ .../vtucslab/screens/LabResponseViewModel.kt | 66 ----- .../vtucslab/screens/display/DisplayState.kt | 7 + .../screens/display/DisplayViewModel.kt | 14 +- .../screens/programs/ProgramFragment.kt | 31 ++- .../vtucslab/screens/programs/ProgramState.kt | 10 + .../screens/programs/ProgramViewModel.kt | 65 ++++- .../screens/repository/RepositoryFragment.kt | 32 ++- .../screens/repository/RepositoryState.kt | 10 + .../screens/repository/RepositoryViewModel.kt | 65 ++++- .../vtucslab/services/VtuCsLabService.kt | 9 +- .../vtucslab/ui/state/NetworkRequestState.kt | 10 - .../shivam/vtucslab/utilities/Constants.kt | 3 +- .../vtucslab/utilities/StaticMethods.kt | 25 +- 33 files changed, 736 insertions(+), 772 deletions(-) delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/adapters/ContentAdapter.java create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/adapters/ContentAdapter.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleFileAdapter.java create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleFileAdapter.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleSubPartAdapter.java create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleSubPartAdapter.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/adapters/NavigationAdapter.java create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/adapters/NavigationAdapter.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/models/ContentFile.java create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/models/ContentFile.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperiment.java create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperiment.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperimentSubPart.java create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperimentSubPart.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/models/LabResponse.java delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/models/Laboratory.java create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/models/Laboratory.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/models/LaboratoryExperimentResponse.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/models/LaboratoryResponse.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/LabResponseViewModel.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayState.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramState.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryState.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/ui/state/NetworkRequestState.kt diff --git a/app/build.gradle b/app/build.gradle index a066821..07d62e1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -44,8 +44,8 @@ dependencies { implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion" implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion" - // GSON - implementation 'com.google.code.gson:gson:2.8.9' + // https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-kotlin + implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.14.2' // Firebase SDK implementation 'com.google.firebase:firebase-messaging-ktx' @@ -53,7 +53,7 @@ dependencies { // Retrofit implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" - implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion" + implementation "com.squareup.retrofit2:converter-jackson:$retrofitVersion" implementation "com.squareup.retrofit2:converter-scalars:$retrofitVersion" testImplementation 'junit:junit:4.13.2' diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/ContentAdapter.java b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/ContentAdapter.java deleted file mode 100644 index 2c43fb4..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/ContentAdapter.java +++ /dev/null @@ -1,204 +0,0 @@ -package com.nagpal.shivam.vtucslab.adapters; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.databinding.DataBindingUtil; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.nagpal.shivam.vtucslab.R; -import com.nagpal.shivam.vtucslab.databinding.LayoutCardSeMspBinding; -import com.nagpal.shivam.vtucslab.databinding.LayoutCardSeSspMfBinding; -import com.nagpal.shivam.vtucslab.databinding.LayoutCardSeSspSfBinding; -import com.nagpal.shivam.vtucslab.models.ContentFile; -import com.nagpal.shivam.vtucslab.models.LabExperiment; -import com.nagpal.shivam.vtucslab.models.LabExperimentSubPart; -import com.nagpal.shivam.vtucslab.utilities.StaticMethods; - -import java.util.ArrayList; -import java.util.Arrays; - -public class ContentAdapter extends RecyclerView.Adapter { - - private static final int VIEW_TYPE_SSP_SF = 0; - private static final int VIEW_TYPE_MSP_SF = 1; - private static final int VIEW_TYPE_SSP_MF = 2; - private static final int VIEW_TYPE_MSP_MF = 3; - private static final int VIEW_TYPE_INVALID = -1; - private final Context mContext; - private final ArrayList mLabExperimentArrayList; - private ItemClickHandler mItemClickHandler; - - public ContentAdapter(Context context, ArrayList labExperimentArrayList) { - this.mContext = context; - this.mLabExperimentArrayList = labExperimentArrayList; - } - - public void setItemClickHandler(ItemClickHandler itemClickHandler) { - mItemClickHandler = itemClickHandler; - } - - public void addAll(@NonNull LabExperiment[] labExperiments) { - int i = mLabExperimentArrayList.size(); - mLabExperimentArrayList.addAll(Arrays.asList(labExperiments)); - notifyItemRangeInserted(i, labExperiments.length); - } - - - @SuppressLint("NotifyDataSetChanged") - public void clear() { - mLabExperimentArrayList.clear(); - notifyDataSetChanged(); - } - - @NonNull - @Override - public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - switch (viewType) { - case VIEW_TYPE_SSP_SF: - LayoutCardSeSspSfBinding sspSfBinding = DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout.layout_card_se_ssp_sf, parent, false); - return new SspSfViewHolder(sspSfBinding); - case VIEW_TYPE_MSP_SF: - LayoutCardSeMspBinding mspSfBinding = DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout.layout_card_se_msp, parent, false); - return new MspSfViewHolder(mspSfBinding); - case VIEW_TYPE_SSP_MF: - LayoutCardSeSspMfBinding sspMfBinding = DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout.layout_card_se_ssp_mf, parent, false); - return new SspMfViewHolder(sspMfBinding); - case VIEW_TYPE_MSP_MF: - LayoutCardSeMspBinding seMspBinding = DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout.layout_card_se_msp, parent, false); - return new MspMfViewHolder(seMspBinding); - default: - return new InvalidViewHolder(new View(mContext)); - - } - } - - @Override - public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { - LabExperiment labExperiment = mLabExperimentArrayList.get(position); - if (holder instanceof SspSfViewHolder) { - SspSfViewHolder sspSfViewHolder = (SspSfViewHolder) holder; - sspSfViewHolder.mBinding.serialOrder.setText(processSerialOrder(labExperiment.getSerialOrder())); - String[] parts = labExperiment.getLabExperimentSubParts()[0].getContentFiles()[0].getFileName().split("\\."); - if (parts.length >= 2) { - sspSfViewHolder.mBinding.programTitle.setText(StaticMethods.formatProgramName(parts[parts.length - 2])); - } - } else if (holder instanceof MspSfViewHolder) { - MspSfViewHolder mspSfViewHolder = (MspSfViewHolder) holder; - mspSfViewHolder.mBinding.serialOrder.setText(processSerialOrder(labExperiment.getSerialOrder())); - MultipleSubPartAdapter adapter = new MultipleSubPartAdapter(mContext, labExperiment.getLabExperimentSubParts(), false, mItemClickHandler); - mspSfViewHolder.mBinding.subPartContainer.setAdapter(adapter); - } else if (holder instanceof SspMfViewHolder) { - SspMfViewHolder sspMfViewHolder = (SspMfViewHolder) holder; - sspMfViewHolder.mBinding.serialOrder.setText(processSerialOrder(labExperiment.getSerialOrder())); - MultipleFileAdapter adapter = new MultipleFileAdapter(mContext, R.layout.layout_card_single_files_without_sub_parts, labExperiment.getLabExperimentSubParts()[0].getContentFiles(), mItemClickHandler); - sspMfViewHolder.mBinding.filesContainer.setAdapter(adapter); - } else if (holder instanceof MspMfViewHolder) { - MspMfViewHolder mspMfViewHolder = (MspMfViewHolder) holder; - mspMfViewHolder.mBinding.serialOrder.setText(processSerialOrder(labExperiment.getSerialOrder())); - MultipleSubPartAdapter adapter = new MultipleSubPartAdapter(mContext, labExperiment.getLabExperimentSubParts(), true, mItemClickHandler); - mspMfViewHolder.mBinding.subPartContainer.setAdapter(adapter); - } - } - - @Override - public int getItemCount() { - return mLabExperimentArrayList.size(); - } - - @Override - public int getItemViewType(int position) { - LabExperiment experiment = mLabExperimentArrayList.get(position); - int contentFileLength = 1; - LabExperimentSubPart[] labExperimentSubParts = experiment.getLabExperimentSubParts(); - for (LabExperimentSubPart subPart : labExperimentSubParts) { - contentFileLength = Math.max(contentFileLength, subPart.getContentFiles().length); - } - if (labExperimentSubParts.length == 1 && contentFileLength == 1) - return VIEW_TYPE_SSP_SF; - else if (labExperimentSubParts.length > 1 && contentFileLength == 1) - return VIEW_TYPE_MSP_SF; - else if (labExperimentSubParts.length == 1) - return VIEW_TYPE_SSP_MF; - else if (labExperimentSubParts.length > 1) - return VIEW_TYPE_MSP_MF; - else - return VIEW_TYPE_INVALID; - } - - private String processSerialOrder(String order) { - try { - int i = Integer.parseInt(order); - return String.valueOf(i); - } catch (NumberFormatException e) { - return order; - } - } - - public interface ItemClickHandler { - void onContentFileClick(ContentFile file); - } - - static class InvalidViewHolder extends RecyclerView.ViewHolder { - InvalidViewHolder(@NonNull View itemView) { - super(itemView); - } - } - - class SspSfViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { - LayoutCardSeSspSfBinding mBinding; - - SspSfViewHolder(LayoutCardSeSspSfBinding binding) { - super(binding.getRoot()); - mBinding = binding; - binding.getRoot().setOnClickListener(this); - } - - @Override - public void onClick(View view) { - if (mItemClickHandler != null) { - int position = getAdapterPosition(); - ContentFile contentFile = mLabExperimentArrayList.get(position).getLabExperimentSubParts()[0].getContentFiles()[0]; - mItemClickHandler.onContentFileClick(contentFile); - } - } - } - - class MspSfViewHolder extends RecyclerView.ViewHolder { - LayoutCardSeMspBinding mBinding; - - MspSfViewHolder(LayoutCardSeMspBinding binding) { - super(binding.getRoot()); - mBinding = binding; - mBinding.subPartContainer.setLayoutManager(new LinearLayoutManager(mContext)); - mBinding.subPartContainer.setHasFixedSize(true); - } - } - - class SspMfViewHolder extends RecyclerView.ViewHolder { - LayoutCardSeSspMfBinding mBinding; - - SspMfViewHolder(LayoutCardSeSspMfBinding binding) { - super(binding.getRoot()); - mBinding = binding; - mBinding.filesContainer.setLayoutManager(new LinearLayoutManager(mContext)); - mBinding.filesContainer.setHasFixedSize(true); - } - } - - class MspMfViewHolder extends RecyclerView.ViewHolder { - LayoutCardSeMspBinding mBinding; - - MspMfViewHolder(LayoutCardSeMspBinding binding) { - super(binding.getRoot()); - mBinding = binding; - mBinding.subPartContainer.setLayoutManager(new LinearLayoutManager(mContext)); - mBinding.subPartContainer.setHasFixedSize(true); - } - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/ContentAdapter.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/ContentAdapter.kt new file mode 100644 index 0000000..1f3b39a --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/ContentAdapter.kt @@ -0,0 +1,230 @@ +package com.nagpal.shivam.vtucslab.adapters + +import android.annotation.SuppressLint +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.nagpal.shivam.vtucslab.R +import com.nagpal.shivam.vtucslab.databinding.LayoutCardSeMspBinding +import com.nagpal.shivam.vtucslab.databinding.LayoutCardSeSspMfBinding +import com.nagpal.shivam.vtucslab.databinding.LayoutCardSeSspSfBinding +import com.nagpal.shivam.vtucslab.models.ContentFile +import com.nagpal.shivam.vtucslab.models.LabExperiment +import com.nagpal.shivam.vtucslab.utilities.StaticMethods.formatProgramName +import java.util.* + +class ContentAdapter( + private val context: Context, + private val labExperimentArrayList: ArrayList +) : RecyclerView.Adapter() { + private var itemClickHandler: ItemClickHandler? = null + fun setItemClickHandler(itemClickHandler: ItemClickHandler) { + this.itemClickHandler = itemClickHandler + } + + fun addAll(labExperiments: List) { + val i = labExperimentArrayList.size + labExperimentArrayList.addAll(labExperiments) + notifyItemRangeInserted(i, labExperiments.size) + } + + @SuppressLint("NotifyDataSetChanged") + fun clear() { + labExperimentArrayList.clear() + notifyDataSetChanged() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + VIEW_TYPE_SSP_SF -> { + val sspSfBinding = + DataBindingUtil.inflate( + LayoutInflater.from(context), + R.layout.layout_card_se_ssp_sf, + parent, + false + ) + SspSfViewHolder(sspSfBinding) + } + VIEW_TYPE_MSP_SF -> { + val mspSfBinding = + DataBindingUtil.inflate( + LayoutInflater.from(context), + R.layout.layout_card_se_msp, + parent, + false + ) + MspSfViewHolder(mspSfBinding) + } + VIEW_TYPE_SSP_MF -> { + val sspMfBinding = + DataBindingUtil.inflate( + LayoutInflater.from(context), + R.layout.layout_card_se_ssp_mf, + parent, + false + ) + SspMfViewHolder(sspMfBinding) + } + VIEW_TYPE_MSP_MF -> { + val seMspBinding = + DataBindingUtil.inflate( + LayoutInflater.from(context), + R.layout.layout_card_se_msp, + parent, + false + ) + MspMfViewHolder(seMspBinding) + } + else -> InvalidViewHolder(View(context)) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val labExperiment = labExperimentArrayList[position] + val serialOrder = processSerialOrder(labExperiment.serialOrder) + when (holder) { + is SspSfViewHolder -> { + holder.binding.serialOrder.text = serialOrder + val parts = + labExperiment.labExperimentSubParts[0].contentFiles[0].fileName.split("\\.".toRegex()) + .dropLastWhile { it.isEmpty() }.toTypedArray() + if (parts.size >= 2) { + holder.binding.programTitle.text = + formatProgramName(parts[parts.size - 2]) + } + } + is MspSfViewHolder -> { + holder.binding.serialOrder.text = serialOrder + val adapter = MultipleSubPartAdapter( + context, + labExperiment.labExperimentSubParts, + false, + itemClickHandler + ) + holder.binding.subPartContainer.adapter = adapter + } + is SspMfViewHolder -> { + holder.binding.serialOrder.text = serialOrder + val adapter = MultipleFileAdapter( + context, + R.layout.layout_card_single_files_without_sub_parts, + labExperiment.labExperimentSubParts[0].contentFiles, + itemClickHandler + ) + holder.binding.filesContainer.adapter = adapter + + } + is MspMfViewHolder -> { + holder.binding.serialOrder.text = serialOrder + val adapter = MultipleSubPartAdapter( + context, + labExperiment.labExperimentSubParts, + true, + itemClickHandler + ) + holder.binding.subPartContainer.adapter = adapter + } + } + } + + override fun getItemCount(): Int { + return labExperimentArrayList.size + } + + override fun getItemViewType(position: Int): Int { + val experiment = labExperimentArrayList[position] + var contentFileLength = 1 + val labExperimentSubParts = experiment.labExperimentSubParts + for (subPart in labExperimentSubParts) { + contentFileLength = contentFileLength.coerceAtLeast(subPart.contentFiles.size) + } + val labExperimentSubPartsSize = labExperimentSubParts.size + return if (labExperimentSubPartsSize == 1 && contentFileLength == 1) + VIEW_TYPE_SSP_SF + else if (labExperimentSubPartsSize > 1 && contentFileLength == 1) + VIEW_TYPE_MSP_SF + else if (labExperimentSubPartsSize == 1) + VIEW_TYPE_SSP_MF + else if (labExperimentSubPartsSize > 1) + VIEW_TYPE_MSP_MF + else VIEW_TYPE_INVALID + } + + private fun processSerialOrder(order: String?): String { + if (order == null) { + return "#" + } + return try { + val i = order.toInt() + i.toString() + } catch (e: NumberFormatException) { + order + } + } + + interface ItemClickHandler { + fun onContentFileClick(file: ContentFile) + } + + internal class InvalidViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) + internal inner class SspSfViewHolder(var binding: LayoutCardSeSspSfBinding) : + RecyclerView.ViewHolder( + binding.root + ), View.OnClickListener { + init { + binding.root.setOnClickListener(this) + } + + override fun onClick(view: View) { + itemClickHandler?.let { + val position = adapterPosition + val contentFile = + labExperimentArrayList[position].labExperimentSubParts[0].contentFiles[0] + it.onContentFileClick(contentFile) + } + } + } + + internal inner class MspSfViewHolder(var binding: LayoutCardSeMspBinding) : + RecyclerView.ViewHolder( + binding.root + ) { + init { + binding.subPartContainer.layoutManager = LinearLayoutManager(context) + binding.subPartContainer.setHasFixedSize(true) + } + } + + internal inner class SspMfViewHolder(var binding: LayoutCardSeSspMfBinding) : + RecyclerView.ViewHolder( + binding.root + ) { + init { + binding.filesContainer.layoutManager = LinearLayoutManager(context) + binding.filesContainer.setHasFixedSize(true) + } + } + + internal inner class MspMfViewHolder(var binding: LayoutCardSeMspBinding) : + RecyclerView.ViewHolder( + binding.root + ) { + init { + binding.subPartContainer.layoutManager = LinearLayoutManager(context) + binding.subPartContainer.setHasFixedSize(true) + } + } + + companion object { + private const val VIEW_TYPE_SSP_SF = 0 + private const val VIEW_TYPE_MSP_SF = 1 + private const val VIEW_TYPE_SSP_MF = 2 + private const val VIEW_TYPE_MSP_MF = 3 + private const val VIEW_TYPE_INVALID = -1 + } +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleFileAdapter.java b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleFileAdapter.java deleted file mode 100644 index 866bf61..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleFileAdapter.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.nagpal.shivam.vtucslab.adapters; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.databinding.DataBindingUtil; -import androidx.recyclerview.widget.RecyclerView; - -import com.nagpal.shivam.vtucslab.databinding.LayoutCardSingleFilesWithoutSubPartsBinding; -import com.nagpal.shivam.vtucslab.models.ContentFile; -import com.nagpal.shivam.vtucslab.utilities.StaticMethods; - -public class MultipleFileAdapter extends RecyclerView.Adapter { - private final Context mContext; - private final ContentFile[] mContentFiles; - private final ContentAdapter.ItemClickHandler mItemClickHandler; - @LayoutRes - private final int mLayoutId; - - public MultipleFileAdapter(Context context, @LayoutRes int layoutId, ContentFile[] contentFiles, ContentAdapter.ItemClickHandler itemClickHandler) { - mContext = context; - mLayoutId = layoutId; - mContentFiles = contentFiles; - mItemClickHandler = itemClickHandler; - } - - @NonNull - @Override - public ContentFileViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { - LayoutCardSingleFilesWithoutSubPartsBinding binding = DataBindingUtil.inflate(LayoutInflater.from(mContext), mLayoutId, viewGroup, false); - return new ContentFileViewHolder(binding); - } - - @Override - public void onBindViewHolder(@NonNull ContentFileViewHolder contentFileViewHolder, int i) { - String[] parts = mContentFiles[i].getFileName().split("\\."); - if (parts.length >= 2) { - contentFileViewHolder.mBinding.programTitle.setText(StaticMethods.formatProgramName((parts[parts.length - 2]))); - } - } - - @Override - public int getItemCount() { - if (mContentFiles != null) { - return mContentFiles.length; - } else { - return 0; - } - } - - class ContentFileViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { - - LayoutCardSingleFilesWithoutSubPartsBinding mBinding; - - ContentFileViewHolder(LayoutCardSingleFilesWithoutSubPartsBinding binding) { - super(binding.getRoot()); - mBinding = binding; - binding.getRoot().setOnClickListener(this); - } - - @Override - public void onClick(View v) { - if (mItemClickHandler != null) { - int position = getAdapterPosition(); - ContentFile file = mContentFiles[position]; - mItemClickHandler.onContentFileClick(file); - } - } - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleFileAdapter.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleFileAdapter.kt new file mode 100644 index 0000000..69eb8fc --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleFileAdapter.kt @@ -0,0 +1,57 @@ +package com.nagpal.shivam.vtucslab.adapters + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.RecyclerView +import com.nagpal.shivam.vtucslab.adapters.ContentAdapter.ItemClickHandler +import com.nagpal.shivam.vtucslab.adapters.MultipleFileAdapter.ContentFileViewHolder +import com.nagpal.shivam.vtucslab.databinding.LayoutCardSingleFilesWithoutSubPartsBinding +import com.nagpal.shivam.vtucslab.models.ContentFile +import com.nagpal.shivam.vtucslab.utilities.StaticMethods.formatProgramName + +class MultipleFileAdapter( + private val context: Context, + @field:LayoutRes @param:LayoutRes private val layoutId: Int, + private val contentFiles: List, + private val itemClickHandler: ItemClickHandler? +) : RecyclerView.Adapter() { + override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ContentFileViewHolder { + val binding = DataBindingUtil.inflate( + LayoutInflater.from(context), layoutId, viewGroup, false + ) + return ContentFileViewHolder(binding) + } + + override fun onBindViewHolder(contentFileViewHolder: ContentFileViewHolder, i: Int) { + val parts = + contentFiles[i].fileName.split("\\.".toRegex()).dropLastWhile { it.isEmpty() } + .toTypedArray() + if (parts.size >= 2) { + contentFileViewHolder.binding.programTitle.text = + formatProgramName(parts[parts.size - 2]) + } + } + + override fun getItemCount(): Int { + return contentFiles.size + } + + inner class ContentFileViewHolder(var binding: LayoutCardSingleFilesWithoutSubPartsBinding) : + RecyclerView.ViewHolder(binding.root), View.OnClickListener { + init { + binding.root.setOnClickListener(this) + } + + override fun onClick(v: View) { + itemClickHandler?.let { + val position = adapterPosition + val file = contentFiles[position] + it.onContentFileClick(file) + } + } + } +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleSubPartAdapter.java b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleSubPartAdapter.java deleted file mode 100644 index be16263..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleSubPartAdapter.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.nagpal.shivam.vtucslab.adapters; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.databinding.DataBindingUtil; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.nagpal.shivam.vtucslab.R; -import com.nagpal.shivam.vtucslab.databinding.LayoutCardSingleSubPartsWithFilesBinding; -import com.nagpal.shivam.vtucslab.databinding.LayoutCardSingleSubPartsWithoutFilesBinding; -import com.nagpal.shivam.vtucslab.models.ContentFile; -import com.nagpal.shivam.vtucslab.models.LabExperimentSubPart; -import com.nagpal.shivam.vtucslab.utilities.StaticMethods; - -public class MultipleSubPartAdapter extends RecyclerView.Adapter { - private final Context mContext; - private final LabExperimentSubPart[] mSubParts; - private final boolean mContainsMultipleFiles; - private final ContentAdapter.ItemClickHandler mItemClickHandler; - - public MultipleSubPartAdapter(Context context, LabExperimentSubPart[] subParts, boolean containsMultipleFiles, ContentAdapter.ItemClickHandler itemClickHandler) { - mContext = context; - mSubParts = subParts; - mContainsMultipleFiles = containsMultipleFiles; - mItemClickHandler = itemClickHandler; - } - - @NonNull - @Override - public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { - - if (!mContainsMultipleFiles) { - LayoutCardSingleSubPartsWithoutFilesBinding binding = DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout.layout_card_single_sub_parts_without_files, viewGroup, false); - return new SubPartWithoutFilesViewHolder(binding); - } else { - LayoutCardSingleSubPartsWithFilesBinding binding = DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout.layout_card_single_sub_parts_with_files, viewGroup, false); - return new SubPartWithFilesViewHolder(binding); - } - } - - @Override - public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int i) { - if (holder instanceof SubPartWithoutFilesViewHolder) { - SubPartWithoutFilesViewHolder subPartWithoutFilesViewHolder = (SubPartWithoutFilesViewHolder) holder; - subPartWithoutFilesViewHolder.mBinding.serialOrder.setText(mSubParts[i].getSubSerialOrder()); - String[] parts = mSubParts[i].getContentFiles()[0].getFileName().split("\\."); - if (parts.length >= 2) { - subPartWithoutFilesViewHolder.mBinding.programTitle.setText(StaticMethods.formatProgramName(parts[parts.length - 2])); - } - } else if (holder instanceof SubPartWithFilesViewHolder) { - SubPartWithFilesViewHolder subPartWithFilesViewHolder = (SubPartWithFilesViewHolder) holder; - subPartWithFilesViewHolder.mBinding.serialOrder.setText(mSubParts[i].getSubSerialOrder()); - MultipleFileAdapter adapter = new MultipleFileAdapter(mContext, R.layout.layout_card_single_files_without_sub_parts, mSubParts[i].getContentFiles(), mItemClickHandler); - subPartWithFilesViewHolder.mBinding.filesContainer.setAdapter(adapter); - } - } - - @Override - public int getItemCount() { - if (mSubParts != null) { - return mSubParts.length; - } - return 0; - } - - class SubPartWithoutFilesViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { - - LayoutCardSingleSubPartsWithoutFilesBinding mBinding; - - SubPartWithoutFilesViewHolder(LayoutCardSingleSubPartsWithoutFilesBinding binding) { - super(binding.getRoot()); - mBinding = binding; - binding.getRoot().setOnClickListener(this); - } - - @Override - public void onClick(View v) { - if (mItemClickHandler != null) { - int position = getAdapterPosition(); - ContentFile contentFile = mSubParts[position].getContentFiles()[0]; - mItemClickHandler.onContentFileClick(contentFile); - } - } - } - - class SubPartWithFilesViewHolder extends RecyclerView.ViewHolder { - - LayoutCardSingleSubPartsWithFilesBinding mBinding; - - SubPartWithFilesViewHolder(LayoutCardSingleSubPartsWithFilesBinding binding) { - super(binding.getRoot()); - mBinding = binding; - mBinding.filesContainer.setLayoutManager(new LinearLayoutManager(mContext)); - mBinding.filesContainer.setHasFixedSize(true); - } - - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleSubPartAdapter.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleSubPartAdapter.kt new file mode 100644 index 0000000..099a788 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleSubPartAdapter.kt @@ -0,0 +1,100 @@ +package com.nagpal.shivam.vtucslab.adapters + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.nagpal.shivam.vtucslab.R +import com.nagpal.shivam.vtucslab.adapters.ContentAdapter.ItemClickHandler +import com.nagpal.shivam.vtucslab.databinding.LayoutCardSingleSubPartsWithFilesBinding +import com.nagpal.shivam.vtucslab.databinding.LayoutCardSingleSubPartsWithoutFilesBinding +import com.nagpal.shivam.vtucslab.models.LabExperimentSubPart +import com.nagpal.shivam.vtucslab.utilities.StaticMethods.formatProgramName + +class MultipleSubPartAdapter( + private val context: Context, + private val subParts: List, + private val containsMultipleFiles: Boolean, + private val itemClickHandler: ItemClickHandler? +) : RecyclerView.Adapter() { + override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): RecyclerView.ViewHolder { + return if (!containsMultipleFiles) { + val binding = + DataBindingUtil.inflate( + LayoutInflater.from(context), + R.layout.layout_card_single_sub_parts_without_files, + viewGroup, + false + ) + SubPartWithoutFilesViewHolder(binding) + } else { + val binding = + DataBindingUtil.inflate( + LayoutInflater.from(context), + R.layout.layout_card_single_sub_parts_with_files, + viewGroup, + false + ) + SubPartWithFilesViewHolder(binding) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, i: Int) { + when (holder) { + is SubPartWithoutFilesViewHolder -> { + holder.binding.serialOrder.text = subParts[i].subSerialOrder + val parts = subParts[i].contentFiles[0].fileName.split("\\.".toRegex()) + .dropLastWhile { it.isEmpty() }.toTypedArray() + if (parts.size >= 2) { + holder.binding.programTitle.text = formatProgramName( + parts[parts.size - 2] + ) + } + } + is SubPartWithFilesViewHolder -> { + holder.binding.serialOrder.text = subParts[i].subSerialOrder + val adapter = MultipleFileAdapter( + context, + R.layout.layout_card_single_files_without_sub_parts, + subParts[i].contentFiles, + itemClickHandler + ) + holder.binding.filesContainer.adapter = adapter + } + } + } + + override fun getItemCount(): Int { + return subParts.size + } + + internal inner class SubPartWithoutFilesViewHolder(var binding: LayoutCardSingleSubPartsWithoutFilesBinding) : + RecyclerView.ViewHolder( + binding.root + ), View.OnClickListener { + init { + binding.root.setOnClickListener(this) + } + + override fun onClick(v: View) { + itemClickHandler?.let { + val position = adapterPosition + val contentFile = subParts[position].contentFiles[0] + it.onContentFileClick(contentFile) + } + } + } + + internal inner class SubPartWithFilesViewHolder(var binding: LayoutCardSingleSubPartsWithFilesBinding) : + RecyclerView.ViewHolder( + binding.root + ) { + init { + binding.filesContainer.layoutManager = LinearLayoutManager(context) + binding.filesContainer.setHasFixedSize(true) + } + } +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/NavigationAdapter.java b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/NavigationAdapter.java deleted file mode 100644 index 2561b6f..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/NavigationAdapter.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.nagpal.shivam.vtucslab.adapters; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; - -import com.nagpal.shivam.vtucslab.R; -import com.nagpal.shivam.vtucslab.models.Laboratory; - -import java.util.ArrayList; -import java.util.Arrays; - -public class NavigationAdapter extends RecyclerView.Adapter { - - private final Context mContext; - private final ArrayList mLaboratoryArrayList; - private NavigationAdapterItemClickHandler mNavigationAdapterItemClickHandler; - - public NavigationAdapter(@NonNull Context context, @NonNull ArrayList laboratoryArrayList) { - mContext = context; - mLaboratoryArrayList = laboratoryArrayList; - } - - public void addAll(Laboratory[] laboratories) { - int i = mLaboratoryArrayList.size(); - mLaboratoryArrayList.addAll(Arrays.asList(laboratories)); - notifyItemRangeInserted(i, laboratories.length); - } - - @SuppressLint("NotifyDataSetChanged") - public void clear() { - mLaboratoryArrayList.clear(); - notifyDataSetChanged(); - } - - public void setNavigationAdapterItemClickHandler(NavigationAdapterItemClickHandler navigationAdapterItemClickHandler) { - mNavigationAdapterItemClickHandler = navigationAdapterItemClickHandler; - } - - @NonNull - @Override - public NavigationViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(mContext).inflate(R.layout.layout_card_repository, parent, false); - return new NavigationViewHolder(view); - } - - @Override - public void onBindViewHolder(@NonNull NavigationViewHolder holder, int position) { - Laboratory laboratory = mLaboratoryArrayList.get(position); - String fileName = laboratory.getFileName(); - String[] parts = fileName.split("\\."); - laboratory.setTitle(parts[0].replace('_', ' ')); - holder.mTextView.setText(laboratory.getTitle()); - } - - @Override - public int getItemCount() { - return mLaboratoryArrayList.size(); - } - - public interface NavigationAdapterItemClickHandler { - void onNavigationAdapterItemClick(Laboratory laboratory, int i); - } - - class NavigationViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { - TextView mTextView; - - NavigationViewHolder(View itemView) { - super(itemView); - mTextView = itemView.findViewById(R.id.text_view_layout); - itemView.setOnClickListener(this); - } - - @Override - public void onClick(View view) { - if (mNavigationAdapterItemClickHandler != null) { - int i = getAdapterPosition(); - mNavigationAdapterItemClickHandler.onNavigationAdapterItemClick(mLaboratoryArrayList.get(i), i); - } - } - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/NavigationAdapter.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/NavigationAdapter.kt new file mode 100644 index 0000000..4f86158 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/NavigationAdapter.kt @@ -0,0 +1,74 @@ +package com.nagpal.shivam.vtucslab.adapters + +import android.annotation.SuppressLint +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.nagpal.shivam.vtucslab.R +import com.nagpal.shivam.vtucslab.adapters.NavigationAdapter.NavigationViewHolder +import com.nagpal.shivam.vtucslab.models.Laboratory +import java.util.* + +class NavigationAdapter( + private val context: Context, + private val laboratoryArrayList: ArrayList +) : RecyclerView.Adapter() { + private var navigationAdapterItemClickHandler: NavigationAdapterItemClickHandler? = null + fun addAll(laboratories: List) { + val i = laboratoryArrayList.size + laboratoryArrayList.addAll(laboratories) + notifyItemRangeInserted(i, laboratories.size) + } + + @SuppressLint("NotifyDataSetChanged") + fun clear() { + laboratoryArrayList.clear() + notifyDataSetChanged() + } + + fun setNavigationAdapterItemClickHandler(navigationAdapterItemClickHandler: NavigationAdapterItemClickHandler?) { + this.navigationAdapterItemClickHandler = navigationAdapterItemClickHandler + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NavigationViewHolder { + val view = + LayoutInflater.from(context).inflate(R.layout.layout_card_repository, parent, false) + return NavigationViewHolder(view) + } + + override fun onBindViewHolder(holder: NavigationViewHolder, position: Int) { + val laboratory = laboratoryArrayList[position] + val fileName = laboratory.fileName + val parts = fileName.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + laboratory.title = parts[0].replace('_', ' ') + holder.textView.text = laboratory.title + } + + override fun getItemCount(): Int { + return laboratoryArrayList.size + } + + interface NavigationAdapterItemClickHandler { + fun onNavigationAdapterItemClick(laboratory: Laboratory, i: Int) + } + + inner class NavigationViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), + View.OnClickListener { + var textView: TextView + + init { + textView = itemView.findViewById(R.id.text_view_layout) + itemView.setOnClickListener(this) + } + + override fun onClick(view: View) { + navigationAdapterItemClickHandler?.let { + val i = adapterPosition + it.onNavigationAdapterItemClick(laboratoryArrayList[i], i) + } + } + } +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/models/ContentFile.java b/app/src/main/java/com/nagpal/shivam/vtucslab/models/ContentFile.java deleted file mode 100644 index 4dce005..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/models/ContentFile.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.nagpal.shivam.vtucslab.models; - -public class ContentFile { - private String fileName; - - public String getFileName() { - return fileName; - } - - public void setFileName(String fileName) { - this.fileName = fileName; - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/models/ContentFile.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/models/ContentFile.kt new file mode 100644 index 0000000..664538a --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/models/ContentFile.kt @@ -0,0 +1,5 @@ +package com.nagpal.shivam.vtucslab.models + +data class ContentFile( + var fileName: String, +) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperiment.java b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperiment.java deleted file mode 100644 index 1a04d5a..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperiment.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.nagpal.shivam.vtucslab.models; - -public class LabExperiment { - - private String serialOrder; - private LabExperimentSubPart[] labExperimentSubParts; - - public String getSerialOrder() { - return serialOrder; - } - - public LabExperiment setSerialOrder(String serialOrder) { - this.serialOrder = serialOrder; - return this; - } - - public LabExperimentSubPart[] getLabExperimentSubParts() { - return labExperimentSubParts; - } - - public LabExperiment setLabExperimentSubParts(LabExperimentSubPart[] labExperimentSubParts) { - this.labExperimentSubParts = labExperimentSubParts; - return this; - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperiment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperiment.kt new file mode 100644 index 0000000..92b3ccb --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperiment.kt @@ -0,0 +1,6 @@ +package com.nagpal.shivam.vtucslab.models + +data class LabExperiment( + var serialOrder: String, + var labExperimentSubParts: List, +) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperimentSubPart.java b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperimentSubPart.java deleted file mode 100644 index eef8030..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperimentSubPart.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.nagpal.shivam.vtucslab.models; - -public class LabExperimentSubPart { - - private String subSerialOrder; - private ContentFile[] contentFiles; - - public String getSubSerialOrder() { - return subSerialOrder; - } - - public LabExperimentSubPart setSubSerialOrder(String subSerialOrder) { - this.subSerialOrder = subSerialOrder; - return this; - } - - public ContentFile[] getContentFiles() { - return contentFiles; - } - - public LabExperimentSubPart setContentFiles(ContentFile[] contentFiles) { - this.contentFiles = contentFiles; - return this; - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperimentSubPart.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperimentSubPart.kt new file mode 100644 index 0000000..80d2c34 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabExperimentSubPart.kt @@ -0,0 +1,6 @@ +package com.nagpal.shivam.vtucslab.models + +data class LabExperimentSubPart( + var subSerialOrder: String?, + var contentFiles: List, +) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabResponse.java b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabResponse.java deleted file mode 100644 index 2af9474..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/models/LabResponse.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.nagpal.shivam.vtucslab.models; - -public class LabResponse { - private String context; - private boolean isValid = true; - private String invalidationMessage; - private Laboratory[] laboratories; - private LabExperiment[] labExperiments; - private String github_raw_content; - private String organization; - private String repository; - private String branch; - - public String getContext() { - return context; - } - - public void setContext(String context) { - this.context = context; - } - - public boolean isValid() { - return isValid; - } - - public void setValid(boolean valid) { - isValid = valid; - } - - public String getInvalidationMessage() { - return invalidationMessage; - } - - public void setInvalidationMessage(String invalidationMessage) { - this.invalidationMessage = invalidationMessage; - } - - public LabExperiment[] getLabExperiments() { - return labExperiments; - } - - public LabResponse setLabExperiments(LabExperiment[] labExperiments) { - this.labExperiments = labExperiments; - return this; - } - - public Laboratory[] getLaboratories() { - return laboratories; - } - - public LabResponse setLaboratories(Laboratory[] laboratories) { - this.laboratories = laboratories; - return this; - } - - public String getGithub_raw_content() { - return github_raw_content; - } - - public void setGithub_raw_content(String github_raw_content) { - this.github_raw_content = github_raw_content; - } - - public String getOrganization() { - return organization; - } - - public void setOrganization(String organization) { - this.organization = organization; - } - - public String getRepository() { - return repository; - } - - public void setRepository(String repository) { - this.repository = repository; - } - - public String getBranch() { - return branch; - } - - public void setBranch(String branch) { - this.branch = branch; - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/models/Laboratory.java b/app/src/main/java/com/nagpal/shivam/vtucslab/models/Laboratory.java deleted file mode 100644 index 8cd086c..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/models/Laboratory.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.nagpal.shivam.vtucslab.models; - -import java.io.Serializable; - -public class Laboratory implements Serializable { - private String title; - private String fileName; - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getFileName() { - return fileName; - } - - public void setFileName(String fileName) { - this.fileName = fileName; - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/models/Laboratory.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/models/Laboratory.kt new file mode 100644 index 0000000..3c8cc29 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/models/Laboratory.kt @@ -0,0 +1,6 @@ +package com.nagpal.shivam.vtucslab.models + +data class Laboratory( + var title: String?, + var fileName: String, +) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/models/LaboratoryExperimentResponse.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LaboratoryExperimentResponse.kt new file mode 100644 index 0000000..3611981 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LaboratoryExperimentResponse.kt @@ -0,0 +1,14 @@ +package com.nagpal.shivam.vtucslab.models + +import com.fasterxml.jackson.annotation.JsonAlias +import com.nagpal.shivam.vtucslab.utilities.Constants + +data class LaboratoryExperimentResponse( + @field:JsonAlias(Constants.GITHUB_RAW_CONTENT) val githubRawContent: String, + val organization: String, + val repository: String, + val branch: String, + val isValid: Boolean = true, + val labExperiments: List, + val invalidationMessage: String? = null, +) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/models/LaboratoryResponse.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LaboratoryResponse.kt new file mode 100644 index 0000000..f41cc2e --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/models/LaboratoryResponse.kt @@ -0,0 +1,14 @@ +package com.nagpal.shivam.vtucslab.models + +import com.fasterxml.jackson.annotation.JsonAlias +import com.nagpal.shivam.vtucslab.utilities.Constants + +data class LaboratoryResponse( + @field:JsonAlias(Constants.GITHUB_RAW_CONTENT) val githubRawContent: String, + val organization: String, + val repository: String, + val branch: String, + val isValid: Boolean = true, + val laboratories: List, + val invalidationMessage: String? = null, +) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/LabResponseViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/LabResponseViewModel.kt deleted file mode 100644 index 740bc35..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/LabResponseViewModel.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.nagpal.shivam.vtucslab.screens - -import android.app.Application -import androidx.lifecycle.AndroidViewModel -import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.models.LabResponse -import com.nagpal.shivam.vtucslab.services.VtuCsLabService -import com.nagpal.shivam.vtucslab.ui.state.LabResponseState -import com.nagpal.shivam.vtucslab.utilities.Constants -import com.nagpal.shivam.vtucslab.utilities.NetworkUtils -import com.nagpal.shivam.vtucslab.utilities.Stages -import com.nagpal.shivam.vtucslab.utilities.StaticMethods -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response - - -open class LabResponseViewModel(app: Application) : AndroidViewModel(app) { - private val initialState = LabResponseState(Stages.LOADING, null, null, null) - private val _uiState = MutableStateFlow(initialState) - private val application = this.getApplication() - val uiState: StateFlow = _uiState.asStateFlow() - - fun loadContent(url: String) { - if (_uiState.value.stage == Stages.SUCCEEDED) { - return - } - if (!NetworkUtils.isNetworkConnected(application)) { - _uiState.update { - LabResponseState( - Stages.FAILED, - null, - Constants.NO_ACTIVE_NETWORK, - null - ) - } - return - } - _uiState.update { initialState } - VtuCsLabService.instance.getLabResponse(url) - .enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - _uiState.update { - val labResponse = response.body()!! - LabResponseState( - Stages.SUCCEEDED, - labResponse, - null, - StaticMethods.getBaseURL(labResponse) - ) - } - } - - override fun onFailure(call: Call, t: Throwable) { - _uiState.update { - LabResponseState(Stages.FAILED, null, null, null) - } - } - }) - } - -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayState.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayState.kt new file mode 100644 index 0000000..7c0963b --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayState.kt @@ -0,0 +1,7 @@ +package com.nagpal.shivam.vtucslab.screens.display + +data class DisplayState( + val stage: String, + val response: String?, + val message: String?, +) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt index 618a7e5..3e4c814 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt @@ -14,16 +14,10 @@ import retrofit2.Call import retrofit2.Callback import retrofit2.Response -data class DisplayResponseState( - val stage: String, - val response: String?, - val message: String?, -) - class DisplayViewModel(app: Application) : AndroidViewModel(app) { var scrollX = 0 var scrollY = 0 - private val initialState = DisplayResponseState(Stages.LOADING, null, null) + private val initialState = DisplayState(Stages.LOADING, null, null) private val _uiState = MutableStateFlow(initialState) val uiState = _uiState.asStateFlow() @@ -36,7 +30,7 @@ class DisplayViewModel(app: Application) : AndroidViewModel(app) { } if (!NetworkUtils.isNetworkConnected(application)) { _uiState.update { - DisplayResponseState( + DisplayState( Stages.FAILED, null, Constants.NO_ACTIVE_NETWORK, @@ -50,7 +44,7 @@ class DisplayViewModel(app: Application) : AndroidViewModel(app) { override fun onResponse(call: Call, response: Response) { _uiState.update { val stringResponse = response.body()!!.replace("\t", "\t\t") - DisplayResponseState( + DisplayState( Stages.SUCCEEDED, stringResponse, null, @@ -60,7 +54,7 @@ class DisplayViewModel(app: Application) : AndroidViewModel(app) { override fun onFailure(call: Call, t: Throwable) { _uiState.update { - DisplayResponseState(Stages.FAILED, null, null) + DisplayState(Stages.FAILED, null, null) } } }) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt index 4443012..e669595 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt @@ -15,6 +15,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.adapters.ContentAdapter import com.nagpal.shivam.vtucslab.databinding.FragmentProgramBinding +import com.nagpal.shivam.vtucslab.models.ContentFile import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.launch @@ -49,11 +50,11 @@ class ProgramFragment : Fragment() { binding.progressBar.visibility = View.VISIBLE } Stages.SUCCEEDED -> { - if (it.labResponse!!.isValid) { + if (it.laboratoryExperimentResponse!!.isValid) { contentAdapter.clear() - contentAdapter.addAll(it.labResponse.labExperiments) + contentAdapter.addAll(it.laboratoryExperimentResponse.labExperiments) } else { - showErrorMessage(it.labResponse.invalidationMessage) + showErrorMessage(it.laboratoryExperimentResponse.invalidationMessage) } } Stages.FAILED -> { @@ -71,22 +72,26 @@ class ProgramFragment : Fragment() { return binding.root } - private fun showErrorMessage(message: String) { + // TODO: Duplicate: Move to a static method + private fun showErrorMessage(message: String?) { binding.emptyTextView.visibility = View.VISIBLE binding.emptyTextView.text = message } private fun setupRepositoryAdapter() { contentAdapter = ContentAdapter(requireContext(), ArrayList()) - contentAdapter.setItemClickHandler { - val actionProgramFragmentToDisplayFragment = - ProgramFragmentDirections.actionProgramFragmentToDisplayFragment( - viewModel.uiState.value.baseUrl!!, - it.fileName, - it.fileName - ) - findNavController().navigate(actionProgramFragmentToDisplayFragment) - } + contentAdapter.setItemClickHandler(object : ContentAdapter.ItemClickHandler { + override fun onContentFileClick(file: ContentFile) { + val actionProgramFragmentToDisplayFragment = + ProgramFragmentDirections.actionProgramFragmentToDisplayFragment( + viewModel.uiState.value.baseUrl!!, + file.fileName, + file.fileName + ) + findNavController().navigate(actionProgramFragmentToDisplayFragment) + } + + }) binding.recyclerView.adapter = contentAdapter } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramState.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramState.kt new file mode 100644 index 0000000..5ab3f55 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramState.kt @@ -0,0 +1,10 @@ +package com.nagpal.shivam.vtucslab.screens.programs + +import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse + +data class ProgramState( + val stage: String, + val laboratoryExperimentResponse: LaboratoryExperimentResponse?, + val message: String?, + val baseUrl: String? +) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt index b83303a..5f9ac27 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt @@ -1,6 +1,67 @@ package com.nagpal.shivam.vtucslab.screens.programs import android.app.Application -import com.nagpal.shivam.vtucslab.screens.LabResponseViewModel +import androidx.lifecycle.AndroidViewModel +import com.nagpal.shivam.vtucslab.VTUCSLabApplication +import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse +import com.nagpal.shivam.vtucslab.services.VtuCsLabService +import com.nagpal.shivam.vtucslab.utilities.Constants +import com.nagpal.shivam.vtucslab.utilities.NetworkUtils +import com.nagpal.shivam.vtucslab.utilities.Stages +import com.nagpal.shivam.vtucslab.utilities.StaticMethods +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response -class ProgramViewModel(app: Application) : LabResponseViewModel(app) +class ProgramViewModel(app: Application) : AndroidViewModel(app) { + private val initialState = ProgramState(Stages.LOADING, null, null, null) + private val _uiState = MutableStateFlow(initialState) + private val application = this.getApplication() + val uiState: StateFlow = _uiState.asStateFlow() + + fun loadContent(url: String) { + if (_uiState.value.stage == Stages.SUCCEEDED) { + return + } + if (!NetworkUtils.isNetworkConnected(application)) { + _uiState.update { + ProgramState( + Stages.FAILED, + null, + Constants.NO_ACTIVE_NETWORK, + null + ) + } + return + } + _uiState.update { initialState } + VtuCsLabService.instance.getLaboratoryExperimentsResponse(url) + .enqueue(object : Callback { + override fun onResponse( + call: Call, + response: Response + ) { + _uiState.update { + val labResponse = response.body()!! + ProgramState( + Stages.SUCCEEDED, + labResponse, + null, + StaticMethods.getBaseURL(labResponse) + ) + } + } + + override fun onFailure(call: Call, t: Throwable) { + _uiState.update { + ProgramState(Stages.FAILED, null, null, null) + } + } + }) + } + +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt index 53d8a78..c299c3f 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt @@ -15,6 +15,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.adapters.NavigationAdapter import com.nagpal.shivam.vtucslab.databinding.FragmentRepositoryBinding +import com.nagpal.shivam.vtucslab.models.Laboratory import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.launch @@ -47,11 +48,11 @@ class RepositoryFragment : Fragment() { binding.progressBar.visibility = View.VISIBLE } Stages.SUCCEEDED -> { - if (it.labResponse!!.isValid) { + if (it.laboratoryResponse!!.isValid) { navigationAdapter.clear() - navigationAdapter.addAll(it.labResponse.laboratories) + navigationAdapter.addAll(it.laboratoryResponse.laboratories) } else { - showErrorMessage(it.labResponse.invalidationMessage) + showErrorMessage(it.laboratoryResponse.invalidationMessage) } } Stages.FAILED -> { @@ -68,7 +69,8 @@ class RepositoryFragment : Fragment() { return binding.root } - private fun showErrorMessage(message: String) { + // TODO: Duplicate: Move to a static method + private fun showErrorMessage(message: String?) { binding.emptyTextView.visibility = View.VISIBLE binding.emptyTextView.text = message } @@ -86,15 +88,19 @@ class RepositoryFragment : Fragment() { private fun setupRepositoryAdapter() { navigationAdapter = NavigationAdapter(requireContext(), ArrayList()) - navigationAdapter.setNavigationAdapterItemClickHandler { laboratory, _ -> - val actionRepositoryFragmentToProgramFragment = - RepositoryFragmentDirections.actionRepositoryFragmentToProgramFragment( - viewModel.uiState.value.baseUrl!!, - laboratory.fileName, - laboratory.title - ) - findNavController().navigate(actionRepositoryFragmentToProgramFragment) - } + navigationAdapter.setNavigationAdapterItemClickHandler(object : + NavigationAdapter.NavigationAdapterItemClickHandler { + override fun onNavigationAdapterItemClick(laboratory: Laboratory, i: Int) { + val actionRepositoryFragmentToProgramFragment = + RepositoryFragmentDirections.actionRepositoryFragmentToProgramFragment( + viewModel.uiState.value.baseUrl!!, + laboratory.fileName, + laboratory.title.orEmpty(), + ) + findNavController().navigate(actionRepositoryFragmentToProgramFragment) + } + + }) binding.repositoryRecyclerView.adapter = navigationAdapter } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryState.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryState.kt new file mode 100644 index 0000000..1710867 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryState.kt @@ -0,0 +1,10 @@ +package com.nagpal.shivam.vtucslab.screens.repository + +import com.nagpal.shivam.vtucslab.models.LaboratoryResponse + +data class RepositoryState( + val stage: String, + val laboratoryResponse: LaboratoryResponse?, + val message: String?, + val baseUrl: String? +) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt index f71b6df..288b945 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -1,6 +1,67 @@ package com.nagpal.shivam.vtucslab.screens.repository import android.app.Application -import com.nagpal.shivam.vtucslab.screens.LabResponseViewModel +import androidx.lifecycle.AndroidViewModel +import com.nagpal.shivam.vtucslab.VTUCSLabApplication +import com.nagpal.shivam.vtucslab.models.LaboratoryResponse +import com.nagpal.shivam.vtucslab.services.VtuCsLabService +import com.nagpal.shivam.vtucslab.utilities.Constants +import com.nagpal.shivam.vtucslab.utilities.NetworkUtils +import com.nagpal.shivam.vtucslab.utilities.Stages +import com.nagpal.shivam.vtucslab.utilities.StaticMethods +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response -class RepositoryViewModel(app: Application) : LabResponseViewModel(app) +class RepositoryViewModel(app: Application) : AndroidViewModel(app) { + private val initialState = RepositoryState(Stages.LOADING, null, null, null) + private val _uiState = MutableStateFlow(initialState) + private val application = this.getApplication() + val uiState: StateFlow = _uiState.asStateFlow() + + fun loadContent(url: String) { + if (_uiState.value.stage == Stages.SUCCEEDED) { + return + } + if (!NetworkUtils.isNetworkConnected(application)) { + _uiState.update { + RepositoryState( + Stages.FAILED, + null, + Constants.NO_ACTIVE_NETWORK, + null + ) + } + return + } + _uiState.update { initialState } + VtuCsLabService.instance.getLaboratoryResponse(url) + .enqueue(object : Callback { + override fun onResponse( + call: Call, + response: Response + ) { + _uiState.update { + val labResponse = response.body()!! + RepositoryState( + Stages.SUCCEEDED, + labResponse, + null, + StaticMethods.getBaseURL(labResponse) + ) + } + } + + override fun onFailure(call: Call, t: Throwable) { + _uiState.update { + RepositoryState(Stages.FAILED, null, null, null) + } + } + }) + } + +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt index 56e3a6c..07c458c 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt @@ -1,6 +1,7 @@ package com.nagpal.shivam.vtucslab.services -import com.nagpal.shivam.vtucslab.models.LabResponse +import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse +import com.nagpal.shivam.vtucslab.models.LaboratoryResponse import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.StaticMethods import retrofit2.Call @@ -8,8 +9,12 @@ import retrofit2.http.GET import retrofit2.http.Url interface VtuCsLabService { + + @GET + fun getLaboratoryResponse(@Url url: String): Call + @GET - fun getLabResponse(@Url url: String): Call + fun getLaboratoryExperimentsResponse(@Url url: String): Call @GET fun fetchRawResponse(@Url url: String): Call diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/ui/state/NetworkRequestState.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/ui/state/NetworkRequestState.kt deleted file mode 100644 index a8eb305..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/ui/state/NetworkRequestState.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.nagpal.shivam.vtucslab.ui.state - -import com.nagpal.shivam.vtucslab.models.LabResponse - -data class LabResponseState( - val stage: String, - val labResponse: LabResponse?, - val message: String?, - val baseUrl: String? -) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt index 6a5be96..729d5f3 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt @@ -1,8 +1,6 @@ package com.nagpal.shivam.vtucslab.utilities object Constants { - const val TITLE_INTENT_TAG = "title" - const val URL_INTENT_TAG = "url" const val GITHUB_RAW_BASE_URL = "https://raw.githubusercontent.com" const val PRIVACY_POLICY_LINK = "https://github.com/ShivamNagpal/Privacy_Policies/blob/master/VTU_CS_LAB_MANUAL.md" @@ -10,4 +8,5 @@ object Constants { "https://raw.githubusercontent.com/vtucs/Index_v3/master/Index_v3.json" const val NO_ACTIVE_NETWORK = "NO_ACTIVE_NETWORK" const val LABEL_CODE = "Code" + const val GITHUB_RAW_CONTENT = "github_raw_content" } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt index 6c7c0fc..e3883d6 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt @@ -1,12 +1,21 @@ package com.nagpal.shivam.vtucslab.utilities -import com.nagpal.shivam.vtucslab.models.LabResponse +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.json.JsonMapper +import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse +import com.nagpal.shivam.vtucslab.models.LaboratoryResponse import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.jackson.JacksonConverterFactory import retrofit2.converter.scalars.ScalarsConverterFactory object StaticMethods { - @JvmStatic + + private val jsonMapper: JsonMapper by lazy { + com.fasterxml.jackson.module.kotlin.jacksonMapperBuilder() + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .build() + } + fun formatProgramName(programName: String): String { return programName.replace('_', ' ') } @@ -14,10 +23,14 @@ object StaticMethods { fun getRetrofitBuilder(): Retrofit.Builder { return Retrofit.Builder() .addConverterFactory(ScalarsConverterFactory.create()) - .addConverterFactory(GsonConverterFactory.create()) + .addConverterFactory(JacksonConverterFactory.create(jsonMapper)) + } + + fun getBaseURL(labResponse: LaboratoryResponse): String { + return "${labResponse.githubRawContent}/${labResponse.organization}/${labResponse.repository}/${labResponse.branch}" } - fun getBaseURL(labResponse: LabResponse): String { - return "${labResponse.github_raw_content}/${labResponse.organization}/${labResponse.repository}/${labResponse.branch}" + fun getBaseURL(laboratoryExperimentResponse: LaboratoryExperimentResponse): String { + return "${laboratoryExperimentResponse.githubRawContent}/${laboratoryExperimentResponse.organization}/${laboratoryExperimentResponse.repository}/${laboratoryExperimentResponse.branch}" } } From 0f2cc1752827f2bec8b6509a89448c33e1214e7b Mon Sep 17 00:00:00 2001 From: WallE Date: Sat, 25 Mar 2023 01:53:19 +0530 Subject: [PATCH 28/50] cleanup: Remove DataBinding in favor of ViewBinding and modify the remaining views --- app/build.gradle | 1 - .../vtucslab/adapters/ContentAdapter.kt | 16 +--- .../vtucslab/adapters/MultipleFileAdapter.kt | 7 +- .../adapters/MultipleSubPartAdapter.kt | 9 +- .../vtucslab/adapters/NavigationAdapter.kt | 1 - .../main/res/layout/layout_card_se_msp.xml | 71 +++++++-------- .../main/res/layout/layout_card_se_ssp_mf.xml | 71 +++++++-------- .../main/res/layout/layout_card_se_ssp_sf.xml | 89 +++++++++---------- ...ut_card_single_files_without_sub_parts.xml | 74 +++++++-------- ...ayout_card_single_sub_parts_with_files.xml | 81 ++++++++--------- ...ut_card_single_sub_parts_without_files.xml | 69 +++++++------- 11 files changed, 225 insertions(+), 264 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 07d62e1..e46f699 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,7 +20,6 @@ android { } } buildFeatures { - dataBinding true // TODO: Remove DataBinding post cleanup viewBinding true } namespace 'com.nagpal.shivam.vtucslab' diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/ContentAdapter.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/ContentAdapter.kt index 1f3b39a..77b27a9 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/ContentAdapter.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/ContentAdapter.kt @@ -5,17 +5,14 @@ import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.databinding.DataBindingUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.databinding.LayoutCardSeMspBinding import com.nagpal.shivam.vtucslab.databinding.LayoutCardSeSspMfBinding import com.nagpal.shivam.vtucslab.databinding.LayoutCardSeSspSfBinding import com.nagpal.shivam.vtucslab.models.ContentFile import com.nagpal.shivam.vtucslab.models.LabExperiment import com.nagpal.shivam.vtucslab.utilities.StaticMethods.formatProgramName -import java.util.* class ContentAdapter( private val context: Context, @@ -42,9 +39,8 @@ class ContentAdapter( return when (viewType) { VIEW_TYPE_SSP_SF -> { val sspSfBinding = - DataBindingUtil.inflate( + LayoutCardSeSspSfBinding.inflate( LayoutInflater.from(context), - R.layout.layout_card_se_ssp_sf, parent, false ) @@ -52,9 +48,8 @@ class ContentAdapter( } VIEW_TYPE_MSP_SF -> { val mspSfBinding = - DataBindingUtil.inflate( + LayoutCardSeMspBinding.inflate( LayoutInflater.from(context), - R.layout.layout_card_se_msp, parent, false ) @@ -62,9 +57,8 @@ class ContentAdapter( } VIEW_TYPE_SSP_MF -> { val sspMfBinding = - DataBindingUtil.inflate( + LayoutCardSeSspMfBinding.inflate( LayoutInflater.from(context), - R.layout.layout_card_se_ssp_mf, parent, false ) @@ -72,9 +66,8 @@ class ContentAdapter( } VIEW_TYPE_MSP_MF -> { val seMspBinding = - DataBindingUtil.inflate( + LayoutCardSeMspBinding.inflate( LayoutInflater.from(context), - R.layout.layout_card_se_msp, parent, false ) @@ -112,7 +105,6 @@ class ContentAdapter( holder.binding.serialOrder.text = serialOrder val adapter = MultipleFileAdapter( context, - R.layout.layout_card_single_files_without_sub_parts, labExperiment.labExperimentSubParts[0].contentFiles, itemClickHandler ) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleFileAdapter.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleFileAdapter.kt index 69eb8fc..556f467 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleFileAdapter.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleFileAdapter.kt @@ -4,8 +4,6 @@ import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.annotation.LayoutRes -import androidx.databinding.DataBindingUtil import androidx.recyclerview.widget.RecyclerView import com.nagpal.shivam.vtucslab.adapters.ContentAdapter.ItemClickHandler import com.nagpal.shivam.vtucslab.adapters.MultipleFileAdapter.ContentFileViewHolder @@ -15,13 +13,12 @@ import com.nagpal.shivam.vtucslab.utilities.StaticMethods.formatProgramName class MultipleFileAdapter( private val context: Context, - @field:LayoutRes @param:LayoutRes private val layoutId: Int, private val contentFiles: List, private val itemClickHandler: ItemClickHandler? ) : RecyclerView.Adapter() { override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ContentFileViewHolder { - val binding = DataBindingUtil.inflate( - LayoutInflater.from(context), layoutId, viewGroup, false + val binding = LayoutCardSingleFilesWithoutSubPartsBinding.inflate( + LayoutInflater.from(context), viewGroup, false ) return ContentFileViewHolder(binding) } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleSubPartAdapter.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleSubPartAdapter.kt index 099a788..93a1b26 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleSubPartAdapter.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/MultipleSubPartAdapter.kt @@ -4,10 +4,8 @@ import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.databinding.DataBindingUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.adapters.ContentAdapter.ItemClickHandler import com.nagpal.shivam.vtucslab.databinding.LayoutCardSingleSubPartsWithFilesBinding import com.nagpal.shivam.vtucslab.databinding.LayoutCardSingleSubPartsWithoutFilesBinding @@ -23,18 +21,16 @@ class MultipleSubPartAdapter( override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): RecyclerView.ViewHolder { return if (!containsMultipleFiles) { val binding = - DataBindingUtil.inflate( + LayoutCardSingleSubPartsWithoutFilesBinding.inflate( LayoutInflater.from(context), - R.layout.layout_card_single_sub_parts_without_files, viewGroup, false ) SubPartWithoutFilesViewHolder(binding) } else { val binding = - DataBindingUtil.inflate( + LayoutCardSingleSubPartsWithFilesBinding.inflate( LayoutInflater.from(context), - R.layout.layout_card_single_sub_parts_with_files, viewGroup, false ) @@ -58,7 +54,6 @@ class MultipleSubPartAdapter( holder.binding.serialOrder.text = subParts[i].subSerialOrder val adapter = MultipleFileAdapter( context, - R.layout.layout_card_single_files_without_sub_parts, subParts[i].contentFiles, itemClickHandler ) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/NavigationAdapter.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/NavigationAdapter.kt index 4f86158..1a65a03 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/NavigationAdapter.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/adapters/NavigationAdapter.kt @@ -10,7 +10,6 @@ import androidx.recyclerview.widget.RecyclerView import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.adapters.NavigationAdapter.NavigationViewHolder import com.nagpal.shivam.vtucslab.models.Laboratory -import java.util.* class NavigationAdapter( private val context: Context, diff --git a/app/src/main/res/layout/layout_card_se_msp.xml b/app/src/main/res/layout/layout_card_se_msp.xml index 4b2c6e6..485935a 100644 --- a/app/src/main/res/layout/layout_card_se_msp.xml +++ b/app/src/main/res/layout/layout_card_se_msp.xml @@ -1,48 +1,45 @@ - + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content"> - + android:layout_height="wrap_content" + android:layout_marginStart="12dp" + android:layout_marginTop="6dp" + android:layout_marginEnd="12dp" + android:layout_marginBottom="6dp" + android:foreground="?attr/selectableItemBackground" + app:cardCornerRadius="4dp" + app:cardElevation="2dp"> - + android:padding="8dp"> - - - + android:background="@drawable/shape_background_number" + android:gravity="center" + android:textColor="@android:color/white" + android:textSize="24sp" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="1" /> - + - - - - \ No newline at end of file + + + diff --git a/app/src/main/res/layout/layout_card_se_ssp_mf.xml b/app/src/main/res/layout/layout_card_se_ssp_mf.xml index b0aea2d..688d99b 100644 --- a/app/src/main/res/layout/layout_card_se_ssp_mf.xml +++ b/app/src/main/res/layout/layout_card_se_ssp_mf.xml @@ -1,48 +1,45 @@ - + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content"> - + android:layout_height="wrap_content" + android:layout_marginStart="12dp" + android:layout_marginTop="6dp" + android:layout_marginEnd="12dp" + android:layout_marginBottom="6dp" + android:foreground="?attr/selectableItemBackground" + app:cardCornerRadius="4dp" + app:cardElevation="2dp"> - + android:padding="8dp"> - - - + android:background="@drawable/shape_background_number" + android:gravity="center" + android:textColor="@android:color/white" + android:textSize="24sp" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="1" /> - + - - - - \ No newline at end of file + + + diff --git a/app/src/main/res/layout/layout_card_se_ssp_sf.xml b/app/src/main/res/layout/layout_card_se_ssp_sf.xml index 3623cbf..ea09748 100644 --- a/app/src/main/res/layout/layout_card_se_ssp_sf.xml +++ b/app/src/main/res/layout/layout_card_se_ssp_sf.xml @@ -1,57 +1,54 @@ - + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content"> - + android:layout_height="wrap_content" + android:layout_marginStart="12dp" + android:layout_marginTop="6dp" + android:layout_marginEnd="12dp" + android:layout_marginBottom="6dp" + android:foreground="?attr/selectableItemBackground" + app:cardCornerRadius="4dp" + app:cardElevation="2dp"> - + android:padding="8dp"> - + android:background="@drawable/shape_background_number" + android:gravity="center" + android:textColor="@android:color/white" + android:textSize="24sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="1" /> - - - + - - - - \ No newline at end of file + + + diff --git a/app/src/main/res/layout/layout_card_single_files_without_sub_parts.xml b/app/src/main/res/layout/layout_card_single_files_without_sub_parts.xml index 6242aad..6860d9a 100644 --- a/app/src/main/res/layout/layout_card_single_files_without_sub_parts.xml +++ b/app/src/main/res/layout/layout_card_single_files_without_sub_parts.xml @@ -1,46 +1,40 @@ - + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:foreground="?attr/selectableItemBackground"> - - - - + + + + - - - - - - - - - - \ No newline at end of file + android:layout_marginStart="16dp" + android:ellipsize="end" + android:singleLine="true" + android:textColor="@android:color/black" + android:textSize="18sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toEndOf="@+id/bullet" + app:layout_constraintTop_toTopOf="parent" + tools:text="TextView" /> + + + diff --git a/app/src/main/res/layout/layout_card_single_sub_parts_with_files.xml b/app/src/main/res/layout/layout_card_single_sub_parts_with_files.xml index 44103ad..e695204 100644 --- a/app/src/main/res/layout/layout_card_single_sub_parts_with_files.xml +++ b/app/src/main/res/layout/layout_card_single_sub_parts_with_files.xml @@ -1,50 +1,47 @@ - - - + + + android:layout_height="wrap_content" + android:padding="8dp"> - - - + android:background="@drawable/shape_background_sub_part" + android:gravity="center" + android:textColor="@color/colorPrimary" + android:textSize="18sp" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="1" /> - + - + - - - \ No newline at end of file + + diff --git a/app/src/main/res/layout/layout_card_single_sub_parts_without_files.xml b/app/src/main/res/layout/layout_card_single_sub_parts_without_files.xml index 7f90368..2f747f9 100644 --- a/app/src/main/res/layout/layout_card_single_sub_parts_without_files.xml +++ b/app/src/main/res/layout/layout_card_single_sub_parts_without_files.xml @@ -1,45 +1,42 @@ - + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:foreground="?attr/selectableItemBackground"> - + android:padding="8dp"> - + android:background="@drawable/shape_background_sub_part" + android:gravity="center" + android:textColor="@color/colorPrimary" + android:textSize="18sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="1" /> - - - + - - - \ No newline at end of file + + From 75c80207fa66081701cec4aa4b631cca5524b25b Mon Sep 17 00:00:00 2001 From: WallE Date: Sat, 25 Mar 2023 03:16:28 +0530 Subject: [PATCH 29/50] feature: Call Retrofit APIs using Kotlin Coroutines --- .../screens/display/DisplayViewModel.kt | 32 ++++++++----------- .../screens/programs/ProgramViewModel.kt | 31 ++++++++---------- .../screens/repository/RepositoryViewModel.kt | 31 ++++++++---------- .../vtucslab/services/VtuCsLabService.kt | 8 ++--- 4 files changed, 44 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt index 3e4c814..d2a705d 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt @@ -2,17 +2,17 @@ package com.nagpal.shivam.vtucslab.screens.display import android.app.Application import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.viewModelScope import com.nagpal.shivam.vtucslab.VTUCSLabApplication import com.nagpal.shivam.vtucslab.services.VtuCsLabService import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response +import kotlinx.coroutines.launch class DisplayViewModel(app: Application) : AndroidViewModel(app) { var scrollX = 0 @@ -39,25 +39,21 @@ class DisplayViewModel(app: Application) : AndroidViewModel(app) { return } _uiState.update { initialState } - VtuCsLabService.instance.fetchRawResponse(url) - .enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { + viewModelScope.launch(Dispatchers.IO) { + try { + val response = VtuCsLabService.instance.fetchRawResponse(url) + if (response.isSuccessful) { _uiState.update { val stringResponse = response.body()!!.replace("\t", "\t\t") - DisplayState( - Stages.SUCCEEDED, - stringResponse, - null, - ) + DisplayState(Stages.SUCCEEDED, stringResponse, null) } + } else { + _uiState.update { DisplayState(Stages.FAILED, null, null) } } - - override fun onFailure(call: Call, t: Throwable) { - _uiState.update { - DisplayState(Stages.FAILED, null, null) - } - } - }) + } catch (throwable: Throwable) { + _uiState.update { DisplayState(Stages.FAILED, null, null) } + } + } } fun resetState() { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt index 5f9ac27..8c07de7 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt @@ -2,20 +2,19 @@ package com.nagpal.shivam.vtucslab.screens.programs import android.app.Application import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.viewModelScope import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse import com.nagpal.shivam.vtucslab.services.VtuCsLabService import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages import com.nagpal.shivam.vtucslab.utilities.StaticMethods +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response +import kotlinx.coroutines.launch class ProgramViewModel(app: Application) : AndroidViewModel(app) { private val initialState = ProgramState(Stages.LOADING, null, null, null) @@ -39,12 +38,10 @@ class ProgramViewModel(app: Application) : AndroidViewModel(app) { return } _uiState.update { initialState } - VtuCsLabService.instance.getLaboratoryExperimentsResponse(url) - .enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response - ) { + viewModelScope.launch(Dispatchers.IO) { + try { + val response = VtuCsLabService.instance.getLaboratoryExperimentsResponse(url) + if (response.isSuccessful) { _uiState.update { val labResponse = response.body()!! ProgramState( @@ -54,14 +51,12 @@ class ProgramViewModel(app: Application) : AndroidViewModel(app) { StaticMethods.getBaseURL(labResponse) ) } + } else { + _uiState.update { ProgramState(Stages.FAILED, null, null, null) } } - - override fun onFailure(call: Call, t: Throwable) { - _uiState.update { - ProgramState(Stages.FAILED, null, null, null) - } - } - }) + } catch (throwable: Throwable) { + _uiState.update { ProgramState(Stages.FAILED, null, null, null) } + } + } } - } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt index 288b945..12707c8 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -2,20 +2,19 @@ package com.nagpal.shivam.vtucslab.screens.repository import android.app.Application import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.viewModelScope import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.models.LaboratoryResponse import com.nagpal.shivam.vtucslab.services.VtuCsLabService import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages import com.nagpal.shivam.vtucslab.utilities.StaticMethods +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response +import kotlinx.coroutines.launch class RepositoryViewModel(app: Application) : AndroidViewModel(app) { private val initialState = RepositoryState(Stages.LOADING, null, null, null) @@ -39,12 +38,10 @@ class RepositoryViewModel(app: Application) : AndroidViewModel(app) { return } _uiState.update { initialState } - VtuCsLabService.instance.getLaboratoryResponse(url) - .enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response - ) { + viewModelScope.launch(Dispatchers.IO) { + try { + val response = VtuCsLabService.instance.getLaboratoryResponse(url) + if (response.isSuccessful) { _uiState.update { val labResponse = response.body()!! RepositoryState( @@ -54,14 +51,12 @@ class RepositoryViewModel(app: Application) : AndroidViewModel(app) { StaticMethods.getBaseURL(labResponse) ) } + } else { + _uiState.update { RepositoryState(Stages.FAILED, null, null, null) } } - - override fun onFailure(call: Call, t: Throwable) { - _uiState.update { - RepositoryState(Stages.FAILED, null, null, null) - } - } - }) + } catch (throwable: Throwable) { + _uiState.update { RepositoryState(Stages.FAILED, null, null, null) } + } + } } - } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt index 07c458c..c8f69f3 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt @@ -4,20 +4,20 @@ import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse import com.nagpal.shivam.vtucslab.models.LaboratoryResponse import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.StaticMethods -import retrofit2.Call +import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Url interface VtuCsLabService { @GET - fun getLaboratoryResponse(@Url url: String): Call + suspend fun getLaboratoryResponse(@Url url: String): Response @GET - fun getLaboratoryExperimentsResponse(@Url url: String): Call + suspend fun getLaboratoryExperimentsResponse(@Url url: String): Response @GET - fun fetchRawResponse(@Url url: String): Call + suspend fun fetchRawResponse(@Url url: String): Response companion object { val instance: VtuCsLabService by lazy { From 601c2d8638a44b48362fcbe30368f9b1416b7d58 Mon Sep 17 00:00:00 2001 From: WallE Date: Sat, 25 Mar 2023 18:08:55 +0530 Subject: [PATCH 30/50] feature: Unify Retrofit Response Handling using Sealed Classes --- .../shivam/vtucslab/retrofit/ApiResult.kt | 7 +++ .../retrofit/RetrofitConfigurations.kt | 43 +++++++++++++++++++ .../screens/display/DisplayViewModel.kt | 30 +++++++++---- .../screens/programs/ProgramViewModel.kt | 32 ++++++++++---- .../screens/repository/RepositoryViewModel.kt | 31 +++++++++---- 5 files changed, 119 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResult.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResult.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResult.kt new file mode 100644 index 0000000..641baf2 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResult.kt @@ -0,0 +1,7 @@ +package com.nagpal.shivam.vtucslab.retrofit + +sealed class ApiResult { + class ApiSuccess(val data: T) : ApiResult() + class ApiError(val code: Int, val message: String?) : ApiResult() + class ApiException(val throwable: Throwable) : ApiResult() +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt new file mode 100644 index 0000000..498c7c1 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt @@ -0,0 +1,43 @@ +package com.nagpal.shivam.vtucslab.retrofit + +import android.util.Log +import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* +import retrofit2.HttpException +import retrofit2.Response + +suspend fun handleApi( + execute: suspend () -> Response +): ApiResult { + return try { + val response = execute() + val body = response.body() + if (response.isSuccessful && body != null) { + ApiSuccess(body) + } else { + ApiError(code = response.code(), message = response.message()) + } + } catch (e: HttpException) { + ApiError(code = e.code(), message = e.message()) + } catch (e: Throwable) { + ApiException(e) + } +} + +fun logNetworkResultError( + logTag: String, + url: String, + networkResult: ApiError +) { + Log.e( + logTag, + "Call to $url resulted in non-success response. Status Code: ${networkResult.code}. Message: ${networkResult.message}" + ) +} + +fun logNetworkResultException( + logTag: String, + url: String, + networkResult: ApiException +) { + Log.e(logTag, "Call to $url failed with exception", networkResult.throwable) +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt index d2a705d..0deb634 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt @@ -4,6 +4,10 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.nagpal.shivam.vtucslab.VTUCSLabApplication +import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* +import com.nagpal.shivam.vtucslab.retrofit.handleApi +import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultError +import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultException import com.nagpal.shivam.vtucslab.services.VtuCsLabService import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils @@ -14,6 +18,8 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +private val LOG_TAG: String = DisplayViewModel::class.java.name + class DisplayViewModel(app: Application) : AndroidViewModel(app) { var scrollX = 0 var scrollY = 0 @@ -40,22 +46,30 @@ class DisplayViewModel(app: Application) : AndroidViewModel(app) { } _uiState.update { initialState } viewModelScope.launch(Dispatchers.IO) { - try { - val response = VtuCsLabService.instance.fetchRawResponse(url) - if (response.isSuccessful) { + when (val networkResult = + handleApi { VtuCsLabService.instance.fetchRawResponse(url) }) { + is ApiSuccess -> { _uiState.update { - val stringResponse = response.body()!!.replace("\t", "\t\t") + val stringResponse = networkResult.data.replace("\t", "\t\t") DisplayState(Stages.SUCCEEDED, stringResponse, null) } - } else { - _uiState.update { DisplayState(Stages.FAILED, null, null) } } - } catch (throwable: Throwable) { - _uiState.update { DisplayState(Stages.FAILED, null, null) } + is ApiError -> { + logNetworkResultError(LOG_TAG, url, networkResult) + updateStateAsFailed() + } + is ApiException -> { + logNetworkResultException(LOG_TAG, url, networkResult) + updateStateAsFailed() + } } } } + private fun updateStateAsFailed() { + _uiState.update { DisplayState(Stages.FAILED, null, null) } + } + fun resetState() { _uiState.update { initialState } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt index 8c07de7..72b1c2e 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt @@ -4,6 +4,10 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.nagpal.shivam.vtucslab.VTUCSLabApplication +import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* +import com.nagpal.shivam.vtucslab.retrofit.handleApi +import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultError +import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultException import com.nagpal.shivam.vtucslab.services.VtuCsLabService import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils @@ -16,6 +20,8 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +private val LOG_TAG: String = ProgramViewModel::class.java.name + class ProgramViewModel(app: Application) : AndroidViewModel(app) { private val initialState = ProgramState(Stages.LOADING, null, null, null) private val _uiState = MutableStateFlow(initialState) @@ -39,11 +45,13 @@ class ProgramViewModel(app: Application) : AndroidViewModel(app) { } _uiState.update { initialState } viewModelScope.launch(Dispatchers.IO) { - try { - val response = VtuCsLabService.instance.getLaboratoryExperimentsResponse(url) - if (response.isSuccessful) { + val networkResult = + handleApi { VtuCsLabService.instance.getLaboratoryExperimentsResponse(url) } + + when (networkResult) { + is ApiSuccess -> { _uiState.update { - val labResponse = response.body()!! + val labResponse = networkResult.data ProgramState( Stages.SUCCEEDED, labResponse, @@ -51,12 +59,20 @@ class ProgramViewModel(app: Application) : AndroidViewModel(app) { StaticMethods.getBaseURL(labResponse) ) } - } else { - _uiState.update { ProgramState(Stages.FAILED, null, null, null) } } - } catch (throwable: Throwable) { - _uiState.update { ProgramState(Stages.FAILED, null, null, null) } + is ApiError -> { + logNetworkResultError(LOG_TAG, url, networkResult) + updateStateAsFailed() + } + is ApiException -> { + logNetworkResultException(LOG_TAG, url, networkResult) + updateStateAsFailed() + } } } } + + private fun updateStateAsFailed() { + _uiState.update { ProgramState(Stages.FAILED, null, null, null) } + } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt index 12707c8..15b4196 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -4,6 +4,10 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.nagpal.shivam.vtucslab.VTUCSLabApplication +import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* +import com.nagpal.shivam.vtucslab.retrofit.handleApi +import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultError +import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultException import com.nagpal.shivam.vtucslab.services.VtuCsLabService import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils @@ -16,6 +20,8 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +private val LOG_TAG: String = RepositoryViewModel::class.java.name + class RepositoryViewModel(app: Application) : AndroidViewModel(app) { private val initialState = RepositoryState(Stages.LOADING, null, null, null) private val _uiState = MutableStateFlow(initialState) @@ -39,11 +45,12 @@ class RepositoryViewModel(app: Application) : AndroidViewModel(app) { } _uiState.update { initialState } viewModelScope.launch(Dispatchers.IO) { - try { - val response = VtuCsLabService.instance.getLaboratoryResponse(url) - if (response.isSuccessful) { + + when (val networkResult = + handleApi { VtuCsLabService.instance.getLaboratoryResponse(url) }) { + is ApiSuccess -> { _uiState.update { - val labResponse = response.body()!! + val labResponse = networkResult.data RepositoryState( Stages.SUCCEEDED, labResponse, @@ -51,12 +58,20 @@ class RepositoryViewModel(app: Application) : AndroidViewModel(app) { StaticMethods.getBaseURL(labResponse) ) } - } else { - _uiState.update { RepositoryState(Stages.FAILED, null, null, null) } } - } catch (throwable: Throwable) { - _uiState.update { RepositoryState(Stages.FAILED, null, null, null) } + is ApiError -> { + logNetworkResultError(LOG_TAG, url, networkResult) + updateStateAsFailed() + } + is ApiException -> { + logNetworkResultException(LOG_TAG, url, networkResult) + updateStateAsFailed() + } } } } + + private fun updateStateAsFailed() { + _uiState.update { RepositoryState(Stages.FAILED, null, null, null) } + } } From 3c322a12f6cef87442ece7e0733ecdfc1ad803ad Mon Sep 17 00:00:00 2001 From: WallE Date: Sat, 25 Mar 2023 18:34:46 +0530 Subject: [PATCH 31/50] feature: Create Retrofit CallAdapter to convert the Response to ApiResult in the interface --- .../shivam/vtucslab/retrofit/ApiResultCall.kt | 39 +++++++++++++++++++ .../vtucslab/retrofit/ApiResultCallAdapter.kt | 15 +++++++ .../retrofit/ApiResultCallAdapterFactory.kt | 31 +++++++++++++++ .../retrofit/RetrofitConfigurations.kt | 4 +- .../screens/display/DisplayViewModel.kt | 4 +- .../screens/programs/ProgramViewModel.kt | 7 +--- .../screens/repository/RepositoryViewModel.kt | 5 +-- .../vtucslab/services/VtuCsLabService.kt | 8 ++-- .../vtucslab/utilities/StaticMethods.kt | 2 + 9 files changed, 97 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultCall.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultCallAdapter.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultCallAdapterFactory.kt diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultCall.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultCall.kt new file mode 100644 index 0000000..27cd0c8 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultCall.kt @@ -0,0 +1,39 @@ +package com.nagpal.shivam.vtucslab.retrofit + +import com.nagpal.shivam.vtucslab.retrofit.ApiResult.ApiException +import okhttp3.Request +import okio.Timeout +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +class ApiResultCall(private val proxy: Call) : Call> { + override fun enqueue(callback: Callback>) { + proxy.enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + val apiResult = handleApiResult { response } + callback.onResponse(this@ApiResultCall, Response.success(apiResult)) + } + + override fun onFailure(call: Call, t: Throwable) { + val apiResult = ApiException(t) + callback.onResponse(this@ApiResultCall, Response.success(apiResult)) + } + }) + } + + override fun execute(): Response> = throw NotImplementedError() + + override fun clone(): Call> = ApiResultCall(proxy.clone()) + + override fun isExecuted(): Boolean = proxy.isExecuted + + override fun cancel() = proxy.cancel() + + override fun isCanceled(): Boolean = proxy.isCanceled + + override fun request(): Request = proxy.request() + + override fun timeout(): Timeout = proxy.timeout() + +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultCallAdapter.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultCallAdapter.kt new file mode 100644 index 0000000..5778538 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultCallAdapter.kt @@ -0,0 +1,15 @@ +package com.nagpal.shivam.vtucslab.retrofit + +import retrofit2.Call +import retrofit2.CallAdapter +import java.lang.reflect.Type + +class ApiResultCallAdapter(private val resultType: Type) : + CallAdapter>> { + override fun responseType(): Type = resultType + + override fun adapt(call: Call): Call> { + return ApiResultCall(call) + } + +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultCallAdapterFactory.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultCallAdapterFactory.kt new file mode 100644 index 0000000..0bbe9fc --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultCallAdapterFactory.kt @@ -0,0 +1,31 @@ +package com.nagpal.shivam.vtucslab.retrofit + +import retrofit2.Call +import retrofit2.CallAdapter +import retrofit2.Retrofit +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type + +class ApiResultCallAdapterFactory private constructor() : CallAdapter.Factory() { + override fun get( + returnType: Type, + annotations: Array, + retrofit: Retrofit + ): CallAdapter<*, *>? { + if (getRawType(returnType) != Call::class.java) { + return null + } + + val callType = getParameterUpperBound(0, returnType as ParameterizedType) + if (getRawType(callType) != ApiResult::class.java) { + return null + } + + val resultType = getParameterUpperBound(0, callType as ParameterizedType) + return ApiResultCallAdapter(resultType) + } + + companion object { + fun create(): ApiResultCallAdapterFactory = ApiResultCallAdapterFactory() + } +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt index 498c7c1..745d93d 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt @@ -5,8 +5,8 @@ import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* import retrofit2.HttpException import retrofit2.Response -suspend fun handleApi( - execute: suspend () -> Response +fun handleApiResult( + execute: () -> Response ): ApiResult { return try { val response = execute() diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt index 0deb634..6963b81 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt @@ -5,7 +5,6 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.nagpal.shivam.vtucslab.VTUCSLabApplication import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* -import com.nagpal.shivam.vtucslab.retrofit.handleApi import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultError import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultException import com.nagpal.shivam.vtucslab.services.VtuCsLabService @@ -46,8 +45,7 @@ class DisplayViewModel(app: Application) : AndroidViewModel(app) { } _uiState.update { initialState } viewModelScope.launch(Dispatchers.IO) { - when (val networkResult = - handleApi { VtuCsLabService.instance.fetchRawResponse(url) }) { + when (val networkResult = VtuCsLabService.instance.fetchRawResponse(url)) { is ApiSuccess -> { _uiState.update { val stringResponse = networkResult.data.replace("\t", "\t\t") diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt index 72b1c2e..77d77c6 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt @@ -5,7 +5,6 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.nagpal.shivam.vtucslab.VTUCSLabApplication import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* -import com.nagpal.shivam.vtucslab.retrofit.handleApi import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultError import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultException import com.nagpal.shivam.vtucslab.services.VtuCsLabService @@ -45,10 +44,8 @@ class ProgramViewModel(app: Application) : AndroidViewModel(app) { } _uiState.update { initialState } viewModelScope.launch(Dispatchers.IO) { - val networkResult = - handleApi { VtuCsLabService.instance.getLaboratoryExperimentsResponse(url) } - - when (networkResult) { + when (val networkResult = + VtuCsLabService.instance.getLaboratoryExperimentsResponse(url)) { is ApiSuccess -> { _uiState.update { val labResponse = networkResult.data diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt index 15b4196..5927d45 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -5,7 +5,6 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.nagpal.shivam.vtucslab.VTUCSLabApplication import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* -import com.nagpal.shivam.vtucslab.retrofit.handleApi import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultError import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultException import com.nagpal.shivam.vtucslab.services.VtuCsLabService @@ -45,9 +44,7 @@ class RepositoryViewModel(app: Application) : AndroidViewModel(app) { } _uiState.update { initialState } viewModelScope.launch(Dispatchers.IO) { - - when (val networkResult = - handleApi { VtuCsLabService.instance.getLaboratoryResponse(url) }) { + when (val networkResult = VtuCsLabService.instance.getLaboratoryResponse(url)) { is ApiSuccess -> { _uiState.update { val labResponse = networkResult.data diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt index c8f69f3..dd83835 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt @@ -2,22 +2,22 @@ package com.nagpal.shivam.vtucslab.services import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse import com.nagpal.shivam.vtucslab.models.LaboratoryResponse +import com.nagpal.shivam.vtucslab.retrofit.ApiResult import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.StaticMethods -import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Url interface VtuCsLabService { @GET - suspend fun getLaboratoryResponse(@Url url: String): Response + suspend fun getLaboratoryResponse(@Url url: String): ApiResult @GET - suspend fun getLaboratoryExperimentsResponse(@Url url: String): Response + suspend fun getLaboratoryExperimentsResponse(@Url url: String): ApiResult @GET - suspend fun fetchRawResponse(@Url url: String): Response + suspend fun fetchRawResponse(@Url url: String): ApiResult companion object { val instance: VtuCsLabService by lazy { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt index e3883d6..0b1e8fd 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.json.JsonMapper import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse import com.nagpal.shivam.vtucslab.models.LaboratoryResponse +import com.nagpal.shivam.vtucslab.retrofit.ApiResultCallAdapterFactory import retrofit2.Retrofit import retrofit2.converter.jackson.JacksonConverterFactory import retrofit2.converter.scalars.ScalarsConverterFactory @@ -24,6 +25,7 @@ object StaticMethods { return Retrofit.Builder() .addConverterFactory(ScalarsConverterFactory.create()) .addConverterFactory(JacksonConverterFactory.create(jsonMapper)) + .addCallAdapterFactory(ApiResultCallAdapterFactory.create()) } fun getBaseURL(labResponse: LaboratoryResponse): String { From cb7283f854cfb103976629a6ce558bfeacc286ba Mon Sep 17 00:00:00 2001 From: WallE Date: Sat, 25 Mar 2023 20:04:47 +0530 Subject: [PATCH 32/50] fix: Log the exception class name with the ApiException logging and move the Retrofit builder to the RetrofitConfiguration --- .../retrofit/RetrofitConfigurations.kt | 18 +++++++++++++++++- .../vtucslab/services/VtuCsLabService.kt | 3 ++- .../shivam/vtucslab/utilities/StaticMethods.kt | 13 +------------ 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt index 745d93d..871f5c8 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt @@ -2,8 +2,20 @@ package com.nagpal.shivam.vtucslab.retrofit import android.util.Log import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* +import com.nagpal.shivam.vtucslab.utilities.StaticMethods import retrofit2.HttpException import retrofit2.Response +import retrofit2.Retrofit +import retrofit2.converter.jackson.JacksonConverterFactory +import retrofit2.converter.scalars.ScalarsConverterFactory + + +fun getRetrofitBuilder(): Retrofit.Builder { + return Retrofit.Builder() + .addConverterFactory(ScalarsConverterFactory.create()) + .addConverterFactory(JacksonConverterFactory.create(StaticMethods.jsonMapper)) + .addCallAdapterFactory(ApiResultCallAdapterFactory.create()) +} fun handleApiResult( execute: () -> Response @@ -39,5 +51,9 @@ fun logNetworkResultException( url: String, networkResult: ApiException ) { - Log.e(logTag, "Call to $url failed with exception", networkResult.throwable) + Log.e( + logTag, + "Call to $url failed with exception: ${networkResult.throwable.javaClass.name}", + networkResult.throwable + ) } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt index dd83835..300284b 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt @@ -3,6 +3,7 @@ package com.nagpal.shivam.vtucslab.services import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse import com.nagpal.shivam.vtucslab.models.LaboratoryResponse import com.nagpal.shivam.vtucslab.retrofit.ApiResult +import com.nagpal.shivam.vtucslab.retrofit.getRetrofitBuilder import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.StaticMethods import retrofit2.http.GET @@ -21,7 +22,7 @@ interface VtuCsLabService { companion object { val instance: VtuCsLabService by lazy { - val retrofit = StaticMethods.getRetrofitBuilder() + val retrofit = getRetrofitBuilder() .baseUrl(Constants.GITHUB_RAW_BASE_URL) .build() return@lazy retrofit.create(VtuCsLabService::class.java) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt index 0b1e8fd..2d2fa66 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt @@ -4,14 +4,10 @@ import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.json.JsonMapper import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse import com.nagpal.shivam.vtucslab.models.LaboratoryResponse -import com.nagpal.shivam.vtucslab.retrofit.ApiResultCallAdapterFactory -import retrofit2.Retrofit -import retrofit2.converter.jackson.JacksonConverterFactory -import retrofit2.converter.scalars.ScalarsConverterFactory object StaticMethods { - private val jsonMapper: JsonMapper by lazy { + val jsonMapper: JsonMapper by lazy { com.fasterxml.jackson.module.kotlin.jacksonMapperBuilder() .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) .build() @@ -21,13 +17,6 @@ object StaticMethods { return programName.replace('_', ' ') } - fun getRetrofitBuilder(): Retrofit.Builder { - return Retrofit.Builder() - .addConverterFactory(ScalarsConverterFactory.create()) - .addConverterFactory(JacksonConverterFactory.create(jsonMapper)) - .addCallAdapterFactory(ApiResultCallAdapterFactory.create()) - } - fun getBaseURL(labResponse: LaboratoryResponse): String { return "${labResponse.githubRawContent}/${labResponse.organization}/${labResponse.repository}/${labResponse.branch}" } From 3538496b6af3a6f482dcc7cd9ba41af5e84510ee Mon Sep 17 00:00:00 2001 From: WallE Date: Sat, 25 Mar 2023 21:16:40 +0530 Subject: [PATCH 33/50] feature: Add Retrofit extensions and refractor Retrofit Configuration Code --- .../vtucslab/retrofit/ApiResultExtensions.kt | 25 +++++++++++++++++ .../retrofit/RetrofitConfigurations.kt | 24 ----------------- .../screens/display/DisplayViewModel.kt | 23 ++++++++-------- .../screens/programs/ProgramViewModel.kt | 27 +++++++++---------- .../screens/repository/RepositoryViewModel.kt | 26 +++++++++--------- .../vtucslab/services/VtuCsLabService.kt | 1 - .../vtucslab/utilities/StaticMethods.kt | 25 +++++++++++++++++ 7 files changed, 88 insertions(+), 63 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultExtensions.kt diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultExtensions.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultExtensions.kt new file mode 100644 index 0000000..653afe8 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultExtensions.kt @@ -0,0 +1,25 @@ +package com.nagpal.shivam.vtucslab.retrofit + +fun ApiResult.onSuccess( + executable: (data: T) -> Unit +): ApiResult = apply { + if (this is ApiResult.ApiSuccess) { + executable(this.data) + } +} + +fun ApiResult.onError( + executable: (code: Int, message: String?) -> Unit +): ApiResult = apply { + if (this is ApiResult.ApiError) { + executable(this.code, this.message) + } +} + +fun ApiResult.onException( + executable: (throwable: Throwable) -> Unit +): ApiResult = apply { + if (this is ApiResult.ApiException) { + executable(this.throwable) + } +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt index 871f5c8..841870a 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/RetrofitConfigurations.kt @@ -1,6 +1,5 @@ package com.nagpal.shivam.vtucslab.retrofit -import android.util.Log import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* import com.nagpal.shivam.vtucslab.utilities.StaticMethods import retrofit2.HttpException @@ -34,26 +33,3 @@ fun handleApiResult( ApiException(e) } } - -fun logNetworkResultError( - logTag: String, - url: String, - networkResult: ApiError -) { - Log.e( - logTag, - "Call to $url resulted in non-success response. Status Code: ${networkResult.code}. Message: ${networkResult.message}" - ) -} - -fun logNetworkResultException( - logTag: String, - url: String, - networkResult: ApiException -) { - Log.e( - logTag, - "Call to $url failed with exception: ${networkResult.throwable.javaClass.name}", - networkResult.throwable - ) -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt index 6963b81..5849523 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt @@ -4,13 +4,15 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* -import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultError -import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultException +import com.nagpal.shivam.vtucslab.retrofit.onError +import com.nagpal.shivam.vtucslab.retrofit.onException +import com.nagpal.shivam.vtucslab.retrofit.onSuccess import com.nagpal.shivam.vtucslab.services.VtuCsLabService import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages +import com.nagpal.shivam.vtucslab.utilities.StaticMethods.logNetworkResultError +import com.nagpal.shivam.vtucslab.utilities.StaticMethods.logNetworkResultException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -45,22 +47,21 @@ class DisplayViewModel(app: Application) : AndroidViewModel(app) { } _uiState.update { initialState } viewModelScope.launch(Dispatchers.IO) { - when (val networkResult = VtuCsLabService.instance.fetchRawResponse(url)) { - is ApiSuccess -> { + VtuCsLabService.instance.fetchRawResponse(url) + .onSuccess { data -> _uiState.update { - val stringResponse = networkResult.data.replace("\t", "\t\t") + val stringResponse = data.replace("\t", "\t\t") DisplayState(Stages.SUCCEEDED, stringResponse, null) } } - is ApiError -> { - logNetworkResultError(LOG_TAG, url, networkResult) + .onError { code, message -> + logNetworkResultError(LOG_TAG, url, code, message) updateStateAsFailed() } - is ApiException -> { - logNetworkResultException(LOG_TAG, url, networkResult) + .onException { throwable -> + logNetworkResultException(LOG_TAG, url, throwable) updateStateAsFailed() } - } } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt index 77d77c6..49309d9 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt @@ -4,14 +4,16 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* -import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultError -import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultException +import com.nagpal.shivam.vtucslab.retrofit.onError +import com.nagpal.shivam.vtucslab.retrofit.onException +import com.nagpal.shivam.vtucslab.retrofit.onSuccess import com.nagpal.shivam.vtucslab.services.VtuCsLabService import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages import com.nagpal.shivam.vtucslab.utilities.StaticMethods +import com.nagpal.shivam.vtucslab.utilities.StaticMethods.logNetworkResultError +import com.nagpal.shivam.vtucslab.utilities.StaticMethods.logNetworkResultException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -44,28 +46,25 @@ class ProgramViewModel(app: Application) : AndroidViewModel(app) { } _uiState.update { initialState } viewModelScope.launch(Dispatchers.IO) { - when (val networkResult = - VtuCsLabService.instance.getLaboratoryExperimentsResponse(url)) { - is ApiSuccess -> { + VtuCsLabService.instance.getLaboratoryExperimentsResponse(url) + .onSuccess { data -> _uiState.update { - val labResponse = networkResult.data ProgramState( Stages.SUCCEEDED, - labResponse, + data, null, - StaticMethods.getBaseURL(labResponse) + StaticMethods.getBaseURL(data) ) } } - is ApiError -> { - logNetworkResultError(LOG_TAG, url, networkResult) + .onError { code, message -> + logNetworkResultError(LOG_TAG, url, code, message) updateStateAsFailed() } - is ApiException -> { - logNetworkResultException(LOG_TAG, url, networkResult) + .onException { throwable -> + logNetworkResultException(LOG_TAG, url, throwable) updateStateAsFailed() } - } } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt index 5927d45..8b2bbef 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -4,14 +4,16 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* -import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultError -import com.nagpal.shivam.vtucslab.retrofit.logNetworkResultException +import com.nagpal.shivam.vtucslab.retrofit.onError +import com.nagpal.shivam.vtucslab.retrofit.onException +import com.nagpal.shivam.vtucslab.retrofit.onSuccess import com.nagpal.shivam.vtucslab.services.VtuCsLabService import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages import com.nagpal.shivam.vtucslab.utilities.StaticMethods +import com.nagpal.shivam.vtucslab.utilities.StaticMethods.logNetworkResultError +import com.nagpal.shivam.vtucslab.utilities.StaticMethods.logNetworkResultException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -44,27 +46,25 @@ class RepositoryViewModel(app: Application) : AndroidViewModel(app) { } _uiState.update { initialState } viewModelScope.launch(Dispatchers.IO) { - when (val networkResult = VtuCsLabService.instance.getLaboratoryResponse(url)) { - is ApiSuccess -> { + VtuCsLabService.instance.getLaboratoryResponse(url) + .onSuccess { data -> _uiState.update { - val labResponse = networkResult.data RepositoryState( Stages.SUCCEEDED, - labResponse, + data, null, - StaticMethods.getBaseURL(labResponse) + StaticMethods.getBaseURL(data) ) } } - is ApiError -> { - logNetworkResultError(LOG_TAG, url, networkResult) + .onError { code, message -> + logNetworkResultError(LOG_TAG, url, code, message) updateStateAsFailed() } - is ApiException -> { - logNetworkResultException(LOG_TAG, url, networkResult) + .onException { throwable -> + logNetworkResultException(LOG_TAG, url, throwable) updateStateAsFailed() } - } } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt index 300284b..8ddd4e3 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/services/VtuCsLabService.kt @@ -5,7 +5,6 @@ import com.nagpal.shivam.vtucslab.models.LaboratoryResponse import com.nagpal.shivam.vtucslab.retrofit.ApiResult import com.nagpal.shivam.vtucslab.retrofit.getRetrofitBuilder import com.nagpal.shivam.vtucslab.utilities.Constants -import com.nagpal.shivam.vtucslab.utilities.StaticMethods import retrofit2.http.GET import retrofit2.http.Url diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt index 2d2fa66..b2c685e 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt @@ -1,5 +1,6 @@ package com.nagpal.shivam.vtucslab.utilities +import android.util.Log import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.json.JsonMapper import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse @@ -24,4 +25,28 @@ object StaticMethods { fun getBaseURL(laboratoryExperimentResponse: LaboratoryExperimentResponse): String { return "${laboratoryExperimentResponse.githubRawContent}/${laboratoryExperimentResponse.organization}/${laboratoryExperimentResponse.repository}/${laboratoryExperimentResponse.branch}" } + + fun logNetworkResultError( + logTag: String, + url: String, + code: Int, + message: String? + ) { + Log.e( + logTag, + "Call to $url resulted in non-success response. Status Code: $code. Message: $message" + ) + } + + fun logNetworkResultException( + logTag: String, + url: String, + throwable: Throwable + ) { + Log.e( + logTag, + "Call to $url failed with exception: ${throwable.javaClass.name}", + throwable + ) + } } From 0de247b85c9454ceb0fa16d3e444e211f37876aa Mon Sep 17 00:00:00 2001 From: WallE Date: Sat, 25 Mar 2023 22:11:19 +0530 Subject: [PATCH 34/50] fix: Remove ApiResultExtenstions as they will result in duplicate extensions for suspend methods for little syntax improvement --- .../vtucslab/retrofit/ApiResultExtensions.kt | 25 ------------------- .../screens/display/DisplayViewModel.kt | 19 +++++++------- .../screens/programs/ProgramViewModel.kt | 21 ++++++++-------- .../screens/repository/RepositoryViewModel.kt | 21 ++++++++-------- 4 files changed, 29 insertions(+), 57 deletions(-) delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultExtensions.kt diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultExtensions.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultExtensions.kt deleted file mode 100644 index 653afe8..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/retrofit/ApiResultExtensions.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.nagpal.shivam.vtucslab.retrofit - -fun ApiResult.onSuccess( - executable: (data: T) -> Unit -): ApiResult = apply { - if (this is ApiResult.ApiSuccess) { - executable(this.data) - } -} - -fun ApiResult.onError( - executable: (code: Int, message: String?) -> Unit -): ApiResult = apply { - if (this is ApiResult.ApiError) { - executable(this.code, this.message) - } -} - -fun ApiResult.onException( - executable: (throwable: Throwable) -> Unit -): ApiResult = apply { - if (this is ApiResult.ApiException) { - executable(this.throwable) - } -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt index 5849523..fa7c91a 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt @@ -4,9 +4,7 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.retrofit.onError -import com.nagpal.shivam.vtucslab.retrofit.onException -import com.nagpal.shivam.vtucslab.retrofit.onSuccess +import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* import com.nagpal.shivam.vtucslab.services.VtuCsLabService import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils @@ -47,21 +45,22 @@ class DisplayViewModel(app: Application) : AndroidViewModel(app) { } _uiState.update { initialState } viewModelScope.launch(Dispatchers.IO) { - VtuCsLabService.instance.fetchRawResponse(url) - .onSuccess { data -> + when (val apiResult = VtuCsLabService.instance.fetchRawResponse(url)) { + is ApiSuccess -> { _uiState.update { - val stringResponse = data.replace("\t", "\t\t") + val stringResponse = apiResult.data.replace("\t", "\t\t") DisplayState(Stages.SUCCEEDED, stringResponse, null) } } - .onError { code, message -> - logNetworkResultError(LOG_TAG, url, code, message) + is ApiError -> { + logNetworkResultError(LOG_TAG, url, apiResult.code, apiResult.message) updateStateAsFailed() } - .onException { throwable -> - logNetworkResultException(LOG_TAG, url, throwable) + is ApiException -> { + logNetworkResultException(LOG_TAG, url, apiResult.throwable) updateStateAsFailed() } + } } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt index 49309d9..05ae564 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt @@ -4,9 +4,7 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.retrofit.onError -import com.nagpal.shivam.vtucslab.retrofit.onException -import com.nagpal.shivam.vtucslab.retrofit.onSuccess +import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* import com.nagpal.shivam.vtucslab.services.VtuCsLabService import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils @@ -46,25 +44,26 @@ class ProgramViewModel(app: Application) : AndroidViewModel(app) { } _uiState.update { initialState } viewModelScope.launch(Dispatchers.IO) { - VtuCsLabService.instance.getLaboratoryExperimentsResponse(url) - .onSuccess { data -> + when (val apiResult = VtuCsLabService.instance.getLaboratoryExperimentsResponse(url)) { + is ApiSuccess -> { _uiState.update { ProgramState( Stages.SUCCEEDED, - data, + apiResult.data, null, - StaticMethods.getBaseURL(data) + StaticMethods.getBaseURL(apiResult.data) ) } } - .onError { code, message -> - logNetworkResultError(LOG_TAG, url, code, message) + is ApiError -> { + logNetworkResultError(LOG_TAG, url, apiResult.code, apiResult.message) updateStateAsFailed() } - .onException { throwable -> - logNetworkResultException(LOG_TAG, url, throwable) + is ApiException -> { + logNetworkResultException(LOG_TAG, url, apiResult.throwable) updateStateAsFailed() } + } } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt index 8b2bbef..34f0fc6 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -4,9 +4,7 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.retrofit.onError -import com.nagpal.shivam.vtucslab.retrofit.onException -import com.nagpal.shivam.vtucslab.retrofit.onSuccess +import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* import com.nagpal.shivam.vtucslab.services.VtuCsLabService import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils @@ -46,25 +44,26 @@ class RepositoryViewModel(app: Application) : AndroidViewModel(app) { } _uiState.update { initialState } viewModelScope.launch(Dispatchers.IO) { - VtuCsLabService.instance.getLaboratoryResponse(url) - .onSuccess { data -> + when (val apiResult = VtuCsLabService.instance.getLaboratoryResponse(url)) { + is ApiSuccess -> { _uiState.update { RepositoryState( Stages.SUCCEEDED, - data, + apiResult.data, null, - StaticMethods.getBaseURL(data) + StaticMethods.getBaseURL(apiResult.data) ) } } - .onError { code, message -> - logNetworkResultError(LOG_TAG, url, code, message) + is ApiError -> { + logNetworkResultError(LOG_TAG, url, apiResult.code, apiResult.message) updateStateAsFailed() } - .onException { throwable -> - logNetworkResultException(LOG_TAG, url, throwable) + is ApiException -> { + logNetworkResultException(LOG_TAG, url, apiResult.throwable) updateStateAsFailed() } + } } } From 14badbacba82a92f16808eda82e21d1d4c06ea61 Mon Sep 17 00:00:00 2001 From: WallE Date: Mon, 27 Mar 2023 01:33:56 +0530 Subject: [PATCH 35/50] feature: Introduce Repostory Layer after ViewModel to have a separate Data layer --- .../shivam/vtucslab/VTUCSLabApplication.kt | 4 + .../nagpal/shivam/vtucslab/core/Resource.kt | 7 ++ .../repositories/VtuCsLabRepository.kt | 14 ++++ .../repositories/VtuCsLabRepositoryImpl.kt | 56 +++++++++++++ .../screens/display/DisplayFragment.kt | 6 +- .../screens/display/DisplayViewModel.kt | 69 +++++++++------- .../screens/programs/ProgramFragment.kt | 6 +- .../screens/programs/ProgramViewModel.kt | 81 +++++++++++-------- .../screens/repository/RepositoryFragment.kt | 6 +- .../screens/repository/RepositoryViewModel.kt | 81 +++++++++++-------- 10 files changed, 223 insertions(+), 107 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepository.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt index 8c71608..b0d2f83 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt @@ -2,8 +2,12 @@ package com.nagpal.shivam.vtucslab import androidx.multidex.MultiDexApplication import com.google.firebase.crashlytics.FirebaseCrashlytics +import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository +import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepositoryImpl +import com.nagpal.shivam.vtucslab.services.VtuCsLabService class VTUCSLabApplication : MultiDexApplication() { + val vtuCsLabRepository: VtuCsLabRepository = VtuCsLabRepositoryImpl(VtuCsLabService.instance) override fun onCreate() { super.onCreate() if (BuildConfig.DEBUG) { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt new file mode 100644 index 0000000..c658474 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt @@ -0,0 +1,7 @@ +package com.nagpal.shivam.vtucslab.core + +sealed class Resource { + class Loading(data: T? = null) : Resource() + class Success(val data: T?) : Resource() + class Error(message: String? = null) : Resource() +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepository.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepository.kt new file mode 100644 index 0000000..a4f4b8a --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepository.kt @@ -0,0 +1,14 @@ +package com.nagpal.shivam.vtucslab.repositories + +import com.nagpal.shivam.vtucslab.core.Resource +import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse +import com.nagpal.shivam.vtucslab.models.LaboratoryResponse +import kotlinx.coroutines.flow.Flow + +interface VtuCsLabRepository { + fun fetchLaboratories(url: String): Flow> + + fun fetchExperiments(url: String): Flow> + + fun fetchContent(url: String): Flow> +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt new file mode 100644 index 0000000..c809cef --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt @@ -0,0 +1,56 @@ +package com.nagpal.shivam.vtucslab.repositories + +import com.nagpal.shivam.vtucslab.core.Resource +import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse +import com.nagpal.shivam.vtucslab.models.LaboratoryResponse +import com.nagpal.shivam.vtucslab.retrofit.ApiResult +import com.nagpal.shivam.vtucslab.services.VtuCsLabService +import com.nagpal.shivam.vtucslab.utilities.StaticMethods +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.flow + +private val LOG_TAG: String = VtuCsLabRepositoryImpl::class.java.name + +class VtuCsLabRepositoryImpl( + private val vtuCsLabService: VtuCsLabService +) : VtuCsLabRepository { + override fun fetchLaboratories(url: String): Flow> = flow { + fetch(url) { + vtuCsLabService.getLaboratoryResponse(it) + } + } + + override fun fetchExperiments(url: String): Flow> = + flow { + fetch(url) { + vtuCsLabService.getLaboratoryExperimentsResponse(it) + } + } + + override fun fetchContent(url: String): Flow> = flow { + fetch(url) { + vtuCsLabService.fetchRawResponse(it) + } + } + + private suspend fun FlowCollector>.fetch( + url: String, + executable: suspend (String) -> ApiResult + ) { + emit(Resource.Loading()) + when (val apiResult = executable.invoke(url)) { + is ApiResult.ApiSuccess -> { + emit(Resource.Success(apiResult.data)) + } + is ApiResult.ApiError -> { + StaticMethods.logNetworkResultError(LOG_TAG, url, apiResult.code, apiResult.message) + emit(Resource.Error()) + } + is ApiResult.ApiException -> { + StaticMethods.logNetworkResultException(LOG_TAG, url, apiResult.throwable) + emit(Resource.Error()) + } + } + } +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt index 115dfde..6b1e52f 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt @@ -10,8 +10,8 @@ import android.view.* import android.widget.Toast import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.navArgs @@ -27,7 +27,7 @@ class DisplayFragment : Fragment() { private var _binding: FragmentDisplayBinding? = null private val binding get() = _binding!! private val displayFragmentArgs by navArgs() - private lateinit var viewModel: DisplayViewModel + private val viewModel: DisplayViewModel by viewModels { DisplayViewModel.Factory } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -36,8 +36,6 @@ class DisplayFragment : Fragment() { _binding = FragmentDisplayBinding.inflate(inflater, container, false) setupMenuProvider() - viewModel = ViewModelProvider(this)[DisplayViewModel::class.java] - viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.uiState.collect { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt index fa7c91a..0edb42c 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt @@ -2,32 +2,33 @@ package com.nagpal.shivam.vtucslab.screens.display import android.app.Application import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* -import com.nagpal.shivam.vtucslab.services.VtuCsLabService +import com.nagpal.shivam.vtucslab.core.Resource +import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages -import com.nagpal.shivam.vtucslab.utilities.StaticMethods.logNetworkResultError -import com.nagpal.shivam.vtucslab.utilities.StaticMethods.logNetworkResultException import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch -private val LOG_TAG: String = DisplayViewModel::class.java.name - -class DisplayViewModel(app: Application) : AndroidViewModel(app) { +class DisplayViewModel( + private val application: Application, + private val vtuCsLabRepository: VtuCsLabRepository +) : AndroidViewModel(application) { var scrollX = 0 var scrollY = 0 private val initialState = DisplayState(Stages.LOADING, null, null) private val _uiState = MutableStateFlow(initialState) val uiState = _uiState.asStateFlow() - - private val application = this.getApplication() + private var fetchJob: Job? = null fun loadContent(url: String) { if (_uiState.value.stage == Stages.SUCCEEDED) { @@ -43,24 +44,22 @@ class DisplayViewModel(app: Application) : AndroidViewModel(app) { } return } - _uiState.update { initialState } - viewModelScope.launch(Dispatchers.IO) { - when (val apiResult = VtuCsLabService.instance.fetchRawResponse(url)) { - is ApiSuccess -> { - _uiState.update { - val stringResponse = apiResult.data.replace("\t", "\t\t") - DisplayState(Stages.SUCCEEDED, stringResponse, null) + fetchJob?.cancel() + fetchJob = viewModelScope.launch(Dispatchers.IO) { + vtuCsLabRepository.fetchContent(url) + .onEach { resource -> + when (resource) { + is Resource.Loading -> { + _uiState.update { initialState } + } + is Resource.Success -> { + _uiState.update { DisplayState(Stages.SUCCEEDED, resource.data, null) } + } + is Resource.Error -> { + updateStateAsFailed() + } } - } - is ApiError -> { - logNetworkResultError(LOG_TAG, url, apiResult.code, apiResult.message) - updateStateAsFailed() - } - is ApiException -> { - logNetworkResultException(LOG_TAG, url, apiResult.throwable) - updateStateAsFailed() - } - } + }.launchIn(this) } } @@ -71,4 +70,16 @@ class DisplayViewModel(app: Application) : AndroidViewModel(app) { fun resetState() { _uiState.update { initialState } } + + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val vtuCsLabApplication = this[APPLICATION_KEY] as VTUCSLabApplication + DisplayViewModel( + vtuCsLabApplication, + vtuCsLabApplication.vtuCsLabRepository + ) + } + } + } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt index e669595..45f8958 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt @@ -5,8 +5,8 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController @@ -25,7 +25,7 @@ class ProgramFragment : Fragment() { private var _binding: FragmentProgramBinding? = null private val binding get() = _binding!! - private lateinit var viewModel: ProgramViewModel + private val viewModel: ProgramViewModel by viewModels { ProgramViewModel.Factory } private lateinit var contentAdapter: ContentAdapter private val programFragmentArgs by navArgs() @@ -38,8 +38,6 @@ class ProgramFragment : Fragment() { setupViews() setupRepositoryAdapter() - viewModel = ViewModelProvider(this)[ProgramViewModel::class.java] - viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.uiState.collect { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt index 05ae564..1c571bb 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt @@ -2,30 +2,31 @@ package com.nagpal.shivam.vtucslab.screens.programs import android.app.Application import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* -import com.nagpal.shivam.vtucslab.services.VtuCsLabService +import com.nagpal.shivam.vtucslab.core.Resource +import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages import com.nagpal.shivam.vtucslab.utilities.StaticMethods -import com.nagpal.shivam.vtucslab.utilities.StaticMethods.logNetworkResultError -import com.nagpal.shivam.vtucslab.utilities.StaticMethods.logNetworkResultException import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch -private val LOG_TAG: String = ProgramViewModel::class.java.name - -class ProgramViewModel(app: Application) : AndroidViewModel(app) { +class ProgramViewModel( + private val application: Application, + private val vtuCsLabRepository: VtuCsLabRepository +) : AndroidViewModel(application) { private val initialState = ProgramState(Stages.LOADING, null, null, null) private val _uiState = MutableStateFlow(initialState) - private val application = this.getApplication() val uiState: StateFlow = _uiState.asStateFlow() + private var fetchJob: Job? = null fun loadContent(url: String) { if (_uiState.value.stage == Stages.SUCCEEDED) { @@ -42,32 +43,46 @@ class ProgramViewModel(app: Application) : AndroidViewModel(app) { } return } - _uiState.update { initialState } - viewModelScope.launch(Dispatchers.IO) { - when (val apiResult = VtuCsLabService.instance.getLaboratoryExperimentsResponse(url)) { - is ApiSuccess -> { - _uiState.update { - ProgramState( - Stages.SUCCEEDED, - apiResult.data, - null, - StaticMethods.getBaseURL(apiResult.data) - ) + + fetchJob?.cancel() + fetchJob = viewModelScope.launch(Dispatchers.IO) { + vtuCsLabRepository.fetchExperiments(url) + .onEach { resource -> + when (resource) { + is Resource.Loading -> { + _uiState.update { initialState } + } + is Resource.Success -> { + _uiState.update { + ProgramState( + Stages.SUCCEEDED, + resource.data, + null, + StaticMethods.getBaseURL(resource.data!!) + ) + } + } + is Resource.Error -> { + updateStateAsFailed() + } } - } - is ApiError -> { - logNetworkResultError(LOG_TAG, url, apiResult.code, apiResult.message) - updateStateAsFailed() - } - is ApiException -> { - logNetworkResultException(LOG_TAG, url, apiResult.throwable) - updateStateAsFailed() - } - } + }.launchIn(this) } } private fun updateStateAsFailed() { _uiState.update { ProgramState(Stages.FAILED, null, null, null) } } + + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val vtuCsLabApplication = this[APPLICATION_KEY] as VTUCSLabApplication + ProgramViewModel( + vtuCsLabApplication, + vtuCsLabApplication.vtuCsLabRepository + ) + } + } + } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt index c299c3f..c7e60ba 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt @@ -6,8 +6,8 @@ import android.os.Bundle import android.view.* import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController @@ -25,7 +25,7 @@ class RepositoryFragment : Fragment() { private lateinit var navigationAdapter: NavigationAdapter private val binding get() = _binding!! - private lateinit var viewModel: RepositoryViewModel + private val viewModel: RepositoryViewModel by viewModels { RepositoryViewModel.Factory } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -36,8 +36,6 @@ class RepositoryFragment : Fragment() { setupViews() setupRepositoryAdapter() - viewModel = ViewModelProvider(this)[RepositoryViewModel::class.java] - viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.uiState.collect { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt index 34f0fc6..f551945 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -2,30 +2,31 @@ package com.nagpal.shivam.vtucslab.screens.repository import android.app.Application import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.retrofit.ApiResult.* -import com.nagpal.shivam.vtucslab.services.VtuCsLabService +import com.nagpal.shivam.vtucslab.core.Resource +import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages import com.nagpal.shivam.vtucslab.utilities.StaticMethods -import com.nagpal.shivam.vtucslab.utilities.StaticMethods.logNetworkResultError -import com.nagpal.shivam.vtucslab.utilities.StaticMethods.logNetworkResultException import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch -private val LOG_TAG: String = RepositoryViewModel::class.java.name - -class RepositoryViewModel(app: Application) : AndroidViewModel(app) { +class RepositoryViewModel( + private val application: Application, + private val vtuCsLabRepository: VtuCsLabRepository +) : AndroidViewModel(application) { private val initialState = RepositoryState(Stages.LOADING, null, null, null) private val _uiState = MutableStateFlow(initialState) - private val application = this.getApplication() val uiState: StateFlow = _uiState.asStateFlow() + private var fetchJob: Job? = null fun loadContent(url: String) { if (_uiState.value.stage == Stages.SUCCEEDED) { @@ -42,32 +43,46 @@ class RepositoryViewModel(app: Application) : AndroidViewModel(app) { } return } - _uiState.update { initialState } - viewModelScope.launch(Dispatchers.IO) { - when (val apiResult = VtuCsLabService.instance.getLaboratoryResponse(url)) { - is ApiSuccess -> { - _uiState.update { - RepositoryState( - Stages.SUCCEEDED, - apiResult.data, - null, - StaticMethods.getBaseURL(apiResult.data) - ) + + fetchJob?.cancel() + fetchJob = viewModelScope.launch(Dispatchers.IO) { + vtuCsLabRepository.fetchLaboratories(url) + .onEach { resource -> + when (resource) { + is Resource.Loading -> { + _uiState.update { initialState } + } + is Resource.Success -> { + _uiState.update { + RepositoryState( + Stages.SUCCEEDED, + resource.data, + null, + StaticMethods.getBaseURL(resource.data!!) + ) + } + } + is Resource.Error -> { + updateStateAsFailed() + } } - } - is ApiError -> { - logNetworkResultError(LOG_TAG, url, apiResult.code, apiResult.message) - updateStateAsFailed() - } - is ApiException -> { - logNetworkResultException(LOG_TAG, url, apiResult.throwable) - updateStateAsFailed() - } - } + }.launchIn(this) } } private fun updateStateAsFailed() { _uiState.update { RepositoryState(Stages.FAILED, null, null, null) } } + + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val vtuCsLabApplication = this[APPLICATION_KEY] as VTUCSLabApplication + RepositoryViewModel( + vtuCsLabApplication, + vtuCsLabApplication.vtuCsLabRepository + ) + } + } + } } From bf8267d38196e90c51fb39bcee0728e34b373ab1 Mon Sep 17 00:00:00 2001 From: WallE Date: Wed, 29 Mar 2023 00:51:59 +0530 Subject: [PATCH 36/50] feature: Create Events for the interaction with the view models --- .../com/nagpal/shivam/vtucslab/core/Resource.kt | 4 ++-- .../com/nagpal/shivam/vtucslab/core/UIText.kt | 7 +++++++ .../nagpal/shivam/vtucslab/screens/UiEvent.kt | 6 ++++++ .../vtucslab/screens/display/DisplayFragment.kt | 15 +++++++-------- .../screens/display/DisplayViewModel.kt | 17 +++++++++++++++-- .../screens/programs/ProgramFragment.kt | 3 ++- .../screens/programs/ProgramViewModel.kt | 15 ++++++++++++++- .../screens/repository/RepositoryFragment.kt | 3 ++- .../screens/repository/RepositoryViewModel.kt | 14 +++++++++++++- 9 files changed, 68 insertions(+), 16 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/core/UIText.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/UiEvent.kt diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt index c658474..d1b570c 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt @@ -1,7 +1,7 @@ package com.nagpal.shivam.vtucslab.core sealed class Resource { - class Loading(data: T? = null) : Resource() + class Loading(val data: T? = null) : Resource() class Success(val data: T?) : Resource() - class Error(message: String? = null) : Resource() + class Error(val message: String? = null) : Resource() } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/core/UIText.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/core/UIText.kt new file mode 100644 index 0000000..64be18c --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/core/UIText.kt @@ -0,0 +1,7 @@ +package com.nagpal.shivam.vtucslab.core + +import androidx.annotation.StringRes + +sealed class UIText { + class StringResource(@field:StringRes val resourceId: Int) : UIText() +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/UiEvent.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/UiEvent.kt new file mode 100644 index 0000000..53a3859 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/UiEvent.kt @@ -0,0 +1,6 @@ +package com.nagpal.shivam.vtucslab.screens + +sealed class UiEvent { + class LoadContent(val url: String) : UiEvent() + class RefreshContent(val url: String) : UiEvent() +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt index 6b1e52f..d96407b 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt @@ -17,6 +17,7 @@ import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.navArgs import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.databinding.FragmentDisplayBinding +import com.nagpal.shivam.vtucslab.screens.UiEvent import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.launch @@ -29,13 +30,16 @@ class DisplayFragment : Fragment() { private val displayFragmentArgs by navArgs() private val viewModel: DisplayViewModel by viewModels { DisplayViewModel.Factory } + private val url: String by lazy { + return@lazy "${displayFragmentArgs.baseUrl}/${displayFragmentArgs.fileName}" + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentDisplayBinding.inflate(inflater, container, false) setupMenuProvider() - viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.uiState.collect { @@ -85,8 +89,7 @@ class DisplayFragment : Fragment() { override fun onMenuItemSelected(menuItem: MenuItem): Boolean { return when (menuItem.itemId) { R.id.display_menu_item_refresh -> { - viewModel.resetState() - loadContent() + viewModel.onEvent(UiEvent.RefreshContent(url)) return true } R.id.menu_item_copy_display_activity -> { @@ -115,7 +118,7 @@ class DisplayFragment : Fragment() { override fun onResume() { super.onResume() - loadContent() + viewModel.onEvent(UiEvent.LoadContent(url)) } override fun onSaveInstanceState(outState: Bundle) { @@ -123,8 +126,4 @@ class DisplayFragment : Fragment() { viewModel.scrollY = binding.verticalScroll.scrollY super.onSaveInstanceState(outState) } - - private fun loadContent() { - viewModel.loadContent("${displayFragmentArgs.baseUrl}/${displayFragmentArgs.fileName}") - } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt index 0edb42c..e6d4f98 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt @@ -10,6 +10,7 @@ import androidx.lifecycle.viewmodel.viewModelFactory import com.nagpal.shivam.vtucslab.VTUCSLabApplication import com.nagpal.shivam.vtucslab.core.Resource import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository +import com.nagpal.shivam.vtucslab.screens.UiEvent import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages @@ -30,7 +31,19 @@ class DisplayViewModel( val uiState = _uiState.asStateFlow() private var fetchJob: Job? = null - fun loadContent(url: String) { + fun onEvent(event: UiEvent) { + when (event) { + is UiEvent.LoadContent -> { + loadContent(event.url) + } + is UiEvent.RefreshContent -> { + resetState() + loadContent(event.url) + } + } + } + + private fun loadContent(url: String) { if (_uiState.value.stage == Stages.SUCCEEDED) { return } @@ -67,7 +80,7 @@ class DisplayViewModel( _uiState.update { DisplayState(Stages.FAILED, null, null) } } - fun resetState() { + private fun resetState() { _uiState.update { initialState } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt index 45f8958..00f400a 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt @@ -16,6 +16,7 @@ import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.adapters.ContentAdapter import com.nagpal.shivam.vtucslab.databinding.FragmentProgramBinding import com.nagpal.shivam.vtucslab.models.ContentFile +import com.nagpal.shivam.vtucslab.screens.UiEvent import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.launch @@ -101,7 +102,7 @@ class ProgramFragment : Fragment() { override fun onResume() { super.onResume() - viewModel.loadContent("${programFragmentArgs.baseUrl}/${programFragmentArgs.fileName}") + viewModel.onEvent(UiEvent.LoadContent("${programFragmentArgs.baseUrl}/${programFragmentArgs.fileName}")) } override fun onDestroyView() { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt index 1c571bb..64fd969 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt @@ -10,6 +10,7 @@ import androidx.lifecycle.viewmodel.viewModelFactory import com.nagpal.shivam.vtucslab.VTUCSLabApplication import com.nagpal.shivam.vtucslab.core.Resource import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository +import com.nagpal.shivam.vtucslab.screens.UiEvent import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages @@ -28,7 +29,19 @@ class ProgramViewModel( val uiState: StateFlow = _uiState.asStateFlow() private var fetchJob: Job? = null - fun loadContent(url: String) { + + fun onEvent(event: UiEvent) { + when (event) { + is UiEvent.LoadContent -> { + loadContent(event.url) + } + is UiEvent.RefreshContent -> { + // ToDo: Handle this case + } + } + } + + private fun loadContent(url: String) { if (_uiState.value.stage == Stages.SUCCEEDED) { return } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt index c7e60ba..da88665 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt @@ -16,6 +16,7 @@ import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.adapters.NavigationAdapter import com.nagpal.shivam.vtucslab.databinding.FragmentRepositoryBinding import com.nagpal.shivam.vtucslab.models.Laboratory +import com.nagpal.shivam.vtucslab.screens.UiEvent import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.launch @@ -81,7 +82,7 @@ class RepositoryFragment : Fragment() { override fun onResume() { super.onResume() - viewModel.loadContent(Constants.INDEX_REPOSITORY_URL) + viewModel.onEvent(UiEvent.LoadContent(Constants.INDEX_REPOSITORY_URL)) } private fun setupRepositoryAdapter() { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt index f551945..2046d3c 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -10,6 +10,7 @@ import androidx.lifecycle.viewmodel.viewModelFactory import com.nagpal.shivam.vtucslab.VTUCSLabApplication import com.nagpal.shivam.vtucslab.core.Resource import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository +import com.nagpal.shivam.vtucslab.screens.UiEvent import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages @@ -28,7 +29,18 @@ class RepositoryViewModel( val uiState: StateFlow = _uiState.asStateFlow() private var fetchJob: Job? = null - fun loadContent(url: String) { + fun onEvent(event: UiEvent) { + when (event) { + is UiEvent.LoadContent -> { + loadContent(event.url) + } + is UiEvent.RefreshContent -> { + // ToDo: Handle this case + } + } + } + + private fun loadContent(url: String) { if (_uiState.value.stage == Stages.SUCCEEDED) { return } From d5557f331e2c85601e4c7f9d6b97cc5ca2ce0aca Mon Sep 17 00:00:00 2001 From: WallE Date: Wed, 29 Mar 2023 01:09:54 +0530 Subject: [PATCH 37/50] refractor: Introduce common UI State for all the 3 screens --- .../shivam/vtucslab/screens/ContentState.kt | 8 +++++++ .../screens/display/DisplayFragment.kt | 4 ++-- .../vtucslab/screens/display/DisplayState.kt | 7 ------ .../screens/display/DisplayViewModel.kt | 17 +++++++++----- .../screens/programs/ProgramFragment.kt | 6 ++--- .../vtucslab/screens/programs/ProgramState.kt | 10 --------- .../screens/programs/ProgramViewModel.kt | 22 +++++++++---------- .../screens/repository/RepositoryFragment.kt | 6 ++--- .../screens/repository/RepositoryState.kt | 10 --------- .../screens/repository/RepositoryViewModel.kt | 17 +++++++------- 10 files changed, 47 insertions(+), 60 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayState.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramState.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryState.kt diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt new file mode 100644 index 0000000..0518db1 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt @@ -0,0 +1,8 @@ +package com.nagpal.shivam.vtucslab.screens + +data class ContentState( + val stage: String, + val data: T? = null, + val message: String? = null, + val baseUrl: String? = null, +) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt index d96407b..89dabf0 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt @@ -50,7 +50,7 @@ class DisplayFragment : Fragment() { binding.progressBar.visibility = View.VISIBLE } Stages.SUCCEEDED -> { - binding.displayTextView.text = it.response + binding.displayTextView.text = it.data requireActivity().invalidateOptionsMenu() Handler(Looper.getMainLooper()).postDelayed({ binding.horizontalScroll.scrollX = viewModel.scrollX @@ -99,7 +99,7 @@ class DisplayFragment : Fragment() { val clipData = ClipData( ClipData.newPlainText( Constants.LABEL_CODE, - viewModel.uiState.value.response + viewModel.uiState.value.data ) ) clipboard.setPrimaryClip(clipData) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayState.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayState.kt deleted file mode 100644 index 7c0963b..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayState.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.nagpal.shivam.vtucslab.screens.display - -data class DisplayState( - val stage: String, - val response: String?, - val message: String?, -) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt index e6d4f98..1fe7f69 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt @@ -10,6 +10,7 @@ import androidx.lifecycle.viewmodel.viewModelFactory import com.nagpal.shivam.vtucslab.VTUCSLabApplication import com.nagpal.shivam.vtucslab.core.Resource import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository +import com.nagpal.shivam.vtucslab.screens.ContentState import com.nagpal.shivam.vtucslab.screens.UiEvent import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils @@ -25,7 +26,7 @@ class DisplayViewModel( ) : AndroidViewModel(application) { var scrollX = 0 var scrollY = 0 - private val initialState = DisplayState(Stages.LOADING, null, null) + private val initialState = ContentState(Stages.LOADING) private val _uiState = MutableStateFlow(initialState) val uiState = _uiState.asStateFlow() @@ -49,10 +50,9 @@ class DisplayViewModel( } if (!NetworkUtils.isNetworkConnected(application)) { _uiState.update { - DisplayState( + ContentState( Stages.FAILED, - null, - Constants.NO_ACTIVE_NETWORK, + message = Constants.NO_ACTIVE_NETWORK, ) } return @@ -66,7 +66,12 @@ class DisplayViewModel( _uiState.update { initialState } } is Resource.Success -> { - _uiState.update { DisplayState(Stages.SUCCEEDED, resource.data, null) } + _uiState.update { + ContentState( + Stages.SUCCEEDED, + data = resource.data, + ) + } } is Resource.Error -> { updateStateAsFailed() @@ -77,7 +82,7 @@ class DisplayViewModel( } private fun updateStateAsFailed() { - _uiState.update { DisplayState(Stages.FAILED, null, null) } + _uiState.update { ContentState(Stages.FAILED) } } private fun resetState() { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt index 00f400a..aa948d6 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt @@ -49,11 +49,11 @@ class ProgramFragment : Fragment() { binding.progressBar.visibility = View.VISIBLE } Stages.SUCCEEDED -> { - if (it.laboratoryExperimentResponse!!.isValid) { + if (it.data!!.isValid) { contentAdapter.clear() - contentAdapter.addAll(it.laboratoryExperimentResponse.labExperiments) + contentAdapter.addAll(it.data.labExperiments) } else { - showErrorMessage(it.laboratoryExperimentResponse.invalidationMessage) + showErrorMessage(it.data.invalidationMessage) } } Stages.FAILED -> { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramState.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramState.kt deleted file mode 100644 index 5ab3f55..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramState.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.nagpal.shivam.vtucslab.screens.programs - -import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse - -data class ProgramState( - val stage: String, - val laboratoryExperimentResponse: LaboratoryExperimentResponse?, - val message: String?, - val baseUrl: String? -) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt index 64fd969..cc8326b 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt @@ -9,7 +9,9 @@ import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import com.nagpal.shivam.vtucslab.VTUCSLabApplication import com.nagpal.shivam.vtucslab.core.Resource +import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository +import com.nagpal.shivam.vtucslab.screens.ContentState import com.nagpal.shivam.vtucslab.screens.UiEvent import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils @@ -24,9 +26,10 @@ class ProgramViewModel( private val application: Application, private val vtuCsLabRepository: VtuCsLabRepository ) : AndroidViewModel(application) { - private val initialState = ProgramState(Stages.LOADING, null, null, null) + private val initialState = + ContentState(Stages.LOADING) private val _uiState = MutableStateFlow(initialState) - val uiState: StateFlow = _uiState.asStateFlow() + val uiState: StateFlow> = _uiState.asStateFlow() private var fetchJob: Job? = null @@ -47,11 +50,9 @@ class ProgramViewModel( } if (!NetworkUtils.isNetworkConnected(application)) { _uiState.update { - ProgramState( + ContentState( Stages.FAILED, - null, - Constants.NO_ACTIVE_NETWORK, - null + message = Constants.NO_ACTIVE_NETWORK, ) } return @@ -67,11 +68,10 @@ class ProgramViewModel( } is Resource.Success -> { _uiState.update { - ProgramState( + ContentState( Stages.SUCCEEDED, - resource.data, - null, - StaticMethods.getBaseURL(resource.data!!) + data = resource.data, + baseUrl = StaticMethods.getBaseURL(resource.data!!), ) } } @@ -84,7 +84,7 @@ class ProgramViewModel( } private fun updateStateAsFailed() { - _uiState.update { ProgramState(Stages.FAILED, null, null, null) } + _uiState.update { ContentState(Stages.FAILED) } } companion object { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt index da88665..08d8507 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt @@ -47,11 +47,11 @@ class RepositoryFragment : Fragment() { binding.progressBar.visibility = View.VISIBLE } Stages.SUCCEEDED -> { - if (it.laboratoryResponse!!.isValid) { + if (it.data!!.isValid) { navigationAdapter.clear() - navigationAdapter.addAll(it.laboratoryResponse.laboratories) + navigationAdapter.addAll(it.data.laboratories) } else { - showErrorMessage(it.laboratoryResponse.invalidationMessage) + showErrorMessage(it.data.invalidationMessage) } } Stages.FAILED -> { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryState.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryState.kt deleted file mode 100644 index 1710867..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryState.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.nagpal.shivam.vtucslab.screens.repository - -import com.nagpal.shivam.vtucslab.models.LaboratoryResponse - -data class RepositoryState( - val stage: String, - val laboratoryResponse: LaboratoryResponse?, - val message: String?, - val baseUrl: String? -) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt index 2046d3c..14a15f4 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -9,7 +9,9 @@ import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import com.nagpal.shivam.vtucslab.VTUCSLabApplication import com.nagpal.shivam.vtucslab.core.Resource +import com.nagpal.shivam.vtucslab.models.LaboratoryResponse import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository +import com.nagpal.shivam.vtucslab.screens.ContentState import com.nagpal.shivam.vtucslab.screens.UiEvent import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils @@ -24,9 +26,9 @@ class RepositoryViewModel( private val application: Application, private val vtuCsLabRepository: VtuCsLabRepository ) : AndroidViewModel(application) { - private val initialState = RepositoryState(Stages.LOADING, null, null, null) + private val initialState = ContentState(Stages.LOADING) private val _uiState = MutableStateFlow(initialState) - val uiState: StateFlow = _uiState.asStateFlow() + val uiState: StateFlow> = _uiState.asStateFlow() private var fetchJob: Job? = null fun onEvent(event: UiEvent) { @@ -46,7 +48,7 @@ class RepositoryViewModel( } if (!NetworkUtils.isNetworkConnected(application)) { _uiState.update { - RepositoryState( + ContentState( Stages.FAILED, null, Constants.NO_ACTIVE_NETWORK, @@ -66,11 +68,10 @@ class RepositoryViewModel( } is Resource.Success -> { _uiState.update { - RepositoryState( + ContentState( Stages.SUCCEEDED, - resource.data, - null, - StaticMethods.getBaseURL(resource.data!!) + data = resource.data, + baseUrl = StaticMethods.getBaseURL(resource.data!!), ) } } @@ -83,7 +84,7 @@ class RepositoryViewModel( } private fun updateStateAsFailed() { - _uiState.update { RepositoryState(Stages.FAILED, null, null, null) } + _uiState.update { ContentState(Stages.FAILED) } } companion object { From b8dffac117c3ee373e779b76e435e524b21b6246 Mon Sep 17 00:00:00 2001 From: WallE Date: Wed, 29 Mar 2023 01:32:51 +0530 Subject: [PATCH 38/50] refractor: Extract the loadcontent to a common Utils method --- .../nagpal/shivam/vtucslab/screens/Utils.kt | 68 +++++++++++++++++++ .../screens/display/DisplayViewModel.kt | 58 ++++------------ .../screens/programs/ProgramViewModel.kt | 59 ++++------------ .../screens/repository/RepositoryViewModel.kt | 61 ++++------------- 4 files changed, 107 insertions(+), 139 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt new file mode 100644 index 0000000..3b49033 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt @@ -0,0 +1,68 @@ +package com.nagpal.shivam.vtucslab.screens + +import android.app.Application +import com.nagpal.shivam.vtucslab.core.Resource +import com.nagpal.shivam.vtucslab.utilities.Constants +import com.nagpal.shivam.vtucslab.utilities.NetworkUtils +import com.nagpal.shivam.vtucslab.utilities.Stages +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch + +object Utils { + fun loadContent( + uiStateFlow: MutableStateFlow>, + application: Application, + fetchJob: Job?, + viewModelScope: CoroutineScope, + fetchExecutable: (String) -> Flow>, + getBaseUrl: (T) -> String?, + url: String + ): Job? { + if (uiStateFlow.value.stage == Stages.SUCCEEDED) { + return null + } + if (!NetworkUtils.isNetworkConnected(application)) { + uiStateFlow.update { + ContentState( + Stages.FAILED, + message = Constants.NO_ACTIVE_NETWORK, + ) + } + return null + } + + fetchJob?.cancel() + return viewModelScope.launch(Dispatchers.IO) { + fetchExecutable.invoke(url) + .onEach { resource -> + when (resource) { + is Resource.Loading -> { + uiStateFlow.update { ContentState(Stages.LOADING) } + } + is Resource.Success -> { + uiStateFlow.update { + ContentState( + Stages.SUCCEEDED, + data = resource.data, + baseUrl = getBaseUrl(resource.data!!), + ) + } + } + is Resource.Error -> { + uiStateFlow.update { ContentState(Stages.FAILED) } + } + } + }.launchIn(this) + } + } + + fun resetState( + uiStateFlow: MutableStateFlow>, + initialState: ContentState + ) { + uiStateFlow.update { initialState } + } +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt index 1fe7f69..de072b8 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt @@ -8,17 +8,14 @@ import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.core.Resource import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository import com.nagpal.shivam.vtucslab.screens.ContentState import com.nagpal.shivam.vtucslab.screens.UiEvent -import com.nagpal.shivam.vtucslab.utilities.Constants -import com.nagpal.shivam.vtucslab.utilities.NetworkUtils +import com.nagpal.shivam.vtucslab.screens.Utils import com.nagpal.shivam.vtucslab.utilities.Stages -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.launch +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow class DisplayViewModel( private val application: Application, @@ -45,48 +42,19 @@ class DisplayViewModel( } private fun loadContent(url: String) { - if (_uiState.value.stage == Stages.SUCCEEDED) { - return - } - if (!NetworkUtils.isNetworkConnected(application)) { - _uiState.update { - ContentState( - Stages.FAILED, - message = Constants.NO_ACTIVE_NETWORK, - ) - } - return - } - fetchJob?.cancel() - fetchJob = viewModelScope.launch(Dispatchers.IO) { - vtuCsLabRepository.fetchContent(url) - .onEach { resource -> - when (resource) { - is Resource.Loading -> { - _uiState.update { initialState } - } - is Resource.Success -> { - _uiState.update { - ContentState( - Stages.SUCCEEDED, - data = resource.data, - ) - } - } - is Resource.Error -> { - updateStateAsFailed() - } - } - }.launchIn(this) - } - } - - private fun updateStateAsFailed() { - _uiState.update { ContentState(Stages.FAILED) } + fetchJob = Utils.loadContent( + _uiState, + application, + fetchJob, + viewModelScope, + { vtuCsLabRepository.fetchContent(it) }, + { null }, + url + ) } private fun resetState() { - _uiState.update { initialState } + Utils.resetState(_uiState, initialState) } companion object { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt index cc8326b..ea8fef7 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt @@ -8,19 +8,17 @@ import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.core.Resource import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository import com.nagpal.shivam.vtucslab.screens.ContentState import com.nagpal.shivam.vtucslab.screens.UiEvent -import com.nagpal.shivam.vtucslab.utilities.Constants -import com.nagpal.shivam.vtucslab.utilities.NetworkUtils +import com.nagpal.shivam.vtucslab.screens.Utils import com.nagpal.shivam.vtucslab.utilities.Stages import com.nagpal.shivam.vtucslab.utilities.StaticMethods -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.launch +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow class ProgramViewModel( private val application: Application, @@ -45,46 +43,15 @@ class ProgramViewModel( } private fun loadContent(url: String) { - if (_uiState.value.stage == Stages.SUCCEEDED) { - return - } - if (!NetworkUtils.isNetworkConnected(application)) { - _uiState.update { - ContentState( - Stages.FAILED, - message = Constants.NO_ACTIVE_NETWORK, - ) - } - return - } - - fetchJob?.cancel() - fetchJob = viewModelScope.launch(Dispatchers.IO) { - vtuCsLabRepository.fetchExperiments(url) - .onEach { resource -> - when (resource) { - is Resource.Loading -> { - _uiState.update { initialState } - } - is Resource.Success -> { - _uiState.update { - ContentState( - Stages.SUCCEEDED, - data = resource.data, - baseUrl = StaticMethods.getBaseURL(resource.data!!), - ) - } - } - is Resource.Error -> { - updateStateAsFailed() - } - } - }.launchIn(this) - } - } - - private fun updateStateAsFailed() { - _uiState.update { ContentState(Stages.FAILED) } + fetchJob = Utils.loadContent( + _uiState, + application, + fetchJob, + viewModelScope, + { vtuCsLabRepository.fetchExperiments(it) }, + { StaticMethods.getBaseURL(it) }, + url + ) } companion object { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt index 14a15f4..2402881 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -8,19 +8,17 @@ import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import com.nagpal.shivam.vtucslab.VTUCSLabApplication -import com.nagpal.shivam.vtucslab.core.Resource import com.nagpal.shivam.vtucslab.models.LaboratoryResponse import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository import com.nagpal.shivam.vtucslab.screens.ContentState import com.nagpal.shivam.vtucslab.screens.UiEvent -import com.nagpal.shivam.vtucslab.utilities.Constants -import com.nagpal.shivam.vtucslab.utilities.NetworkUtils +import com.nagpal.shivam.vtucslab.screens.Utils import com.nagpal.shivam.vtucslab.utilities.Stages import com.nagpal.shivam.vtucslab.utilities.StaticMethods -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.launch +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow class RepositoryViewModel( private val application: Application, @@ -43,48 +41,15 @@ class RepositoryViewModel( } private fun loadContent(url: String) { - if (_uiState.value.stage == Stages.SUCCEEDED) { - return - } - if (!NetworkUtils.isNetworkConnected(application)) { - _uiState.update { - ContentState( - Stages.FAILED, - null, - Constants.NO_ACTIVE_NETWORK, - null - ) - } - return - } - - fetchJob?.cancel() - fetchJob = viewModelScope.launch(Dispatchers.IO) { - vtuCsLabRepository.fetchLaboratories(url) - .onEach { resource -> - when (resource) { - is Resource.Loading -> { - _uiState.update { initialState } - } - is Resource.Success -> { - _uiState.update { - ContentState( - Stages.SUCCEEDED, - data = resource.data, - baseUrl = StaticMethods.getBaseURL(resource.data!!), - ) - } - } - is Resource.Error -> { - updateStateAsFailed() - } - } - }.launchIn(this) - } - } - - private fun updateStateAsFailed() { - _uiState.update { ContentState(Stages.FAILED) } + fetchJob = Utils.loadContent( + _uiState, + application, + fetchJob, + viewModelScope, + { vtuCsLabRepository.fetchLaboratories(it) }, + { StaticMethods.getBaseURL(it) }, + url + ) } companion object { From cbcc1ad4e618c269f1b6379c73d1c8c40f6bf4fc Mon Sep 17 00:00:00 2001 From: WallE Date: Thu, 30 Mar 2023 01:21:27 +0530 Subject: [PATCH 39/50] feature: Implement ErrorType to pass the error messages from upper layers --- .../com/nagpal/shivam/vtucslab/core/ErrorType.kt | 6 ++++++ .../java/com/nagpal/shivam/vtucslab/core/UIText.kt | 7 ------- .../nagpal/shivam/vtucslab/screens/ContentState.kt | 4 +++- .../com/nagpal/shivam/vtucslab/screens/Utils.kt | 13 +++++++++++-- .../vtucslab/screens/display/DisplayFragment.kt | 9 ++++----- .../vtucslab/screens/programs/ProgramFragment.kt | 12 +++++------- .../screens/repository/RepositoryFragment.kt | 10 +++++----- .../nagpal/shivam/vtucslab/utilities/Constants.kt | 1 - 8 files changed, 34 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/core/ErrorType.kt delete mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/core/UIText.kt diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/core/ErrorType.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/core/ErrorType.kt new file mode 100644 index 0000000..67436c9 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/core/ErrorType.kt @@ -0,0 +1,6 @@ +package com.nagpal.shivam.vtucslab.core + +sealed class ErrorType { + object NoActiveInternetConnection : ErrorType() + object SomeErrorOccurred : ErrorType() +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/core/UIText.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/core/UIText.kt deleted file mode 100644 index 64be18c..0000000 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/core/UIText.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.nagpal.shivam.vtucslab.core - -import androidx.annotation.StringRes - -sealed class UIText { - class StringResource(@field:StringRes val resourceId: Int) : UIText() -} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt index 0518db1..f41ded3 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt @@ -1,8 +1,10 @@ package com.nagpal.shivam.vtucslab.screens +import com.nagpal.shivam.vtucslab.core.ErrorType + data class ContentState( val stage: String, val data: T? = null, - val message: String? = null, + val errorType: ErrorType? = null, val baseUrl: String? = null, ) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt index 3b49033..8468984 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt @@ -1,8 +1,10 @@ package com.nagpal.shivam.vtucslab.screens import android.app.Application +import android.content.Context +import com.nagpal.shivam.vtucslab.R +import com.nagpal.shivam.vtucslab.core.ErrorType import com.nagpal.shivam.vtucslab.core.Resource -import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.CoroutineScope @@ -28,7 +30,7 @@ object Utils { uiStateFlow.update { ContentState( Stages.FAILED, - message = Constants.NO_ACTIVE_NETWORK, + errorType = ErrorType.NoActiveInternetConnection ) } return null @@ -65,4 +67,11 @@ object Utils { ) { uiStateFlow.update { initialState } } + + fun mapErrorTypeToString(context: Context, errorType: ErrorType?): String { + return when (errorType) { + ErrorType.NoActiveInternetConnection -> context.getString(R.string.no_internet_connection) + else -> context.getString(R.string.error_occurred) + } + } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt index 89dabf0..05d879b 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt @@ -18,6 +18,7 @@ import androidx.navigation.fragment.navArgs import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.databinding.FragmentDisplayBinding import com.nagpal.shivam.vtucslab.screens.UiEvent +import com.nagpal.shivam.vtucslab.screens.Utils import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.launch @@ -58,11 +59,9 @@ class DisplayFragment : Fragment() { }, 500) } Stages.FAILED -> { - if (it.message == Constants.NO_ACTIVE_NETWORK) { - showErrorMessage(getString(R.string.no_internet_connection)) - } else { - showErrorMessage(getString(R.string.error_occurred)) - } + val message: String = + Utils.mapErrorTypeToString(requireContext(), it.errorType) + showErrorMessage(message) } } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt index aa948d6..434d504 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt @@ -12,12 +12,11 @@ import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager -import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.adapters.ContentAdapter import com.nagpal.shivam.vtucslab.databinding.FragmentProgramBinding import com.nagpal.shivam.vtucslab.models.ContentFile import com.nagpal.shivam.vtucslab.screens.UiEvent -import com.nagpal.shivam.vtucslab.utilities.Constants +import com.nagpal.shivam.vtucslab.screens.Utils import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.launch @@ -53,15 +52,14 @@ class ProgramFragment : Fragment() { contentAdapter.clear() contentAdapter.addAll(it.data.labExperiments) } else { + // TODO: Handle this logic in Data Layer showErrorMessage(it.data.invalidationMessage) } } Stages.FAILED -> { - if (it.message == Constants.NO_ACTIVE_NETWORK) { - showErrorMessage(getString(R.string.no_internet_connection)) - } else { - showErrorMessage(getString(R.string.error_occurred)) - } + val message: String = + Utils.mapErrorTypeToString(requireContext(), it.errorType) + showErrorMessage(message) } } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt index 08d8507..0eda449 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt @@ -17,6 +17,7 @@ import com.nagpal.shivam.vtucslab.adapters.NavigationAdapter import com.nagpal.shivam.vtucslab.databinding.FragmentRepositoryBinding import com.nagpal.shivam.vtucslab.models.Laboratory import com.nagpal.shivam.vtucslab.screens.UiEvent +import com.nagpal.shivam.vtucslab.screens.Utils import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.launch @@ -51,15 +52,14 @@ class RepositoryFragment : Fragment() { navigationAdapter.clear() navigationAdapter.addAll(it.data.laboratories) } else { + // TODO: Handle this logic in Data Layer showErrorMessage(it.data.invalidationMessage) } } Stages.FAILED -> { - if (it.message == Constants.NO_ACTIVE_NETWORK) { - showErrorMessage(getString(R.string.no_internet_connection)) - } else { - showErrorMessage(getString(R.string.error_occurred)) - } + val message: String = + Utils.mapErrorTypeToString(requireContext(), it.errorType) + showErrorMessage(message) } } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt index 729d5f3..a6e4506 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt @@ -6,7 +6,6 @@ object Constants { "https://github.com/ShivamNagpal/Privacy_Policies/blob/master/VTU_CS_LAB_MANUAL.md" const val INDEX_REPOSITORY_URL = "https://raw.githubusercontent.com/vtucs/Index_v3/master/Index_v3.json" - const val NO_ACTIVE_NETWORK = "NO_ACTIVE_NETWORK" const val LABEL_CODE = "Code" const val GITHUB_RAW_CONTENT = "github_raw_content" } From a7e7399e6c100dc7051108348cac3796cd092ae7 Mon Sep 17 00:00:00 2001 From: WallE Date: Thu, 30 Mar 2023 02:04:18 +0530 Subject: [PATCH 40/50] fix: Check the internet connectivity just before making the API call --- .../shivam/vtucslab/VTUCSLabApplication.kt | 4 +++- .../nagpal/shivam/vtucslab/core/Resource.kt | 2 +- .../repositories/VtuCsLabRepositoryImpl.kt | 8 ++++++++ .../nagpal/shivam/vtucslab/screens/Utils.kt | 19 ++++++------------- .../screens/display/DisplayViewModel.kt | 3 +-- .../screens/programs/ProgramViewModel.kt | 3 +-- .../screens/repository/RepositoryViewModel.kt | 3 +-- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt index b0d2f83..93685ba 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt @@ -7,7 +7,9 @@ import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepositoryImpl import com.nagpal.shivam.vtucslab.services.VtuCsLabService class VTUCSLabApplication : MultiDexApplication() { - val vtuCsLabRepository: VtuCsLabRepository = VtuCsLabRepositoryImpl(VtuCsLabService.instance) + val vtuCsLabRepository: VtuCsLabRepository = + VtuCsLabRepositoryImpl(this, VtuCsLabService.instance) + override fun onCreate() { super.onCreate() if (BuildConfig.DEBUG) { diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt index d1b570c..7835a60 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt @@ -3,5 +3,5 @@ package com.nagpal.shivam.vtucslab.core sealed class Resource { class Loading(val data: T? = null) : Resource() class Success(val data: T?) : Resource() - class Error(val message: String? = null) : Resource() + class Error(val errorType: ErrorType? = null) : Resource() } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt index c809cef..a2640cb 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt @@ -1,10 +1,13 @@ package com.nagpal.shivam.vtucslab.repositories +import android.app.Application +import com.nagpal.shivam.vtucslab.core.ErrorType import com.nagpal.shivam.vtucslab.core.Resource import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse import com.nagpal.shivam.vtucslab.models.LaboratoryResponse import com.nagpal.shivam.vtucslab.retrofit.ApiResult import com.nagpal.shivam.vtucslab.services.VtuCsLabService +import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.StaticMethods import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.FlowCollector @@ -13,6 +16,7 @@ import kotlinx.coroutines.flow.flow private val LOG_TAG: String = VtuCsLabRepositoryImpl::class.java.name class VtuCsLabRepositoryImpl( + val application: Application, private val vtuCsLabService: VtuCsLabService ) : VtuCsLabRepository { override fun fetchLaboratories(url: String): Flow> = flow { @@ -39,6 +43,10 @@ class VtuCsLabRepositoryImpl( executable: suspend (String) -> ApiResult ) { emit(Resource.Loading()) + if (!NetworkUtils.isNetworkConnected(application)) { + emit(Resource.Error(ErrorType.NoActiveInternetConnection)) + return + } when (val apiResult = executable.invoke(url)) { is ApiResult.ApiSuccess -> { emit(Resource.Success(apiResult.data)) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt index 8468984..96ed10f 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt @@ -1,11 +1,9 @@ package com.nagpal.shivam.vtucslab.screens -import android.app.Application import android.content.Context import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.core.ErrorType import com.nagpal.shivam.vtucslab.core.Resource -import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -16,7 +14,6 @@ import kotlinx.coroutines.launch object Utils { fun loadContent( uiStateFlow: MutableStateFlow>, - application: Application, fetchJob: Job?, viewModelScope: CoroutineScope, fetchExecutable: (String) -> Flow>, @@ -26,15 +23,6 @@ object Utils { if (uiStateFlow.value.stage == Stages.SUCCEEDED) { return null } - if (!NetworkUtils.isNetworkConnected(application)) { - uiStateFlow.update { - ContentState( - Stages.FAILED, - errorType = ErrorType.NoActiveInternetConnection - ) - } - return null - } fetchJob?.cancel() return viewModelScope.launch(Dispatchers.IO) { @@ -54,7 +42,12 @@ object Utils { } } is Resource.Error -> { - uiStateFlow.update { ContentState(Stages.FAILED) } + uiStateFlow.update { + ContentState( + Stages.FAILED, + errorType = resource.errorType, + ) + } } } }.launchIn(this) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt index de072b8..cc038f5 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt @@ -18,7 +18,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow class DisplayViewModel( - private val application: Application, + application: Application, private val vtuCsLabRepository: VtuCsLabRepository ) : AndroidViewModel(application) { var scrollX = 0 @@ -44,7 +44,6 @@ class DisplayViewModel( private fun loadContent(url: String) { fetchJob = Utils.loadContent( _uiState, - application, fetchJob, viewModelScope, { vtuCsLabRepository.fetchContent(it) }, diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt index ea8fef7..0c7325f 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt @@ -21,7 +21,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow class ProgramViewModel( - private val application: Application, + application: Application, private val vtuCsLabRepository: VtuCsLabRepository ) : AndroidViewModel(application) { private val initialState = @@ -45,7 +45,6 @@ class ProgramViewModel( private fun loadContent(url: String) { fetchJob = Utils.loadContent( _uiState, - application, fetchJob, viewModelScope, { vtuCsLabRepository.fetchExperiments(it) }, diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt index 2402881..a843006 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -21,7 +21,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow class RepositoryViewModel( - private val application: Application, + application: Application, private val vtuCsLabRepository: VtuCsLabRepository ) : AndroidViewModel(application) { private val initialState = ContentState(Stages.LOADING) @@ -43,7 +43,6 @@ class RepositoryViewModel( private fun loadContent(url: String) { fetchJob = Utils.loadContent( _uiState, - application, fetchJob, viewModelScope, { vtuCsLabRepository.fetchLaboratories(it) }, From 28e96230eb3be76c7f34140f4683a76748b6a5ce Mon Sep 17 00:00:00 2001 From: WallE Date: Fri, 31 Mar 2023 01:50:43 +0530 Subject: [PATCH 41/50] feature: Extend Resource to have an Error Payload and separate the ErrorPayload from the UIMessages --- .../nagpal/shivam/vtucslab/core/ErrorType.kt | 6 ++--- .../nagpal/shivam/vtucslab/core/Resource.kt | 8 +++---- .../nagpal/shivam/vtucslab/core/UIMessage.kt | 3 +++ .../shivam/vtucslab/core/UIMessageType.kt | 6 +++++ .../repositories/VtuCsLabRepository.kt | 7 +++--- .../repositories/VtuCsLabRepositoryImpl.kt | 23 ++++++++++--------- .../shivam/vtucslab/screens/ContentState.kt | 4 ++-- .../nagpal/shivam/vtucslab/screens/Utils.kt | 19 ++++++++++----- .../screens/display/DisplayFragment.kt | 5 ++-- .../screens/programs/ProgramFragment.kt | 5 ++-- .../screens/repository/RepositoryFragment.kt | 5 ++-- 11 files changed, 53 insertions(+), 38 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/core/UIMessage.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/core/UIMessageType.kt diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/core/ErrorType.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/core/ErrorType.kt index 67436c9..5cee513 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/core/ErrorType.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/core/ErrorType.kt @@ -1,6 +1,6 @@ package com.nagpal.shivam.vtucslab.core -sealed class ErrorType { - object NoActiveInternetConnection : ErrorType() - object SomeErrorOccurred : ErrorType() +enum class ErrorType { + NoActiveInternetConnection, + SomeErrorOccurred, } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt index 7835a60..8e1523e 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/core/Resource.kt @@ -1,7 +1,7 @@ package com.nagpal.shivam.vtucslab.core -sealed class Resource { - class Loading(val data: T? = null) : Resource() - class Success(val data: T?) : Resource() - class Error(val errorType: ErrorType? = null) : Resource() +sealed class Resource { + class Loading(val data: D? = null) : Resource() + class Success(val data: D?) : Resource() + class Error(val error: E) : Resource() } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/core/UIMessage.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/core/UIMessage.kt new file mode 100644 index 0000000..2d09290 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/core/UIMessage.kt @@ -0,0 +1,3 @@ +package com.nagpal.shivam.vtucslab.core + +data class UIMessage(val messageType: UIMessageType, val args: List = emptyList()) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/core/UIMessageType.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/core/UIMessageType.kt new file mode 100644 index 0000000..2c98d46 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/core/UIMessageType.kt @@ -0,0 +1,6 @@ +package com.nagpal.shivam.vtucslab.core + +enum class UIMessageType { + NoActiveInternetConnection, + SomeErrorOccurred, +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepository.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepository.kt index a4f4b8a..e0cb28b 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepository.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepository.kt @@ -1,14 +1,15 @@ package com.nagpal.shivam.vtucslab.repositories +import com.nagpal.shivam.vtucslab.core.ErrorType import com.nagpal.shivam.vtucslab.core.Resource import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse import com.nagpal.shivam.vtucslab.models.LaboratoryResponse import kotlinx.coroutines.flow.Flow interface VtuCsLabRepository { - fun fetchLaboratories(url: String): Flow> + fun fetchLaboratories(url: String): Flow> - fun fetchExperiments(url: String): Flow> + fun fetchExperiments(url: String): Flow> - fun fetchContent(url: String): Flow> + fun fetchContent(url: String): Flow> } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt index a2640cb..67fcbbb 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt @@ -16,31 +16,32 @@ import kotlinx.coroutines.flow.flow private val LOG_TAG: String = VtuCsLabRepositoryImpl::class.java.name class VtuCsLabRepositoryImpl( - val application: Application, + private val application: Application, private val vtuCsLabService: VtuCsLabService ) : VtuCsLabRepository { - override fun fetchLaboratories(url: String): Flow> = flow { - fetch(url) { - vtuCsLabService.getLaboratoryResponse(it) + override fun fetchLaboratories(url: String): Flow> = + flow { + fetch(url) { + vtuCsLabService.getLaboratoryResponse(it) + } } - } - override fun fetchExperiments(url: String): Flow> = + override fun fetchExperiments(url: String): Flow> = flow { fetch(url) { vtuCsLabService.getLaboratoryExperimentsResponse(it) } } - override fun fetchContent(url: String): Flow> = flow { + override fun fetchContent(url: String): Flow> = flow { fetch(url) { vtuCsLabService.fetchRawResponse(it) } } - private suspend fun FlowCollector>.fetch( + private suspend fun FlowCollector>.fetch( url: String, - executable: suspend (String) -> ApiResult + executable: suspend (String) -> ApiResult ) { emit(Resource.Loading()) if (!NetworkUtils.isNetworkConnected(application)) { @@ -53,11 +54,11 @@ class VtuCsLabRepositoryImpl( } is ApiResult.ApiError -> { StaticMethods.logNetworkResultError(LOG_TAG, url, apiResult.code, apiResult.message) - emit(Resource.Error()) + emit(Resource.Error(ErrorType.SomeErrorOccurred)) } is ApiResult.ApiException -> { StaticMethods.logNetworkResultException(LOG_TAG, url, apiResult.throwable) - emit(Resource.Error()) + emit(Resource.Error(ErrorType.SomeErrorOccurred)) } } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt index f41ded3..e3372a3 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt @@ -1,10 +1,10 @@ package com.nagpal.shivam.vtucslab.screens -import com.nagpal.shivam.vtucslab.core.ErrorType +import com.nagpal.shivam.vtucslab.core.UIMessage data class ContentState( val stage: String, val data: T? = null, - val errorType: ErrorType? = null, + val errorMessage: UIMessage? = null, val baseUrl: String? = null, ) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt index 96ed10f..bced7b0 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt @@ -4,6 +4,8 @@ import android.content.Context import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.core.ErrorType import com.nagpal.shivam.vtucslab.core.Resource +import com.nagpal.shivam.vtucslab.core.UIMessage +import com.nagpal.shivam.vtucslab.core.UIMessageType import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -16,7 +18,7 @@ object Utils { uiStateFlow: MutableStateFlow>, fetchJob: Job?, viewModelScope: CoroutineScope, - fetchExecutable: (String) -> Flow>, + fetchExecutable: (String) -> Flow>, getBaseUrl: (T) -> String?, url: String ): Job? { @@ -43,9 +45,13 @@ object Utils { } is Resource.Error -> { uiStateFlow.update { + val uiMessage: UIMessage = when (resource.error) { + ErrorType.NoActiveInternetConnection -> UIMessage(UIMessageType.NoActiveInternetConnection) + ErrorType.SomeErrorOccurred -> UIMessage(UIMessageType.SomeErrorOccurred) + } ContentState( Stages.FAILED, - errorType = resource.errorType, + errorMessage = uiMessage, ) } } @@ -61,10 +67,11 @@ object Utils { uiStateFlow.update { initialState } } - fun mapErrorTypeToString(context: Context, errorType: ErrorType?): String { - return when (errorType) { - ErrorType.NoActiveInternetConnection -> context.getString(R.string.no_internet_connection) - else -> context.getString(R.string.error_occurred) + fun UIMessage?.asString(context: Context): String { + return when (this?.messageType) { + UIMessageType.NoActiveInternetConnection -> context.getString(R.string.no_internet_connection) + UIMessageType.SomeErrorOccurred -> context.getString(R.string.error_occurred) + null -> context.getString(R.string.error_occurred) } } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt index 05d879b..3610cf5 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt @@ -18,7 +18,7 @@ import androidx.navigation.fragment.navArgs import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.databinding.FragmentDisplayBinding import com.nagpal.shivam.vtucslab.screens.UiEvent -import com.nagpal.shivam.vtucslab.screens.Utils +import com.nagpal.shivam.vtucslab.screens.Utils.asString import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.launch @@ -59,8 +59,7 @@ class DisplayFragment : Fragment() { }, 500) } Stages.FAILED -> { - val message: String = - Utils.mapErrorTypeToString(requireContext(), it.errorType) + val message: String = it.errorMessage.asString(requireContext()) showErrorMessage(message) } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt index 434d504..82ac68e 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt @@ -16,7 +16,7 @@ import com.nagpal.shivam.vtucslab.adapters.ContentAdapter import com.nagpal.shivam.vtucslab.databinding.FragmentProgramBinding import com.nagpal.shivam.vtucslab.models.ContentFile import com.nagpal.shivam.vtucslab.screens.UiEvent -import com.nagpal.shivam.vtucslab.screens.Utils +import com.nagpal.shivam.vtucslab.screens.Utils.asString import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.launch @@ -57,8 +57,7 @@ class ProgramFragment : Fragment() { } } Stages.FAILED -> { - val message: String = - Utils.mapErrorTypeToString(requireContext(), it.errorType) + val message: String = it.errorMessage.asString(requireContext()) showErrorMessage(message) } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt index 0eda449..e43d171 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt @@ -17,7 +17,7 @@ import com.nagpal.shivam.vtucslab.adapters.NavigationAdapter import com.nagpal.shivam.vtucslab.databinding.FragmentRepositoryBinding import com.nagpal.shivam.vtucslab.models.Laboratory import com.nagpal.shivam.vtucslab.screens.UiEvent -import com.nagpal.shivam.vtucslab.screens.Utils +import com.nagpal.shivam.vtucslab.screens.Utils.asString import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.launch @@ -57,8 +57,7 @@ class RepositoryFragment : Fragment() { } } Stages.FAILED -> { - val message: String = - Utils.mapErrorTypeToString(requireContext(), it.errorType) + val message: String = it.errorMessage.asString(requireContext()) showErrorMessage(message) } } From 22f39eeac2e684eb5c085eb888c99a9793554181 Mon Sep 17 00:00:00 2001 From: WallE Date: Fri, 31 Mar 2023 18:26:19 +0530 Subject: [PATCH 42/50] feature: Implement Toast as part of the UIState --- .../shivam/vtucslab/screens/ContentState.kt | 1 + .../shivam/vtucslab/screens/EventEmitter.kt | 5 ++++ .../nagpal/shivam/vtucslab/screens/UiEvent.kt | 1 + .../nagpal/shivam/vtucslab/screens/Utils.kt | 23 ++++++++++++++++++- .../screens/display/DisplayFragment.kt | 10 ++++++++ .../screens/display/DisplayViewModel.kt | 9 ++++++-- .../screens/programs/ProgramFragment.kt | 11 +++++++++ .../screens/programs/ProgramViewModel.kt | 9 ++++++-- .../screens/repository/RepositoryFragment.kt | 11 +++++++++ .../screens/repository/RepositoryViewModel.kt | 9 ++++++-- 10 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/screens/EventEmitter.kt diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt index e3372a3..5b3fbce 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/ContentState.kt @@ -6,5 +6,6 @@ data class ContentState( val stage: String, val data: T? = null, val errorMessage: UIMessage? = null, + val toast: UIMessage? = null, val baseUrl: String? = null, ) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/EventEmitter.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/EventEmitter.kt new file mode 100644 index 0000000..8f784a9 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/EventEmitter.kt @@ -0,0 +1,5 @@ +package com.nagpal.shivam.vtucslab.screens + +interface EventEmitter { + fun onEvent(event: T) +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/UiEvent.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/UiEvent.kt index 53a3859..7197d72 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/UiEvent.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/UiEvent.kt @@ -3,4 +3,5 @@ package com.nagpal.shivam.vtucslab.screens sealed class UiEvent { class LoadContent(val url: String) : UiEvent() class RefreshContent(val url: String) : UiEvent() + object ResetToast : UiEvent() } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt index bced7b0..886d8a8 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/Utils.kt @@ -1,6 +1,7 @@ package com.nagpal.shivam.vtucslab.screens import android.content.Context +import android.widget.Toast import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.core.ErrorType import com.nagpal.shivam.vtucslab.core.Resource @@ -23,7 +24,7 @@ object Utils { url: String ): Job? { if (uiStateFlow.value.stage == Stages.SUCCEEDED) { - return null + return fetchJob } fetchJob?.cancel() @@ -74,4 +75,24 @@ object Utils { null -> context.getString(R.string.error_occurred) } } + + fun showToast( + context: Context, + toast: Toast?, + toastUIMessage: UIMessage?, + eventEmitter: EventEmitter, + event: T + ): Toast? { + return toastUIMessage?.let { uiMessage -> + toast?.cancel() + val newToast = Toast.makeText( + context, + uiMessage.asString(context), + Toast.LENGTH_LONG + ) + newToast.show() + eventEmitter.onEvent(event) + newToast + } + } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt index 3610cf5..f3e47ef 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayFragment.kt @@ -18,6 +18,7 @@ import androidx.navigation.fragment.navArgs import com.nagpal.shivam.vtucslab.R import com.nagpal.shivam.vtucslab.databinding.FragmentDisplayBinding import com.nagpal.shivam.vtucslab.screens.UiEvent +import com.nagpal.shivam.vtucslab.screens.Utils import com.nagpal.shivam.vtucslab.screens.Utils.asString import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.Stages @@ -30,6 +31,7 @@ class DisplayFragment : Fragment() { private val binding get() = _binding!! private val displayFragmentArgs by navArgs() private val viewModel: DisplayViewModel by viewModels { DisplayViewModel.Factory } + private var toast: Toast? = null private val url: String by lazy { return@lazy "${displayFragmentArgs.baseUrl}/${displayFragmentArgs.fileName}" @@ -46,6 +48,14 @@ class DisplayFragment : Fragment() { viewModel.uiState.collect { binding.progressBar.visibility = View.GONE binding.emptyTextView.visibility = View.GONE + toast = Utils.showToast( + requireContext(), + toast, + it.toast, + viewModel, + UiEvent.ResetToast + ) + when (it.stage) { Stages.LOADING -> { binding.progressBar.visibility = View.VISIBLE diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt index cc038f5..f872397 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/display/DisplayViewModel.kt @@ -10,17 +10,19 @@ import androidx.lifecycle.viewmodel.viewModelFactory import com.nagpal.shivam.vtucslab.VTUCSLabApplication import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository import com.nagpal.shivam.vtucslab.screens.ContentState +import com.nagpal.shivam.vtucslab.screens.EventEmitter import com.nagpal.shivam.vtucslab.screens.UiEvent import com.nagpal.shivam.vtucslab.screens.Utils import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update class DisplayViewModel( application: Application, private val vtuCsLabRepository: VtuCsLabRepository -) : AndroidViewModel(application) { +) : AndroidViewModel(application), EventEmitter { var scrollX = 0 var scrollY = 0 private val initialState = ContentState(Stages.LOADING) @@ -29,7 +31,7 @@ class DisplayViewModel( val uiState = _uiState.asStateFlow() private var fetchJob: Job? = null - fun onEvent(event: UiEvent) { + override fun onEvent(event: UiEvent) { when (event) { is UiEvent.LoadContent -> { loadContent(event.url) @@ -38,6 +40,9 @@ class DisplayViewModel( resetState() loadContent(event.url) } + UiEvent.ResetToast -> { + _uiState.update { _uiState.value.copy(toast = null) } + } } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt index 82ac68e..0898680 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramFragment.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle @@ -16,6 +17,7 @@ import com.nagpal.shivam.vtucslab.adapters.ContentAdapter import com.nagpal.shivam.vtucslab.databinding.FragmentProgramBinding import com.nagpal.shivam.vtucslab.models.ContentFile import com.nagpal.shivam.vtucslab.screens.UiEvent +import com.nagpal.shivam.vtucslab.screens.Utils import com.nagpal.shivam.vtucslab.screens.Utils.asString import com.nagpal.shivam.vtucslab.utilities.Stages import kotlinx.coroutines.launch @@ -29,6 +31,7 @@ class ProgramFragment : Fragment() { private lateinit var contentAdapter: ContentAdapter private val programFragmentArgs by navArgs() + private var toast: Toast? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -43,6 +46,14 @@ class ProgramFragment : Fragment() { viewModel.uiState.collect { binding.progressBar.visibility = View.GONE binding.emptyTextView.visibility = View.GONE + toast = Utils.showToast( + requireContext(), + toast, + it.toast, + viewModel, + UiEvent.ResetToast + ) + when (it.stage) { Stages.LOADING -> { binding.progressBar.visibility = View.VISIBLE diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt index 0c7325f..4ad6ac0 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/programs/ProgramViewModel.kt @@ -11,6 +11,7 @@ import com.nagpal.shivam.vtucslab.VTUCSLabApplication import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository import com.nagpal.shivam.vtucslab.screens.ContentState +import com.nagpal.shivam.vtucslab.screens.EventEmitter import com.nagpal.shivam.vtucslab.screens.UiEvent import com.nagpal.shivam.vtucslab.screens.Utils import com.nagpal.shivam.vtucslab.utilities.Stages @@ -19,11 +20,12 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update class ProgramViewModel( application: Application, private val vtuCsLabRepository: VtuCsLabRepository -) : AndroidViewModel(application) { +) : AndroidViewModel(application), EventEmitter { private val initialState = ContentState(Stages.LOADING) private val _uiState = MutableStateFlow(initialState) @@ -31,7 +33,7 @@ class ProgramViewModel( private var fetchJob: Job? = null - fun onEvent(event: UiEvent) { + override fun onEvent(event: UiEvent) { when (event) { is UiEvent.LoadContent -> { loadContent(event.url) @@ -39,6 +41,9 @@ class ProgramViewModel( is UiEvent.RefreshContent -> { // ToDo: Handle this case } + UiEvent.ResetToast -> { + _uiState.update { _uiState.value.copy(toast = null) } + } } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt index e43d171..a12477d 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryFragment.kt @@ -4,6 +4,7 @@ import android.content.Intent import android.net.Uri import android.os.Bundle import android.view.* +import android.widget.Toast import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels @@ -17,6 +18,7 @@ import com.nagpal.shivam.vtucslab.adapters.NavigationAdapter import com.nagpal.shivam.vtucslab.databinding.FragmentRepositoryBinding import com.nagpal.shivam.vtucslab.models.Laboratory import com.nagpal.shivam.vtucslab.screens.UiEvent +import com.nagpal.shivam.vtucslab.screens.Utils import com.nagpal.shivam.vtucslab.screens.Utils.asString import com.nagpal.shivam.vtucslab.utilities.Constants import com.nagpal.shivam.vtucslab.utilities.Stages @@ -28,6 +30,7 @@ class RepositoryFragment : Fragment() { private val binding get() = _binding!! private val viewModel: RepositoryViewModel by viewModels { RepositoryViewModel.Factory } + private var toast: Toast? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -43,6 +46,14 @@ class RepositoryFragment : Fragment() { viewModel.uiState.collect { binding.progressBar.visibility = View.GONE binding.emptyTextView.visibility = View.GONE + toast = Utils.showToast( + requireContext(), + toast, + it.toast, + viewModel, + UiEvent.ResetToast + ) + when (it.stage) { Stages.LOADING -> { binding.progressBar.visibility = View.VISIBLE diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt index a843006..6865519 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/screens/repository/RepositoryViewModel.kt @@ -11,6 +11,7 @@ import com.nagpal.shivam.vtucslab.VTUCSLabApplication import com.nagpal.shivam.vtucslab.models.LaboratoryResponse import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository import com.nagpal.shivam.vtucslab.screens.ContentState +import com.nagpal.shivam.vtucslab.screens.EventEmitter import com.nagpal.shivam.vtucslab.screens.UiEvent import com.nagpal.shivam.vtucslab.screens.Utils import com.nagpal.shivam.vtucslab.utilities.Stages @@ -19,17 +20,18 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update class RepositoryViewModel( application: Application, private val vtuCsLabRepository: VtuCsLabRepository -) : AndroidViewModel(application) { +) : AndroidViewModel(application), EventEmitter { private val initialState = ContentState(Stages.LOADING) private val _uiState = MutableStateFlow(initialState) val uiState: StateFlow> = _uiState.asStateFlow() private var fetchJob: Job? = null - fun onEvent(event: UiEvent) { + override fun onEvent(event: UiEvent) { when (event) { is UiEvent.LoadContent -> { loadContent(event.url) @@ -37,6 +39,9 @@ class RepositoryViewModel( is UiEvent.RefreshContent -> { // ToDo: Handle this case } + UiEvent.ResetToast -> { + _uiState.update { _uiState.value.copy(toast = null) } + } } } From f38334b19220fcc2fa842aab9eedde16bfeb9268 Mon Sep 17 00:00:00 2001 From: WallE Date: Sat, 1 Apr 2023 02:03:13 +0530 Subject: [PATCH 43/50] refractor: Extract all the dependencies versions into centralized versions.gradle file --- app/build.gradle | 34 +++++++++++++++------------------- build.gradle | 12 ++++++------ versions.gradle | 18 ++++++++++++++++++ 3 files changed, 39 insertions(+), 25 deletions(-) create mode 100644 versions.gradle diff --git a/app/build.gradle b/app/build.gradle index e46f699..3209caa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,38 +26,34 @@ android { } dependencies { - def retrofitVersion = '2.9.0' - def navigationVersion = "2.5.3" - - implementation 'androidx.core:core-ktx:1.9.0' - implementation platform('com.google.firebase:firebase-bom:31.2.3') + implementation platform("com.google.firebase:firebase-bom:$versions.firebase_bom") implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'androidx.core:core-ktx:1.9.0' - implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'com.google.android.material:material:1.8.0' - implementation "androidx.multidex:multidex:2.0.1" + implementation "androidx.core:core-ktx:$versions.core_ktx" + implementation "androidx.appcompat:appcompat:$versions.appcompat" + implementation "androidx.constraintlayout:constraintlayout:$versions.constraintlayout" + implementation "com.google.android.material:material:$versions.material" + implementation "androidx.multidex:multidex:$versions.multidex" // Navigation - implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion" - implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion" + implementation "androidx.navigation:navigation-fragment-ktx:$versions.navigation" + implementation "androidx.navigation:navigation-ui-ktx:$versions.navigation" // https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-kotlin - implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.14.2' + implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$versions.jackson_module_kotlin" // Firebase SDK implementation 'com.google.firebase:firebase-messaging-ktx' implementation 'com.google.firebase:firebase-crashlytics-ktx' // Retrofit - implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" - implementation "com.squareup.retrofit2:converter-jackson:$retrofitVersion" - implementation "com.squareup.retrofit2:converter-scalars:$retrofitVersion" + implementation "com.squareup.retrofit2:retrofit:$versions.retrofit" + implementation "com.squareup.retrofit2:converter-jackson:$versions.retrofit" + implementation "com.squareup.retrofit2:converter-scalars:$versions.retrofit" - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test:runner:1.5.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + testImplementation "junit:junit:$versions.junit" + androidTestImplementation "androidx.test:runner:$versions.test_runner" + androidTestImplementation "androidx.test.espresso:espresso-core:$versions.espresso_core" } apply plugin: 'com.google.gms.google-services' diff --git a/build.gradle b/build.gradle index c9ccf43..297ecb5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,18 +1,18 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + apply from: 'versions.gradle' repositories { google() mavenCentral() } dependencies { - def navigationVersion = "2.5.3" - classpath 'com.android.tools.build:gradle:7.4.2' - classpath 'com.google.gms:google-services:4.3.15' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.4' - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0' - classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion" + classpath "com.android.tools.build:gradle:$versions.gradle" + classpath "com.google.gms:google-services:$versions.google_services" + classpath "com.google.firebase:firebase-crashlytics-gradle:$versions.firebase_crashlytics_gradle" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin_gradle_plugin" + classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$versions.navigation" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/versions.gradle b/versions.gradle new file mode 100644 index 0000000..39f2832 --- /dev/null +++ b/versions.gradle @@ -0,0 +1,18 @@ +def versions = [:] +versions.appcompat = "1.6.1" +versions.constraintlayout = "2.1.4" +versions.core_ktx = "1.9.0" +versions.espresso_core = "3.5.1" +versions.firebase_bom = "31.2.3" +versions.firebase_crashlytics_gradle = "2.9.4" +versions.google_services = "4.3.15" +versions.gradle = "7.4.2" +versions.jackson_module_kotlin = "2.14.2" +versions.junit = "4.13.2" +versions.kotlin_gradle_plugin = "1.8.0" +versions.material = "1.8.0" +versions.multidex = "2.0.1" +versions.navigation = "2.5.3" +versions.retrofit = "2.9.0" +versions.test_runner = "1.5.2" +ext.versions = versions From 41e9421a54f3bf66de86c1a9d20b4632b8d4909a Mon Sep 17 00:00:00 2001 From: WallE Date: Sat, 1 Apr 2023 02:25:32 +0530 Subject: [PATCH 44/50] feature: Change the color of the Navigation Bar to black --- app/src/main/res/values-v21/styles.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 app/src/main/res/values-v21/styles.xml diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml new file mode 100644 index 0000000..de7fde9 --- /dev/null +++ b/app/src/main/res/values-v21/styles.xml @@ -0,0 +1,12 @@ + + + + + From e03297ea02354e26789dd82dacd11b7bc21dda02 Mon Sep 17 00:00:00 2001 From: WallE Date: Tue, 11 Apr 2023 00:18:21 +0530 Subject: [PATCH 45/50] feature: Integrate Room DB to store and fetch the LabResponse --- app/build.gradle | 5 + .../shivam/vtucslab/VTUCSLabApplication.kt | 27 +++- .../shivam/vtucslab/data/local/AppDatabase.kt | 11 ++ .../shivam/vtucslab/data/local/Converters.kt | 16 +++ .../shivam/vtucslab/data/local/DBConstants.kt | 12 ++ .../shivam/vtucslab/data/local/LabResponse.kt | 19 +++ .../vtucslab/data/local/LabResponseDao.kt | 16 +++ .../vtucslab/data/local/LabResponseType.kt | 7 ++ .../repositories/VtuCsLabRepositoryImpl.kt | 117 +++++++++++++++--- .../vtucslab/utilities/Configurations.kt | 5 + .../shivam/vtucslab/utilities/Constants.kt | 1 + .../vtucslab/utilities/StaticMethods.kt | 7 ++ app/src/main/res/layout/fragment_display.xml | 4 +- app/src/main/res/layout/fragment_program.xml | 4 +- .../main/res/layout/fragment_repository.xml | 4 +- app/src/main/res/values/strings.xml | 2 +- app/src/main/res/values/styles.xml | 10 ++ versions.gradle | 1 + 18 files changed, 240 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/data/local/AppDatabase.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/data/local/Converters.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/data/local/DBConstants.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/data/local/LabResponse.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/data/local/LabResponseDao.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/data/local/LabResponseType.kt create mode 100644 app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Configurations.kt diff --git a/app/build.gradle b/app/build.gradle index 3209caa..145e179 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,6 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' apply plugin: 'com.google.firebase.crashlytics' apply plugin: 'androidx.navigation.safeargs' @@ -39,6 +41,9 @@ dependencies { implementation "androidx.navigation:navigation-fragment-ktx:$versions.navigation" implementation "androidx.navigation:navigation-ui-ktx:$versions.navigation" + implementation "androidx.room:room-runtime:$versions.room" + kapt "androidx.room:room-compiler:$versions.room" + // https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-kotlin implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$versions.jackson_module_kotlin" diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt index 93685ba..d390c02 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/VTUCSLabApplication.kt @@ -1,19 +1,42 @@ package com.nagpal.shivam.vtucslab import androidx.multidex.MultiDexApplication +import androidx.room.Room import com.google.firebase.crashlytics.FirebaseCrashlytics +import com.nagpal.shivam.vtucslab.data.local.AppDatabase import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepository import com.nagpal.shivam.vtucslab.repositories.VtuCsLabRepositoryImpl import com.nagpal.shivam.vtucslab.services.VtuCsLabService +import com.nagpal.shivam.vtucslab.utilities.Constants.VTU_CS_LAB +import com.nagpal.shivam.vtucslab.utilities.StaticMethods class VTUCSLabApplication : MultiDexApplication() { - val vtuCsLabRepository: VtuCsLabRepository = - VtuCsLabRepositoryImpl(this, VtuCsLabService.instance) + private lateinit var _db: AppDatabase + val db: AppDatabase + get() = _db + + private lateinit var _vtuCsLabRepository: VtuCsLabRepository + val vtuCsLabRepository: VtuCsLabRepository + get() = _vtuCsLabRepository override fun onCreate() { super.onCreate() if (BuildConfig.DEBUG) { FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false) } + + _db = Room.databaseBuilder( + applicationContext, + AppDatabase::class.java, + VTU_CS_LAB + ).build() + + val vtuCsLabService = VtuCsLabService.instance + _vtuCsLabRepository = VtuCsLabRepositoryImpl( + this, + vtuCsLabService, + _db.labResponseDao(), + StaticMethods.jsonMapper + ) } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/AppDatabase.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/AppDatabase.kt new file mode 100644 index 0000000..2a39579 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/AppDatabase.kt @@ -0,0 +1,11 @@ +package com.nagpal.shivam.vtucslab.data.local + +import androidx.room.Database +import androidx.room.RoomDatabase +import androidx.room.TypeConverters + +@Database(entities = [LabResponse::class], version = 1) +@TypeConverters(Converters::class) +abstract class AppDatabase : RoomDatabase() { + abstract fun labResponseDao(): LabResponseDao +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/Converters.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/Converters.kt new file mode 100644 index 0000000..ee97e52 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/Converters.kt @@ -0,0 +1,16 @@ +package com.nagpal.shivam.vtucslab.data.local + +import androidx.room.TypeConverter +import java.util.* + +class Converters { + @TypeConverter + fun dateFromTimestamp(value: Long?): Date? { + return value?.let { Date(it) } + } + + @TypeConverter + fun dateToTimestamp(date: Date?): Long? { + return date?.time + } +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/DBConstants.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/DBConstants.kt new file mode 100644 index 0000000..57c0b6e --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/DBConstants.kt @@ -0,0 +1,12 @@ +package com.nagpal.shivam.vtucslab.data.local + +object Tables { + const val LAB_RESPONSE = "lab_response" +} + +object LabResponseAttributes { + const val URL = "url" + const val RESPONSE = "response" + const val RESPONSE_TYPE = "response_type" + const val FETCHED_AT = "fetched_at" +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/LabResponse.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/LabResponse.kt new file mode 100644 index 0000000..f0a9ea4 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/LabResponse.kt @@ -0,0 +1,19 @@ +package com.nagpal.shivam.vtucslab.data.local + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.nagpal.shivam.vtucslab.data.local.LabResponseAttributes.FETCHED_AT +import com.nagpal.shivam.vtucslab.data.local.LabResponseAttributes.RESPONSE +import com.nagpal.shivam.vtucslab.data.local.LabResponseAttributes.RESPONSE_TYPE +import com.nagpal.shivam.vtucslab.data.local.LabResponseAttributes.URL +import com.nagpal.shivam.vtucslab.data.local.Tables.LAB_RESPONSE +import java.util.* + +@Entity(LAB_RESPONSE) +data class LabResponse( + @PrimaryKey @ColumnInfo(name = URL) val url: String, + @ColumnInfo(name = RESPONSE) val response: String, + @ColumnInfo(name = RESPONSE_TYPE) val responseType: LabResponseType, + @ColumnInfo(name = FETCHED_AT) val fetchedAt: Date, +) diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/LabResponseDao.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/LabResponseDao.kt new file mode 100644 index 0000000..f8d7b74 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/LabResponseDao.kt @@ -0,0 +1,16 @@ +package com.nagpal.shivam.vtucslab.data.local + +import androidx.room.Dao +import androidx.room.Query +import androidx.room.Upsert +import com.nagpal.shivam.vtucslab.data.local.LabResponseAttributes.URL +import com.nagpal.shivam.vtucslab.data.local.Tables.LAB_RESPONSE + +@Dao +interface LabResponseDao { + @Upsert + fun upsert(labResponse: LabResponse) + + @Query("SELECT * FROM $LAB_RESPONSE where $URL = :url") + fun findByUrl(url: String): LabResponse? +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/LabResponseType.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/LabResponseType.kt new file mode 100644 index 0000000..90347b9 --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/data/local/LabResponseType.kt @@ -0,0 +1,7 @@ +package com.nagpal.shivam.vtucslab.data.local + +enum class LabResponseType { + LABORATORY, + EXPERIMENT, + CONTENT, +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt index 67fcbbb..897129c 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/repositories/VtuCsLabRepositoryImpl.kt @@ -1,65 +1,150 @@ package com.nagpal.shivam.vtucslab.repositories import android.app.Application +import com.fasterxml.jackson.core.JsonParseException +import com.fasterxml.jackson.databind.json.JsonMapper import com.nagpal.shivam.vtucslab.core.ErrorType import com.nagpal.shivam.vtucslab.core.Resource +import com.nagpal.shivam.vtucslab.data.local.LabResponse +import com.nagpal.shivam.vtucslab.data.local.LabResponseDao +import com.nagpal.shivam.vtucslab.data.local.LabResponseType import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse import com.nagpal.shivam.vtucslab.models.LaboratoryResponse import com.nagpal.shivam.vtucslab.retrofit.ApiResult import com.nagpal.shivam.vtucslab.services.VtuCsLabService +import com.nagpal.shivam.vtucslab.utilities.Configurations import com.nagpal.shivam.vtucslab.utilities.NetworkUtils import com.nagpal.shivam.vtucslab.utilities.StaticMethods import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.flow +import java.util.Date private val LOG_TAG: String = VtuCsLabRepositoryImpl::class.java.name class VtuCsLabRepositoryImpl( private val application: Application, - private val vtuCsLabService: VtuCsLabService + private val vtuCsLabService: VtuCsLabService, + private val labResponseDao: LabResponseDao, + private val jsonMapper: JsonMapper, ) : VtuCsLabRepository { override fun fetchLaboratories(url: String): Flow> = flow { - fetch(url) { - vtuCsLabService.getLaboratoryResponse(it) + fetch( + flow = this, + url, + LabResponseType.LABORATORY, + vtuCsLabService::getLaboratoryResponse, + { data -> jsonMapper.writeValueAsString(data) } + ) { stringContent -> + jsonMapper.readValue( + stringContent, + LaboratoryResponse::class.java + ) } } override fun fetchExperiments(url: String): Flow> = flow { - fetch(url) { - vtuCsLabService.getLaboratoryExperimentsResponse(it) + fetch( + flow = this, + url, + LabResponseType.EXPERIMENT, + vtuCsLabService::getLaboratoryExperimentsResponse, + { data -> jsonMapper.writeValueAsString(data) } + ) { stringContent -> + jsonMapper.readValue( + stringContent, + LaboratoryExperimentResponse::class.java + ) } } override fun fetchContent(url: String): Flow> = flow { - fetch(url) { - vtuCsLabService.fetchRawResponse(it) - } + fetch( + flow = this, + url, + LabResponseType.CONTENT, + vtuCsLabService::fetchRawResponse, + { stringContent -> stringContent } + ) { stringContent -> stringContent } } - private suspend fun FlowCollector>.fetch( + private suspend fun fetch( + flow: FlowCollector>, url: String, - executable: suspend (String) -> ApiResult + labResponseType: LabResponseType, + fetchFromNetwork: suspend (String) -> ApiResult, + encodeToString: (D) -> String, + decodeFromString: (String) -> D, ) { - emit(Resource.Loading()) + flow.emit(Resource.Loading()) + + val labResponse = labResponseDao.findByUrl(url) + var foundInDB = false + labResponse?.let { + try { + flow.emit(Resource.Success(decodeFromString.invoke(it.response))) + foundInDB = true + if (it.fetchedAt.after( + StaticMethods.getCurrentDateMinusSeconds(Configurations.RESPONSE_FRESHNESS_TIME) + ) + ) { + return + } + } catch (_: JsonParseException) { + } + } + if (!NetworkUtils.isNetworkConnected(application)) { - emit(Resource.Error(ErrorType.NoActiveInternetConnection)) + emitNetworkErrors( + flow, + foundInDB, + Resource.Error(ErrorType.NoActiveInternetConnection), + ) return } - when (val apiResult = executable.invoke(url)) { + when (val apiResult = fetchFromNetwork.invoke(url)) { is ApiResult.ApiSuccess -> { - emit(Resource.Success(apiResult.data)) + val data = apiResult.data + labResponseDao.upsert( + LabResponse( + url, + encodeToString(data), + labResponseType, + Date(), + ) + ) + flow.emit(Resource.Success(data)) } + is ApiResult.ApiError -> { StaticMethods.logNetworkResultError(LOG_TAG, url, apiResult.code, apiResult.message) - emit(Resource.Error(ErrorType.SomeErrorOccurred)) + emitNetworkErrors( + flow, + foundInDB, + Resource.Error(ErrorType.SomeErrorOccurred), + ) } + is ApiResult.ApiException -> { StaticMethods.logNetworkResultException(LOG_TAG, url, apiResult.throwable) - emit(Resource.Error(ErrorType.SomeErrorOccurred)) + emitNetworkErrors( + flow, + foundInDB, + Resource.Error(ErrorType.SomeErrorOccurred), + ) } } } + + private suspend fun emitNetworkErrors( + flow: FlowCollector>, + foundInDB: Boolean, + errorResource: Resource.Error + ) { + if (!foundInDB) { + flow.emit(errorResource) + } + } } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Configurations.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Configurations.kt new file mode 100644 index 0000000..60f0e7a --- /dev/null +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Configurations.kt @@ -0,0 +1,5 @@ +package com.nagpal.shivam.vtucslab.utilities + +object Configurations { + const val RESPONSE_FRESHNESS_TIME = 3 * 60 * 60 +} diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt index a6e4506..378bab7 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/Constants.kt @@ -8,4 +8,5 @@ object Constants { "https://raw.githubusercontent.com/vtucs/Index_v3/master/Index_v3.json" const val LABEL_CODE = "Code" const val GITHUB_RAW_CONTENT = "github_raw_content" + const val VTU_CS_LAB = "vtu_cs_lab" } diff --git a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt index b2c685e..4288884 100644 --- a/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt +++ b/app/src/main/java/com/nagpal/shivam/vtucslab/utilities/StaticMethods.kt @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.json.JsonMapper import com.nagpal.shivam.vtucslab.models.LaboratoryExperimentResponse import com.nagpal.shivam.vtucslab.models.LaboratoryResponse +import java.util.* object StaticMethods { @@ -49,4 +50,10 @@ object StaticMethods { throwable ) } + + fun getCurrentDateMinusSeconds(seconds: Int): Date { + val calendar = Calendar.getInstance() + calendar.add(Calendar.SECOND, -seconds) + return calendar.time + } } diff --git a/app/src/main/res/layout/fragment_display.xml b/app/src/main/res/layout/fragment_display.xml index 5961aa8..9a6f5d5 100644 --- a/app/src/main/res/layout/fragment_display.xml +++ b/app/src/main/res/layout/fragment_display.xml @@ -38,9 +38,7 @@ VTU CS Lab VTU CS Lab New Version - No Internet Connection. + No Internet Connection.\n\nThis content is not available offline yet, Kindly connect to the internet to sync this content locally. Some Error Occurred. Copy Git Repository diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 58b3344..1ca91d6 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -14,6 +14,16 @@ @color/colorAccent + +