diff --git a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseScreen.java b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseScreen.java deleted file mode 100644 index 331d91742..000000000 --- a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseScreen.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2016 Álinson Santos Xavier - * - * This file is part of Loop Habit Tracker. - * - * Loop Habit Tracker 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. - * - * Loop Habit Tracker 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 this program. If not, see . - */ - -package org.isoron.androidbase.activities; - -import android.content.*; -import android.graphics.*; -import android.graphics.drawable.*; -import android.net.*; -import android.os.*; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import androidx.core.content.res.*; -import androidx.appcompat.app.*; -import androidx.appcompat.view.ActionMode; -import androidx.appcompat.widget.Toolbar; -import android.view.*; -import android.widget.*; - -import com.google.android.material.snackbar.Snackbar; - -import org.isoron.androidbase.*; -import org.isoron.androidbase.utils.*; - -import java.io.*; - -import static android.os.Build.VERSION.SDK_INT; -import static android.os.Build.VERSION_CODES.LOLLIPOP; -import static androidx.core.content.FileProvider.getUriForFile; - -/** - * Base class for all screens in the application. - *

- * Screens are responsible for deciding what root views and what menus should be - * attached to the main window. They are also responsible for showing other - * screens and for receiving their results. - */ -public class BaseScreen -{ - protected BaseActivity activity; - - @Nullable - private BaseRootView rootView; - - @Nullable - private BaseSelectionMenu selectionMenu; - - protected Snackbar snackbar; - - public BaseScreen(@NonNull BaseActivity activity) - { - this.activity = activity; - } - - @Deprecated - public static int getDefaultActionBarColor(Context context) - { - if (SDK_INT < LOLLIPOP) - { - return ResourcesCompat.getColor(context.getResources(), - R.color.grey_900, context.getTheme()); - } - else - { - StyledResources res = new StyledResources(context); - return res.getColor(R.attr.colorPrimary); - } - } - - @Deprecated - public static void setupActionBarColor(@NonNull AppCompatActivity activity, - int color) - { - - Toolbar toolbar = (Toolbar) activity.findViewById(R.id.toolbar); - if (toolbar == null) return; - - activity.setSupportActionBar(toolbar); - - ActionBar actionBar = activity.getSupportActionBar(); - if (actionBar == null) return; - - actionBar.setDisplayHomeAsUpEnabled(true); - - ColorDrawable drawable = new ColorDrawable(color); - actionBar.setBackgroundDrawable(drawable); - - if (SDK_INT >= LOLLIPOP) - { - int darkerColor = ColorUtils.mixColors(color, Color.BLACK, 0.75f); - activity.getWindow().setStatusBarColor(darkerColor); - - toolbar.setElevation(InterfaceUtils.dpToPixels(activity, 2)); - - View view = activity.findViewById(R.id.toolbarShadow); - if (view != null) view.setVisibility(View.GONE); - - view = activity.findViewById(R.id.headerShadow); - if (view != null) view.setVisibility(View.GONE); - } - } - - /** - * Notifies the screen that its contents should be updated. - */ - public void invalidate() - { - if (rootView == null) return; - rootView.invalidate(); - } - - public void invalidateToolbar() - { - if (rootView == null) return; - - activity.runOnUiThread(() -> - { - Toolbar toolbar = rootView.getToolbar(); - activity.setSupportActionBar(toolbar); - ActionBar actionBar = activity.getSupportActionBar(); - if (actionBar == null) return; - - actionBar.setDisplayHomeAsUpEnabled(rootView.getDisplayHomeAsUp()); - - int color = rootView.getToolbarColor(); - setActionBarColor(actionBar, color); - setStatusBarColor(color); - }); - } - - /** - * Called when another Activity has finished, and has returned some result. - * - * @param requestCode the request code originally supplied to {@link - * android.app.Activity#startActivityForResult(Intent, - * int, Bundle)}. - * @param resultCode the result code sent by the other activity. - * @param data an Intent containing extra data sent by the other - * activity. - * @see {@link android.app.Activity#onActivityResult(int, int, Intent)} - */ - public void onResult(int requestCode, int resultCode, Intent data) - { - } - - - /** - * Called after activity has been recreated, and the dialogs should be - * reattached to their controllers. - */ - public void reattachDialogs() - { - } - - /** - * Sets the menu to be shown by this screen. - *

- * This menu will be visible if when there is no active selection operation. - * If the provided menu is null, then no menu will be shown. - * - * @param menu the menu to be shown. - */ - public void setMenu(@Nullable BaseMenu menu) - { - activity.setBaseMenu(menu); - } - - /** - * Sets the root view for this screen. - * - * @param rootView the root view for this screen. - */ - public void setRootView(@Nullable BaseRootView rootView) - { - this.rootView = rootView; - activity.setContentView(rootView); - if (rootView == null) return; - rootView.onAttachedToScreen(this); - invalidateToolbar(); - } - - /** - * Sets the menu to be shown when a selection is active on the screen. - * - * @param menu the menu to be shown during a selection - */ - public void setSelectionMenu(@Nullable BaseSelectionMenu menu) - { - this.selectionMenu = menu; - } - - /** - * Shows a message on the screen. - * - * @param stringId the string resource id for this message. - */ - public void showMessage(@StringRes Integer stringId) - { - if (stringId == null || rootView == null) return; - if (snackbar == null) - { - snackbar = Snackbar.make(rootView, stringId, Snackbar.LENGTH_SHORT); - int tvId = R.id.snackbar_text; - TextView tv = (TextView) snackbar.getView().findViewById(tvId); - tv.setTextColor(Color.WHITE); - } - else snackbar.setText(stringId); - snackbar.show(); - } - - public void showSendEmailScreen(@StringRes int toId, - @StringRes int subjectId, - String content) - { - String to = activity.getString(toId); - String subject = activity.getString(subjectId); - - Intent intent = new Intent(); - intent.setAction(Intent.ACTION_SEND); - intent.setType("message/rfc822"); - intent.putExtra(Intent.EXTRA_EMAIL, new String[]{ to }); - intent.putExtra(Intent.EXTRA_SUBJECT, subject); - intent.putExtra(Intent.EXTRA_TEXT, content); - activity.startActivity(intent); - } - - public void showSendFileScreen(@NonNull String archiveFilename) - { - File file = new File(archiveFilename); - Uri fileUri = getUriForFile(activity, "org.isoron.uhabits", file); - - Intent intent = new Intent(); - intent.setAction(Intent.ACTION_SEND); - intent.setType("application/zip"); - intent.putExtra(Intent.EXTRA_STREAM, fileUri); - intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - activity.startActivity(intent); - } - - /** - * Instructs the screen to start a selection. - *

- * If a selection menu was provided, this menu will be shown instead of the - * regular one. - */ - public void startSelection() - { - activity.startSupportActionMode(new ActionModeWrapper()); - } - - private void setActionBarColor(@NonNull ActionBar actionBar, int color) - { - ColorDrawable drawable = new ColorDrawable(color); - actionBar.setBackgroundDrawable(drawable); - } - - private void setStatusBarColor(int baseColor) - { - if (SDK_INT < LOLLIPOP) return; - - int darkerColor = ColorUtils.mixColors(baseColor, Color.BLACK, 0.75f); - activity.getWindow().setStatusBarColor(darkerColor); - } - - private class ActionModeWrapper implements ActionMode.Callback - { - @Override - public boolean onActionItemClicked(@Nullable ActionMode mode, - @Nullable MenuItem item) - { - if (item == null || selectionMenu == null) return false; - return selectionMenu.onItemClicked(item); - } - - @Override - public boolean onCreateActionMode(@Nullable ActionMode mode, - @Nullable Menu menu) - { - if (selectionMenu == null) return false; - if (mode == null || menu == null) return false; - selectionMenu.onCreate(activity.getMenuInflater(), mode, menu); - return true; - } - - @Override - public void onDestroyActionMode(@Nullable ActionMode mode) - { - if (selectionMenu == null) return; - selectionMenu.onFinish(); - } - - @Override - public boolean onPrepareActionMode(@Nullable ActionMode mode, - @Nullable Menu menu) - { - if (selectionMenu == null || menu == null) return false; - return selectionMenu.onPrepare(menu); - } - } -} diff --git a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseScreen.kt b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseScreen.kt new file mode 100644 index 000000000..443344e87 --- /dev/null +++ b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseScreen.kt @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker 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. + * + * Loop Habit Tracker 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 this program. If not, see . + */ +package org.isoron.androidbase.activities + +import android.content.* +import android.graphics.* +import android.graphics.drawable.* +import android.view.* +import android.widget.* +import androidx.annotation.* +import androidx.appcompat.app.* +import androidx.appcompat.view.ActionMode +import androidx.appcompat.widget.Toolbar +import androidx.core.content.* +import com.google.android.material.snackbar.* +import org.isoron.androidbase.* +import org.isoron.androidbase.utils.* +import org.isoron.androidbase.utils.ColorUtils.mixColors +import org.isoron.androidbase.utils.InterfaceUtils.dpToPixels +import java.io.* + +/** + * Base class for all screens in the application. + * + * Screens are responsible for deciding what root views and what menus should be attached to the + * main window. They are also responsible for showing other screens and for receiving their results. + */ +open class BaseScreen(@JvmField protected var activity: BaseActivity) { + + private var rootView: BaseRootView? = null + private var selectionMenu: BaseSelectionMenu? = null + private var snackbar: Snackbar? = null + + /** + * Notifies the screen that its contents should be updated. + */ + fun invalidate() { + rootView?.invalidate() + } + + fun invalidateToolbar() { + rootView?.let { root -> + activity.runOnUiThread { + val toolbar = root.getToolbar() + activity.setSupportActionBar(toolbar) + activity.supportActionBar?.let { actionBar -> + actionBar.setDisplayHomeAsUpEnabled(root.displayHomeAsUp) + val color = root.getToolbarColor() + setActionBarColor(actionBar, color) + setStatusBarColor(color) + } + } + } + } + + /** + * Called when another Activity has finished, and has returned some result. + * + * @param requestCode the request code originally supplied to startActivityForResult. + * @param resultCode the result code sent by the other activity. + * @param data an Intent containing extra data sent by the other + * activity. + * @see {@link android.app.Activity.onActivityResult + */ + open fun onResult(requestCode: Int, resultCode: Int, data: Intent?) {} + + /** + * Called after activity has been recreated, and the dialogs should be + * reattached to their controllers. + */ + open fun reattachDialogs() {} + + /** + * Sets the menu to be shown by this screen. + * + * + * This menu will be visible if when there is no active selection operation. + * If the provided menu is null, then no menu will be shown. + * + * @param menu the menu to be shown. + */ + fun setMenu(menu: BaseMenu?) { + activity.setBaseMenu(menu) + } + + /** + * Sets the root view for this screen. + * + * @param rootView the root view for this screen. + */ + fun setRootView(rootView: BaseRootView?) { + this.rootView = rootView + activity.setContentView(rootView) + rootView?.let { + it.onAttachedToScreen(this) + invalidateToolbar() + } + } + + /** + * Sets the menu to be shown when a selection is active on the screen. + * + * @param menu the menu to be shown during a selection + */ + fun setSelectionMenu(menu: BaseSelectionMenu?) { + selectionMenu = menu + } + + /** + * Shows a message on the screen. + * + * @param stringId the string resource id for this message. + */ + fun showMessage(@StringRes stringId: Int?) { + val rootView = this.rootView + var snackbar = this.snackbar + if (stringId == null || rootView == null) return + if (snackbar == null) { + snackbar = Snackbar.make(rootView, stringId, Snackbar.LENGTH_SHORT) + val tvId = R.id.snackbar_text + val tv = snackbar.view.findViewById(tvId) + tv.setTextColor(Color.WHITE) + this.snackbar = snackbar + } + snackbar.setText(stringId) + snackbar.show() + } + + fun showSendEmailScreen(@StringRes toId: Int, @StringRes subjectId: Int, content: String?) { + val to = activity.getString(toId) + val subject = activity.getString(subjectId) + activity.startActivity(Intent().apply { + action = Intent.ACTION_SEND + type = "message/rfc822" + putExtra(Intent.EXTRA_EMAIL, arrayOf(to)) + putExtra(Intent.EXTRA_SUBJECT, subject) + putExtra(Intent.EXTRA_TEXT, content) + }) + } + + fun showSendFileScreen(archiveFilename: String) { + val file = File(archiveFilename) + val fileUri = FileProvider.getUriForFile(activity, "org.isoron.uhabits", file) + activity.startActivity(Intent().apply { + action = Intent.ACTION_SEND + type = "application/zip" + putExtra(Intent.EXTRA_STREAM, fileUri) + flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + }) + } + + /** + * Instructs the screen to start a selection. + * + * If a selection menu was provided, this menu will be shown instead of the regular one. + */ + fun startSelection() { + activity.startSupportActionMode(ActionModeWrapper()) + } + + private fun setActionBarColor(actionBar: ActionBar, color: Int) { + val drawable = ColorDrawable(color) + actionBar.setBackgroundDrawable(drawable) + } + + private fun setStatusBarColor(baseColor: Int) { + val darkerColor = mixColors(baseColor, Color.BLACK, 0.75f) + activity.window.statusBarColor = darkerColor + } + + private inner class ActionModeWrapper : ActionMode.Callback { + override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean { + val selectionMenu = selectionMenu + if (item == null || selectionMenu == null) return false + return selectionMenu.onItemClicked(item) + } + + override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean { + if (mode == null || menu == null) return false + val selectionMenu = selectionMenu ?: return false + selectionMenu.onCreate(activity.menuInflater, mode, menu) + return true + } + + override fun onDestroyActionMode(mode: ActionMode?) { + selectionMenu?.onFinish() + } + + override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean { + val selectionMenu = selectionMenu + if (selectionMenu == null || menu == null) return false + return selectionMenu.onPrepare(menu) + } + } + + companion object { + @JvmStatic + @Deprecated("") + fun getDefaultActionBarColor(context: Context) = + StyledResources(context).getColor(R.attr.colorPrimary) + + @JvmStatic + @Deprecated("") + fun setupActionBarColor(activity: AppCompatActivity, color: Int) { + val toolbar = activity.findViewById(R.id.toolbar) ?: return + activity.setSupportActionBar(toolbar) + val supportActionBar = activity.supportActionBar ?: return + supportActionBar.setDisplayHomeAsUpEnabled(true) + val drawable = ColorDrawable(color) + supportActionBar.setBackgroundDrawable(drawable) + val darkerColor = mixColors(color, Color.BLACK, 0.75f) + activity.window.statusBarColor = darkerColor + toolbar.elevation = dpToPixels(activity, 2f) + activity.findViewById(R.id.toolbarShadow)?.visibility = View.GONE + activity.findViewById(R.id.headerShadow)?.visibility = View.GONE + } + } +} \ No newline at end of file