diff --git a/app/src/main/java/com/itsaky/androidide/adapters/EditorBottomSheetTabAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/EditorBottomSheetTabAdapter.java index 5bf6abf12a..7f0e924816 100644 --- a/app/src/main/java/com/itsaky/androidide/adapters/EditorBottomSheetTabAdapter.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/EditorBottomSheetTabAdapter.java @@ -29,7 +29,7 @@ import com.itsaky.androidide.fragments.DiagnosticsListFragment; import com.itsaky.androidide.fragments.IDELogFragment; import com.itsaky.androidide.fragments.SearchResultFragment; -import com.itsaky.androidide.fragments.SimpleOutputFragment; +import com.itsaky.androidide.fragments.BuildOutputFragment; import com.itsaky.androidide.utils.ILogger; import java.util.ArrayList; @@ -48,7 +48,7 @@ public EditorBottomSheetTabAdapter(@NonNull FragmentActivity fragmentActivity) { this.fragments.add( new Tab( fragmentActivity.getString(R.string.build_output), - SimpleOutputFragment.class, + BuildOutputFragment.class, ++index)); this.fragments.add( new Tab(fragmentActivity.getString(R.string.app_logs), AppLogFragment.class, ++index)); @@ -117,8 +117,8 @@ public String getTitle(int position) { } @Nullable - public SimpleOutputFragment getBuildOutputFragment() { - return findFragmentByClass(SimpleOutputFragment.class); + public BuildOutputFragment getBuildOutputFragment() { + return findFragmentByClass(BuildOutputFragment.class); } @Nullable diff --git a/app/src/main/java/com/itsaky/androidide/fragments/SimpleOutputFragment.kt b/app/src/main/java/com/itsaky/androidide/fragments/BuildOutputFragment.kt similarity index 82% rename from app/src/main/java/com/itsaky/androidide/fragments/SimpleOutputFragment.kt rename to app/src/main/java/com/itsaky/androidide/fragments/BuildOutputFragment.kt index 7ef2c2a185..212c92709d 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/SimpleOutputFragment.kt +++ b/app/src/main/java/com/itsaky/androidide/fragments/BuildOutputFragment.kt @@ -19,11 +19,13 @@ package com.itsaky.androidide.fragments import android.os.Bundle import android.view.View import com.blankj.utilcode.util.ThreadUtils +import com.itsaky.androidide.R -class SimpleOutputFragment : NonEditableEditorFragment() { +class BuildOutputFragment : NonEditableEditorFragment() { private val unsavedLines: MutableList = ArrayList() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + emptyStateViewModel.emptyMessage.value = getString(R.string.msg_emptyview_buildoutput) if (unsavedLines.isNotEmpty()) { for (line in unsavedLines) { editor?.append("${line!!.trim()}\n") @@ -48,7 +50,9 @@ class SimpleOutputFragment : NonEditableEditorFragment() { } else { "${output}\n" } - editor!!.append(message) + editor!!.append(message).also { + emptyStateViewModel.isEmpty.value = false + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/itsaky/androidide/fragments/DiagnosticsListFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/DiagnosticsListFragment.java deleted file mode 100644 index 2b91acd8a0..0000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/DiagnosticsListFragment.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.fragments; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.itsaky.androidide.adapters.DiagnosticsAdapter; -import com.itsaky.androidide.ui.EmptyView; - -import java.util.ArrayList; - -public class DiagnosticsListFragment extends Fragment { - - private RecyclerView list; - private EmptyView emptyView; - - private DiagnosticsAdapter unsavedAdapter; - - @Nullable - @Override - public View onCreateView( - @NonNull LayoutInflater inflater, - @Nullable ViewGroup parent, - @Nullable Bundle savedInstanceState) { - final var container = new LinearLayout(inflater.getContext()); - container.setOrientation(LinearLayout.VERTICAL); - container.setLayoutParams(new ViewGroup.LayoutParams(-1, -1)); - - this.list = new RecyclerView(inflater.getContext()); - this.emptyView = new EmptyView(inflater.getContext()); - - container.addView(list, new LinearLayout.LayoutParams(-1, -1)); - container.addView(emptyView, new LinearLayout.LayoutParams(-1, -1)); - - return container; - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - list.setLayoutManager(new LinearLayoutManager(list.getContext())); - list.setAdapter( - unsavedAdapter != null ? unsavedAdapter : new DiagnosticsAdapter(new ArrayList<>(), null)); - unsavedAdapter = null; - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unsavedAdapter = null; - list = null; - emptyView = null; - } - - public void setAdapter(@NonNull DiagnosticsAdapter adapter) { - if (list != null) { - list.setAdapter(adapter); - } else { - unsavedAdapter = adapter; - } - } - - public void handleResultVisibility(boolean error) { - if (emptyView != null && list != null) { - emptyView.setVisibility(error ? View.VISIBLE : View.GONE); - list.setVisibility(error ? View.GONE : View.VISIBLE); - } - } -} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/DiagnosticsListFragment.kt b/app/src/main/java/com/itsaky/androidide/fragments/DiagnosticsListFragment.kt new file mode 100644 index 0000000000..8004260eb8 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/fragments/DiagnosticsListFragment.kt @@ -0,0 +1,35 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ +package com.itsaky.androidide.fragments + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import com.itsaky.androidide.R +import com.itsaky.androidide.adapters.DiagnosticsAdapter + +class DiagnosticsListFragment : RecyclerViewFragment() { + + override fun onCreateAdapter(): RecyclerView.Adapter<*> { + return DiagnosticsAdapter(ArrayList(), null) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + emptyStateViewModel.emptyMessage.value = getString(R.string.msg_emptyview_diagnostics) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/itsaky/androidide/fragments/EmptyStateFragment.kt b/app/src/main/java/com/itsaky/androidide/fragments/EmptyStateFragment.kt new file mode 100644 index 0000000000..d1818883ff --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/fragments/EmptyStateFragment.kt @@ -0,0 +1,79 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.fragments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.viewModels +import androidx.viewbinding.ViewBinding +import com.itsaky.androidide.databinding.FragmentEmptyStateBinding +import com.itsaky.androidide.viewmodel.EmptyStateFragmentViewModel + +/** + * A fragment that shows a message when there is no data to show in the subclass fragment. + * + * @author Akash Yadav + */ +abstract class EmptyStateFragment(layout: Int, + bind: (View) -> T) : FragmentWithBinding(layout, bind) { + + protected var emptyStateBinding: FragmentEmptyStateBinding? = null + private set + + protected val emptyStateViewModel by viewModels() + + internal var isEmpty: Boolean + get() = emptyStateViewModel.isEmpty.value ?: false + set(value) { + emptyStateViewModel.isEmpty.value = value + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View { + + return FragmentEmptyStateBinding.inflate(inflater, container, false).also { emptyStateBinding -> + this.emptyStateBinding = emptyStateBinding + + // add the main fragment view + emptyStateBinding.root.addView( + super.onCreateView(inflater, emptyStateBinding.root, savedInstanceState) + ) + }.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + emptyStateViewModel.isEmpty.observe(viewLifecycleOwner) { isEmpty -> + emptyStateBinding?.apply { + root.displayedChild = if (isEmpty) 0 else 1 + } + } + + emptyStateViewModel.emptyMessage.observe(viewLifecycleOwner) { message -> + emptyStateBinding?.emptyView?.message = message + } + } + + override fun onDestroyView() { + this.emptyStateBinding = null + super.onDestroyView() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/itsaky/androidide/fragments/LogFragments.kt b/app/src/main/java/com/itsaky/androidide/fragments/LogFragments.kt index df08e4d845..0af0d2a634 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/LogFragments.kt +++ b/app/src/main/java/com/itsaky/androidide/fragments/LogFragments.kt @@ -17,6 +17,9 @@ package com.itsaky.androidide.fragments +import android.os.Bundle +import android.view.View +import com.itsaky.androidide.R import com.itsaky.androidide.utils.ILogger /** @@ -43,6 +46,11 @@ class IDELogFragment : LogViewFragment() { override fun isSimpleFormattingEnabled() = true override fun getFilename() = "ide_logs" + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + emptyStateViewModel.emptyMessage.value = getString(R.string.msg_emptyview_idelogs) + } + override fun onDestroy() { super.onDestroy() ILogger.removeLogListener(logListener) @@ -57,4 +65,9 @@ class IDELogFragment : LogViewFragment() { class AppLogFragment : LogViewFragment() { override fun isSimpleFormattingEnabled() = false override fun getFilename() = "app_logs" + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + emptyStateViewModel.emptyMessage.value = getString(R.string.msg_emptyview_applogs) + } } diff --git a/app/src/main/java/com/itsaky/androidide/fragments/LogViewFragment.kt b/app/src/main/java/com/itsaky/androidide/fragments/LogViewFragment.kt index b350230829..c54cd40a36 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/LogViewFragment.kt +++ b/app/src/main/java/com/itsaky/androidide/fragments/LogViewFragment.kt @@ -20,11 +20,9 @@ package com.itsaky.androidide.fragments import android.os.Bundle import android.os.Handler import android.os.Looper -import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment import com.blankj.utilcode.util.ThreadUtils +import com.itsaky.androidide.R import com.itsaky.androidide.databinding.FragmentLogBinding import com.itsaky.androidide.editor.language.log.LogLanguage import com.itsaky.androidide.editor.schemes.IDEColorSchemeProvider @@ -44,7 +42,9 @@ import kotlin.math.min * * @author Akash Yadav */ -abstract class LogViewFragment : Fragment(), ShareableOutputFragment { +abstract class LogViewFragment : + EmptyStateFragment(R.layout.fragment_log, FragmentLogBinding::bind), + ShareableOutputFragment { companion object { @@ -78,8 +78,6 @@ abstract class LogViewFragment : Fragment(), ShareableOutputFragment { const val MAX_LINE_COUNT = TRIM_ON_LINE_COUNT - 300 } - var binding: FragmentLogBinding? = null - private var lastLog = -1L private val cacheLock = ReentrantLock() @@ -161,7 +159,13 @@ abstract class LogViewFragment : Fragment(), ShareableOutputFragment { } private fun append(chars: CharSequence?) { - chars?.let { ThreadUtils.runOnUiThread { binding?.editor?.append(chars) } } + chars?.let { + ThreadUtils.runOnUiThread { + _binding?.editor?.append(chars)?.also { + emptyStateViewModel.isEmpty.value = false + } + } + } } private fun trimLinesAtStart() { @@ -171,7 +175,7 @@ abstract class LogViewFragment : Fragment(), ShareableOutputFragment { } ThreadUtils.runOnUiThread { - binding?.editor?.text?.apply { + _binding?.editor?.text?.apply { if (lineCount <= TRIM_ON_LINE_COUNT) { isTrimming.set(false) return@apply @@ -193,18 +197,9 @@ abstract class LogViewFragment : Fragment(), ShareableOutputFragment { appendLog(line) } - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle?, - ): View { - binding = FragmentLogBinding.inflate(inflater, container, false) - return binding!!.root - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val editor = this.binding!!.editor + val editor = this.binding.editor editor.props.autoIndent = false editor.isEditable = false editor.dividerWidth = 0f @@ -246,24 +241,17 @@ abstract class LogViewFragment : Fragment(), ShareableOutputFragment { } override fun onDestroyView() { - binding?.editor?.release() + _binding?.editor?.release() super.onDestroyView() } - override fun onDestroy() { - super.onDestroy() - this.binding = null - } - override fun getContent(): String { - if (this.binding == null) { - return "" - } - - return this.binding!!.editor.text.toString() + return this._binding?.editor?.text?.toString() ?: "" } override fun clearOutput() { - binding?.editor?.setText("") + _binding?.editor?.setText("")?.also { + emptyStateViewModel.isEmpty.value = true + } } } diff --git a/app/src/main/java/com/itsaky/androidide/fragments/NonEditableEditorFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/NonEditableEditorFragment.java index d15e13736a..79614912b8 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/NonEditableEditorFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/NonEditableEditorFragment.java @@ -18,40 +18,29 @@ package com.itsaky.androidide.fragments; import android.os.Bundle; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; - +import com.itsaky.androidide.R; import com.itsaky.androidide.databinding.FragmentNonEditableEditorBinding; +import com.itsaky.androidide.editor.ui.IDEEditor; import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE; import com.itsaky.androidide.utils.TypefaceUtilsKt; -import com.itsaky.androidide.editor.ui.IDEEditor; - import io.github.rosemoe.sora.lang.EmptyLanguage; -public abstract class NonEditableEditorFragment extends Fragment +public abstract class NonEditableEditorFragment extends + EmptyStateFragment implements ShareableOutputFragment { - - private FragmentNonEditableEditorBinding binding; - @Nullable - @Override - public View onCreateView( - @NonNull LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - this.binding = FragmentNonEditableEditorBinding.inflate(inflater, container, false); - return binding.getRoot(); + public NonEditableEditorFragment() { + super(R.layout.fragment_non_editable_editor, FragmentNonEditableEditorBinding::bind); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - final var editor = binding.getRoot(); + getEmptyStateViewModel().getEmptyMessage().setValue(createEmptyStateMessage()); + final var editor = getBinding().getRoot(); editor.setEditable(false); editor.setDividerWidth(0); editor.setEditorLanguage(new EmptyLanguage()); @@ -63,10 +52,8 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat editor.setColorScheme(SchemeAndroidIDE.newInstance(requireContext())); } - @Override - public void onDestroyView() { - super.onDestroyView(); - binding = null; + private CharSequence createEmptyStateMessage() { + return null; } @NonNull @@ -82,6 +69,7 @@ public String getContent() { @Nullable public IDEEditor getEditor() { + final var binding = get_binding(); if (binding == null) { return null; } @@ -103,5 +91,6 @@ public void clearOutput() { // Editing CodeEditor's content is a synchronized operation editor.getText().delete(0, editor.getText().length()); + getEmptyStateViewModel().isEmpty().setValue(true); } } diff --git a/app/src/main/java/com/itsaky/androidide/fragments/RecyclerViewFragment.kt b/app/src/main/java/com/itsaky/androidide/fragments/RecyclerViewFragment.kt new file mode 100644 index 0000000000..b97333188f --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/fragments/RecyclerViewFragment.kt @@ -0,0 +1,81 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.fragments + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.LayoutManager +import com.itsaky.androidide.R +import com.itsaky.androidide.databinding.FragmentRecyclerviewBinding + +/** + * A fragment which shows a [RecyclerView]. + * + * @author Akash Yadav + */ +abstract class RecyclerViewFragment> : + EmptyStateFragment(R.layout.fragment_recyclerview, + FragmentRecyclerviewBinding::bind) { + + private var unsavedAdapter: A? = null + + /** + * Creates the adapter for the [RecyclerView]. + */ + protected abstract fun onCreateAdapter(): RecyclerView.Adapter<*> + + /** + * Creates the layout manager for the [RecyclerView]. + */ + protected open fun onCreateLayoutManager(): LayoutManager { + return LinearLayoutManager(requireContext()) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.root.apply { + layoutManager = onCreateLayoutManager() + adapter = unsavedAdapter ?: onCreateAdapter() + } + + unsavedAdapter = null + + checkIsEmpty() + } + + override fun onDestroyView() { + super.onDestroyView() + unsavedAdapter = null + } + + /** + * Set the adapter for the [RecyclerView]. + */ + fun setAdapter(adapter: A) { + _binding?.root?.let { list -> list.adapter = adapter } ?: run { unsavedAdapter = adapter } + checkIsEmpty() + } + + private fun checkIsEmpty() { + if (_binding?.root?.adapter?.itemCount == 0) { + emptyStateViewModel.isEmpty.value = true + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/itsaky/androidide/fragments/SearchResultFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/SearchResultFragment.java deleted file mode 100644 index e4bfffa2b6..0000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/SearchResultFragment.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.fragments; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.itsaky.androidide.adapters.SearchListAdapter; -import com.itsaky.androidide.ui.EmptyView; - -import java.util.Collections; - -import kotlin.Unit; - -public class SearchResultFragment extends Fragment { - - private RecyclerView list; - private EmptyView emptyView; - - private SearchListAdapter unsavedAdapter; - - @Nullable - @Override - public View onCreateView( - @NonNull LayoutInflater inflater, - @Nullable ViewGroup parent, - @Nullable Bundle savedInstanceState) { - final var container = new LinearLayout(inflater.getContext()); - container.setOrientation(LinearLayout.VERTICAL); - container.setLayoutParams(new ViewGroup.LayoutParams(-1, -1)); - - this.list = new RecyclerView(inflater.getContext()); - this.emptyView = new EmptyView(inflater.getContext()); - - container.addView(list, new LinearLayout.LayoutParams(-1, -1)); - container.addView(emptyView, new LinearLayout.LayoutParams(-1, -1)); - - return container; - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - list.setLayoutManager(new LinearLayoutManager(list.getContext())); - list.setAdapter( - unsavedAdapter != null - ? unsavedAdapter - : new SearchListAdapter(Collections.emptyMap(), this::noOp, this::noOp)); - unsavedAdapter = null; - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unsavedAdapter = null; - list = null; - emptyView = null; - } - - public void setAdapter(@NonNull final SearchListAdapter adapter) { - if (list == null) { - unsavedAdapter = adapter; - return; - } - this.list.setAdapter(adapter); - } - - public void handleResultVisibility(boolean error) { - if (emptyView != null && list != null) { - emptyView.setVisibility(error ? View.VISIBLE : View.GONE); - list.setVisibility(error ? View.GONE : View.VISIBLE); - } - } - - private Unit noOp(Object obj) { - return Unit.INSTANCE; - } -} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/SearchResultFragment.kt b/app/src/main/java/com/itsaky/androidide/fragments/SearchResultFragment.kt new file mode 100644 index 0000000000..a4389fb35b --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/fragments/SearchResultFragment.kt @@ -0,0 +1,28 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ +package com.itsaky.androidide.fragments + +import androidx.recyclerview.widget.RecyclerView +import com.itsaky.androidide.adapters.SearchListAdapter + +class SearchResultFragment : RecyclerViewFragment() { + + override fun onCreateAdapter(): RecyclerView.Adapter<*> { + val noOp: (Any) -> Unit = {} + return SearchListAdapter(emptyMap(), noOp, noOp) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt b/app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt index 4b16a5390d..0473fd59c3 100644 --- a/app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt +++ b/app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt @@ -86,13 +86,15 @@ constructor( localContext.resources.getDimension(R.dimen.editor_sheet_collapsed_height) } - @JvmField var binding: LayoutEditorBottomSheetBinding + @JvmField + var binding: LayoutEditorBottomSheetBinding val pagerAdapter: EditorBottomSheetTabAdapter private val log = ILogger.newInstance("EditorBottomSheet") companion object { + const val CHILD_SYMBOL_INPUT = 0 const val CHILD_HEADER = 1 const val CHILD_ACTION = 2 @@ -138,7 +140,8 @@ constructor( val filename = fragment.getFilename() @Suppress("DEPRECATION") - val progress = android.app.ProgressDialog.show(context, null, context.getString(string.please_wait)) + val progress = android.app.ProgressDialog.show(context, null, + context.getString(string.please_wait)) executeAsync(fragment::getContent) { progress.dismiss() shareText(it, filename) @@ -234,11 +237,11 @@ constructor( } fun handleDiagnosticsResultVisibility(errorVisible: Boolean) { - runOnUiThread { pagerAdapter.diagnosticsFragment?.handleResultVisibility(errorVisible) } + runOnUiThread { pagerAdapter.diagnosticsFragment?.isEmpty = errorVisible } } fun handleSearchResultVisibility(errorVisible: Boolean) { - runOnUiThread { pagerAdapter.searchResultFragment?.handleResultVisibility(errorVisible) } + runOnUiThread { pagerAdapter.searchResultFragment?.isEmpty = errorVisible } } fun setDiagnosticsAdapter(adapter: DiagnosticsAdapter) { @@ -300,7 +303,8 @@ constructor( flashError(context.getString(string.msg_output_text_extraction_failed)) return } - val pd = android.app.ProgressDialog.show(context, null, context.getString(string.please_wait), true, false) + val pd = android.app.ProgressDialog.show(context, null, context.getString(string.please_wait), + true, false) executeAsyncProvideError( Callable { writeTempFile(text, type) }, CallbackWithError { result: File?, error: Throwable? -> diff --git a/app/src/main/java/com/itsaky/androidide/ui/EmptyView.java b/app/src/main/java/com/itsaky/androidide/ui/EmptyView.java index f7a6a22dc6..761d440eab 100755 --- a/app/src/main/java/com/itsaky/androidide/ui/EmptyView.java +++ b/app/src/main/java/com/itsaky/androidide/ui/EmptyView.java @@ -21,16 +21,22 @@ package com.itsaky.androidide.ui; import android.content.Context; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.TypedValue; +import android.view.View; import android.widget.RelativeLayout; import android.widget.TextView; - +import androidx.annotation.NonNull; import com.itsaky.androidide.R; import com.itsaky.androidide.utils.ResourceUtilsKt; public class EmptyView extends RelativeLayout { + private static final int MESSAGE_TEXTVIEW = View.generateViewId(); + + private CharSequence message = null; + public EmptyView(Context context) { this(context, null); } @@ -54,7 +60,8 @@ private void init() { removeAllViews(); TextView text = new TextView(getContext()); - text.setText(com.itsaky.androidide.resources.R.string.msg_empty_view); + text.setId(MESSAGE_TEXTVIEW); + text.setText(getMessage()); text.setTextColor(ResourceUtilsKt.resolveAttr(getContext(), R.attr.colorSecondaryVariant)); text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); @@ -63,4 +70,18 @@ private void init() { addView(text, params); } + + public void setMessage(CharSequence message) { + this.message = message; + + final TextView text = findViewById(MESSAGE_TEXTVIEW); + if (text != null) { + text.setText(getMessage()); + } + } + + @NonNull + public CharSequence getMessage() { + return TextUtils.isEmpty(message) ? getContext().getString(R.string.msg_empty_view) : message; + } } diff --git a/app/src/main/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModel.kt b/app/src/main/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModel.kt new file mode 100644 index 0000000000..9a57c21e7f --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/viewmodel/EmptyStateFragmentViewModel.kt @@ -0,0 +1,31 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.viewmodel + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.itsaky.androidide.R + +/** + * @author Akash Yadav + */ +class EmptyStateFragmentViewModel : ViewModel() { + + val isEmpty = MutableLiveData(true) + val emptyMessage = MutableLiveData("") +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_empty_state.xml b/app/src/main/res/layout/fragment_empty_state.xml new file mode 100644 index 0000000000..090d8f4551 --- /dev/null +++ b/app/src/main/res/layout/fragment_empty_state.xml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_recyclerview.xml b/app/src/main/res/layout/fragment_recyclerview.xml new file mode 100644 index 0000000000..4a20b468b8 --- /dev/null +++ b/app/src/main/res/layout/fragment_recyclerview.xml @@ -0,0 +1,20 @@ + + + \ No newline at end of file diff --git a/common/src/main/java/com/itsaky/androidide/fragments/FragmentWithBinding.kt b/common/src/main/java/com/itsaky/androidide/fragments/FragmentWithBinding.kt index e3678d5567..2606576496 100644 --- a/common/src/main/java/com/itsaky/androidide/fragments/FragmentWithBinding.kt +++ b/common/src/main/java/com/itsaky/androidide/fragments/FragmentWithBinding.kt @@ -30,7 +30,7 @@ import androidx.viewbinding.ViewBinding * @author Akash Yadav */ abstract class FragmentWithBinding(@LayoutRes layout: Int, - private val bind: (View) -> T + private val bind: (View) -> T ) : BaseFragment(layout) { @Suppress("PropertyName") @@ -41,10 +41,14 @@ abstract class FragmentWithBinding(@LayoutRes layout: Int, _binding) { "Cannot access ViewBinding. Fragment may have been destroyed." } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle? ): View { return super.onCreateView(inflater, container, savedInstanceState)!! - .also { _binding = bind(it) } + .also(this::doBind) + } + + protected fun doBind(it: View) { + _binding = bind(it) } override fun onDestroyView() { diff --git a/resources/src/main/res/values/strings.xml b/resources/src/main/res/values/strings.xml index 8933a5f2ca..12dd908d7e 100644 --- a/resources/src/main/res/values/strings.xml +++ b/resources/src/main/res/values/strings.xml @@ -23,7 +23,7 @@ IDE preferences Discussions on Telegram Official Telegram Channel - I have nothing to show you… + No data Terminal Reset App Logs @@ -586,4 +586,8 @@ Debugging Dump logs Dump AndroidIDE logs to $HOME/.androidide/logs + Run the debug variant of your application to view its logs here. + Logs from the IDE are shown here. + Open a file to show its diagnostic results + "Build the application or run a task to see its build output here. "