diff --git a/android/android-base/src/main/java/org/isoron/androidbase/AndroidBugReporter.kt b/android/android-base/src/main/java/org/isoron/androidbase/AndroidBugReporter.kt index ea013ceef..00efcbc7e 100644 --- a/android/android-base/src/main/java/org/isoron/androidbase/AndroidBugReporter.kt +++ b/android/android-base/src/main/java/org/isoron/androidbase/AndroidBugReporter.kt @@ -95,7 +95,7 @@ open class AndroidBugReporter @Inject constructor(@AppContext private val contex appendln("App Version Name: ${BuildConfig.VERSION_NAME}") appendln("App Version Code: ${BuildConfig.VERSION_CODE}") appendln("OS Version: ${System.getProperty("os.version")} (${Build.VERSION.INCREMENTAL})") - appendln("OS API Level: ${Build.VERSION.SDK}") + appendln("OS API Level: ${Build.VERSION.SDK_INT}") appendln("Device: ${Build.DEVICE}") appendln("Model (Product): ${Build.MODEL} (${Build.PRODUCT})") appendln("Manufacturer: ${Build.MANUFACTURER}") diff --git a/android/android-base/src/main/java/org/isoron/androidbase/AppContext.kt b/android/android-base/src/main/java/org/isoron/androidbase/AppContext.kt index 3146da5e2..55e22e9b3 100644 --- a/android/android-base/src/main/java/org/isoron/androidbase/AppContext.kt +++ b/android/android-base/src/main/java/org/isoron/androidbase/AppContext.kt @@ -18,12 +18,9 @@ */ package org.isoron.androidbase -import java.lang.annotation.Documented -import java.lang.annotation.Retention -import java.lang.annotation.RetentionPolicy import javax.inject.Qualifier @Qualifier -@Documented -@Retention(RetentionPolicy.RUNTIME) +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) annotation class AppContext \ No newline at end of file diff --git a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseActivity.java b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseActivity.java deleted file mode 100644 index 2dea22e50..000000000 --- a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseActivity.java +++ /dev/null @@ -1,129 +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.os.*; - -import androidx.annotation.Nullable; -import androidx.appcompat.app.*; -import android.view.*; - -import org.isoron.androidbase.*; - -import static android.R.anim.fade_in; -import static android.R.anim.fade_out; - -/** - * Base class for all activities in the application. - *

- * This class delegates the responsibilities of an Android activity to other - * classes. For example, callbacks related to menus are forwarded to a {@link - * BaseMenu}, while callbacks related to activity results are forwarded to a - * {@link BaseScreen}. - *

- * A BaseActivity also installs an {@link java.lang.Thread.UncaughtExceptionHandler} - * to the main thread. By default, this handler is an instance of - * BaseExceptionHandler, which logs the exception to the disk before the application - * crashes. To the default handler, you should override the method - * getExceptionHandler. - */ -abstract public class BaseActivity extends AppCompatActivity -{ - @Nullable - private BaseMenu baseMenu; - - @Nullable - private BaseScreen screen; - - @Override - public boolean onCreateOptionsMenu(@Nullable Menu menu) - { - if (menu == null) return true; - if (baseMenu == null) return true; - baseMenu.onCreate(getMenuInflater(), menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(@Nullable MenuItem item) - { - if (item == null) return false; - if (baseMenu == null) return false; - return baseMenu.onItemSelected(item); - } - - public void restartWithFade(Class cls) - { - new Handler().postDelayed(() -> - { - finish(); - overridePendingTransition(fade_in, fade_out); - startActivity(new Intent(this, cls)); - - }, 500); // HACK: Let the menu disappear first - } - - public void setBaseMenu(@Nullable BaseMenu baseMenu) - { - this.baseMenu = baseMenu; - } - - public void setScreen(@Nullable BaseScreen screen) - { - this.screen = screen; - } - - public void showDialog(AppCompatDialogFragment dialog, String tag) - { - dialog.show(getSupportFragmentManager(), tag); - } - - public void showDialog(AppCompatDialog dialog) - { - dialog.show(); - } - - @Override - protected void onActivityResult(int request, int result, Intent data) - { - if (screen == null) super.onActivityResult(request, result, data); - else screen.onResult(request, result, data); - } - - @Override - protected void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - Thread.setDefaultUncaughtExceptionHandler(getExceptionHandler()); - } - - protected Thread.UncaughtExceptionHandler getExceptionHandler() - { - return new BaseExceptionHandler(this); - } - - @Override - protected void onResume() - { - super.onResume(); - if(screen != null) screen.reattachDialogs(); - } -} diff --git a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseActivity.kt b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseActivity.kt new file mode 100644 index 000000000..7d02aaf50 --- /dev/null +++ b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseActivity.kt @@ -0,0 +1,104 @@ +/* + * 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.R.anim +import android.content.* +import android.os.* +import android.view.* +import androidx.appcompat.app.* +import org.isoron.androidbase.* + +/** + * Base class for all activities in the application. + * + * This class delegates the responsibilities of an Android activity to other classes. For example, + * callbacks related to menus are forwarded to a []BaseMenu], while callbacks related to activity + * results are forwarded to a [BaseScreen]. + * + * + * A BaseActivity also installs an [java.lang.Thread.UncaughtExceptionHandler] to the main thread. + * By default, this handler is an instance of BaseExceptionHandler, which logs the exception to the + * disk before the application crashes. To the default handler, you should override the method + * getExceptionHandler. + */ +abstract class BaseActivity : AppCompatActivity() { + private var baseMenu: BaseMenu? = null + private var screen: BaseScreen? = null + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + if (menu != null) baseMenu?.onCreate(menuInflater, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + if (item == null) return false + return baseMenu?.onItemSelected(item) ?: false + } + + fun restartWithFade(cls: Class<*>?) { + Handler().postDelayed({ + finish() + overridePendingTransition(anim.fade_in, anim.fade_out) + startActivity(Intent(this, cls)) + }, 500) // HACK: Let the menu disappear first + } + + fun setBaseMenu(baseMenu: BaseMenu?) { + this.baseMenu = baseMenu + } + + fun setScreen(screen: BaseScreen?) { + this.screen = screen + } + + fun showDialog(dialog: AppCompatDialogFragment, tag: String?) { + dialog.show(supportFragmentManager, tag) + } + + fun showDialog(dialog: AppCompatDialog) { + dialog.show() + } + + override fun onActivityResult(request: Int, result: Int, data: Intent?) { + val screen = screen + if(screen == null) super.onActivityResult(request, result, data) + else screen.onResult(request, result, data) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Thread.setDefaultUncaughtExceptionHandler(getExceptionHandler()) + } + + private fun getExceptionHandler() = BaseExceptionHandler(this) + + override fun onResume() { + super.onResume() + screen?.reattachDialogs() + } + + override fun startActivity(intent: Intent?) { + try { + super.startActivity(intent) + } catch(e: ActivityNotFoundException) { + this.screen?.showMessage(R.string.activity_not_found) + } + } +} \ No newline at end of file diff --git a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseMenu.java b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseMenu.kt similarity index 70% rename from android/android-base/src/main/java/org/isoron/androidbase/activities/BaseMenu.java rename to android/android-base/src/main/java/org/isoron/androidbase/activities/BaseMenu.kt index c5113a3fa..337b906dc 100644 --- a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseMenu.java +++ b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseMenu.kt @@ -16,71 +16,50 @@ * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ +package org.isoron.androidbase.activities -package org.isoron.androidbase.activities; - -import android.view.*; - -import androidx.annotation.MenuRes; -import androidx.annotation.NonNull; +import android.view.* +import androidx.annotation.* /** * Base class for all the menus in the application. - *

+ * * This class receives from BaseActivity all callbacks related to menus, such as * menu creation and click events. It also handles some implementation details * of creating menus in Android, such as inflating the resources. */ -public abstract class BaseMenu -{ - @NonNull - private final BaseActivity activity; - - public BaseMenu(@NonNull BaseActivity activity) - { - this.activity = activity; - } - - @NonNull - public BaseActivity getActivity() - { - return activity; - } +abstract class BaseMenu(private val activity: BaseActivity) { /** * Declare that the menu has changed, and should be recreated. */ - public void invalidate() - { - activity.invalidateOptionsMenu(); + fun invalidate() { + activity.invalidateOptionsMenu() } /** * Called when the menu is first displayed. - *

+ * * The given menu is already inflated and ready to receive items. The * application should override this method and add items to the menu here. * * @param menu the menu that is being created. */ - public void onCreate(@NonNull Menu menu) - { - } + open fun onCreate(menu: Menu) {} /** * Called when the menu is first displayed. - *

+ * * This method should not be overridden. The application should override * the methods onCreate(Menu) and getMenuResourceId instead. * * @param inflater a menu inflater, for creating the menu * @param menu the menu that is being created. */ - public void onCreate(@NonNull MenuInflater inflater, @NonNull Menu menu) - { - menu.clear(); - inflater.inflate(getMenuResourceId(), menu); - onCreate(menu); + fun onCreate(inflater: MenuInflater, menu: Menu) { + menu.clear() + inflater.inflate(getMenuResourceId(), menu) + onCreate(menu) } /** @@ -89,10 +68,7 @@ public abstract class BaseMenu * @param item the item that was selected. * @return true if the event was consumed, or false otherwise */ - public boolean onItemSelected(@NonNull MenuItem item) - { - return false; - } + open fun onItemSelected(item: MenuItem): Boolean = false /** * Returns the id of the resource that should be used to inflate this menu. @@ -100,5 +76,6 @@ public abstract class BaseMenu * @return id of the menu resource. */ @MenuRes - protected abstract int getMenuResourceId(); -} + protected abstract fun getMenuResourceId(): Int + +} \ No newline at end of file diff --git a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseRootView.java b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseRootView.java deleted file mode 100644 index 3ff339387..000000000 --- a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseRootView.java +++ /dev/null @@ -1,110 +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 androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; -import android.view.*; -import android.widget.*; - -import org.isoron.androidbase.*; -import org.isoron.androidbase.utils.*; - -import static android.os.Build.VERSION.SDK_INT; -import static android.os.Build.VERSION_CODES.LOLLIPOP; - -/** - * Base class for all root views in the application. - *

- * A root view is an Android view that is directly attached to an activity. This - * view usually includes a toolbar and a progress bar. This abstract class hides - * some of the complexity of setting these things up, for every version of - * Android. - */ -public abstract class BaseRootView extends FrameLayout -{ - @NonNull - private final Context context; - - protected boolean shouldDisplayHomeAsUp = false; - - @Nullable - private BaseScreen screen; - - public BaseRootView(@NonNull Context context) - { - super(context); - this.context = context; - } - - public boolean getDisplayHomeAsUp() - { - return shouldDisplayHomeAsUp; - } - - public void setDisplayHomeAsUp(boolean b) - { - shouldDisplayHomeAsUp = b; - } - - @NonNull - public Toolbar getToolbar() - { - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); - if (toolbar == null) throw new RuntimeException( - "Your BaseRootView should have a " + - "toolbar with id R.id.toolbar"); - return toolbar; - } - - public int getToolbarColor() - { - StyledResources res = new StyledResources(context); - return res.getColor(R.attr.colorPrimary); - } - - protected void initToolbar() - { - if (SDK_INT >= LOLLIPOP) - { - getToolbar().setElevation(InterfaceUtils.dpToPixels(context, 2)); - - View view = findViewById(R.id.toolbarShadow); - if (view != null) view.setVisibility(GONE); - - view = findViewById(R.id.headerShadow); - if (view != null) view.setVisibility(GONE); - } - } - - public void onAttachedToScreen(BaseScreen screen) - { - this.screen = screen; - } - - @Nullable - public BaseScreen getScreen() - { - return screen; - } -} diff --git a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseRootView.kt b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseRootView.kt new file mode 100644 index 000000000..cc8fb3c7e --- /dev/null +++ b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseRootView.kt @@ -0,0 +1,63 @@ +/* + * 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.Context +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES +import android.view.View +import android.widget.FrameLayout +import androidx.appcompat.widget.Toolbar +import org.isoron.androidbase.R +import org.isoron.androidbase.utils.InterfaceUtils.dpToPixels +import org.isoron.androidbase.utils.StyledResources + +/** + * Base class for all root views in the application. + * + * + * A root view is an Android view that is directly attached to an activity. This + * view usually includes a toolbar and a progress bar. This abstract class hides + * some of the complexity of setting these things up, for every version of + * Android. + */ +abstract class BaseRootView(context: Context) : FrameLayout(context) { + var displayHomeAsUp = false + var screen: BaseScreen? = null + private set + + open fun getToolbar(): Toolbar { + return findViewById(R.id.toolbar) + ?: throw RuntimeException("Your BaseRootView should have a toolbar with id R.id.toolbar") + } + + open fun getToolbarColor(): Int = StyledResources(context).getColor(R.attr.colorPrimary) + + protected open fun initToolbar() { + if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { + getToolbar().elevation = dpToPixels(context, 2f) + findViewById(R.id.toolbarShadow)?.visibility = View.GONE + findViewById(R.id.headerShadow)?.visibility = View.GONE + } + } + + fun onAttachedToScreen(screen: BaseScreen?) { + this.screen = screen + } +} \ No newline at end of file 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 diff --git a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseSelectionMenu.java b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseSelectionMenu.kt similarity index 65% rename from android/android-base/src/main/java/org/isoron/androidbase/activities/BaseSelectionMenu.java rename to android/android-base/src/main/java/org/isoron/androidbase/activities/BaseSelectionMenu.kt index a1b486555..5d4b9af11 100644 --- a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseSelectionMenu.java +++ b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseSelectionMenu.kt @@ -16,50 +16,43 @@ * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ +package org.isoron.androidbase.activities -package org.isoron.androidbase.activities; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.view.ActionMode; -import android.view.*; +import android.view.* +import androidx.appcompat.view.ActionMode /** * Base class for all the selection menus in the application. - *

+ * * A selection menu is a menu that appears when the screen starts a selection * operation. It contains actions that modify the selected items, such as delete * or archive. Since it replaces the toolbar, it also has a title. - *

+ * * This class hides many implementation details of creating such menus in - * Android. The interface is supposed to look very similar to {@link BaseMenu}, + * Android. The interface is supposed to look very similar to [BaseMenu], * with a few additional methods, such as finishing the selection operation. - * Internally, it uses an {@link ActionMode}. + * Internally, it uses an [ActionMode]. */ -public abstract class BaseSelectionMenu -{ - @Nullable - private ActionMode actionMode; +abstract class BaseSelectionMenu { + private var actionMode: ActionMode? = null /** * Finishes the selection operation. */ - public void finish() - { - if (actionMode != null) actionMode.finish(); + fun finish() { + actionMode?.finish() } /** * Declare that the menu has changed, and should be recreated. */ - public void invalidate() - { - if (actionMode != null) actionMode.invalidate(); + fun invalidate() { + actionMode?.invalidate() } /** * Called when the menu is first displayed. - *

+ * * This method should not be overridden. The application should override * the methods onCreate(Menu) and getMenuResourceId instead. * @@ -67,22 +60,16 @@ public abstract class BaseSelectionMenu * @param mode the action mode associated with this menu. * @param menu the menu that is being created. */ - public void onCreate(@NonNull MenuInflater inflater, - @NonNull ActionMode mode, - @NonNull Menu menu) - { - this.actionMode = mode; - inflater.inflate(getResourceId(), menu); - onCreate(menu); + fun onCreate(inflater: MenuInflater, mode: ActionMode, menu: Menu) { + actionMode = mode + inflater.inflate(getResourceId(), menu) + onCreate(menu) } /** * Called when the selection operation is about to finish. */ - public void onFinish() - { - - } + open fun onFinish() {} /** * Called whenever an item on the menu is selected. @@ -90,11 +77,7 @@ public abstract class BaseSelectionMenu * @param item the item that was selected. * @return true if the event was consumed, or false otherwise */ - public boolean onItemClicked(@NonNull MenuItem item) - { - return false; - } - + open fun onItemClicked(item: MenuItem): Boolean = false /** * Called whenever the menu is invalidated. @@ -102,29 +85,23 @@ public abstract class BaseSelectionMenu * @param menu the menu to be refreshed * @return true if the menu has changes, false otherwise */ - public boolean onPrepare(@NonNull Menu menu) - { - return false; - } + open fun onPrepare(menu: Menu): Boolean = false /** * Sets the title of the selection menu. * * @param title the new title. */ - public void setTitle(String title) - { - if (actionMode != null) actionMode.setTitle(title); + fun setTitle(title: String?) { + actionMode?.title = title } - protected abstract int getResourceId(); + protected abstract fun getResourceId(): Int /** * Called when the menu is first created. * * @param menu the menu being created */ - protected void onCreate(@NonNull Menu menu) - { - } -} + protected fun onCreate(menu: Menu) {} +} \ No newline at end of file diff --git a/android/uhabits-android/src/main/res/layout/show_habit_preview.xml b/android/android-base/src/main/res/values/strings.xml similarity index 63% rename from android/uhabits-android/src/main/res/layout/show_habit_preview.xml rename to android/android-base/src/main/res/values/strings.xml index a6c9317ed..032239605 100644 --- a/android/uhabits-android/src/main/res/layout/show_habit_preview.xml +++ b/android/android-base/src/main/res/values/strings.xml @@ -1,6 +1,6 @@ - - - - \ No newline at end of file + + No app was found to support this action + \ No newline at end of file diff --git a/android/build.sh b/android/build.sh index bb15f5541..b8f288cfa 100755 --- a/android/build.sh +++ b/android/build.sh @@ -71,12 +71,12 @@ build_apk() { if [ ! -z $RELEASE ]; then log_info "Building release APK" - ./gradlew assembleRelease + $GRADLE assembleRelease cp -v uhabits-android/build/outputs/apk/release/uhabits-android-release.apk build/loop-$VERSION-release.apk fi log_info "Building debug APK" - ./gradlew assembleDebug --stacktrace || fail + $GRADLE assembleDebug --stacktrace || fail cp -v uhabits-android/build/outputs/apk/debug/uhabits-android-debug.apk build/loop-$VERSION-debug.apk } diff --git a/android/gradle.properties b/android/gradle.properties index 6e98ecfec..1f783d1bc 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ -VERSION_CODE = 51 -VERSION_NAME = 1.8.8 +VERSION_CODE = 52 +VERSION_NAME = 1.8.9 MIN_SDK_VERSION = 21 TARGET_SDK_VERSION = 29 diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/BarChart/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/BarChart/render.png deleted file mode 100644 index a994ddfaf..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/BarChart/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/BarChart/renderDataOffset.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/BarChart/renderDataOffset.png deleted file mode 100644 index d8409a9c0..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/BarChart/renderDataOffset.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/BarChart/renderDifferentSize.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/BarChart/renderDifferentSize.png deleted file mode 100644 index b9a30b653..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/BarChart/renderDifferentSize.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/BarChart/renderTransparent.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/BarChart/renderTransparent.png deleted file mode 100644 index 5a5b0ade9..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/BarChart/renderTransparent.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/FrequencyChart/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/FrequencyChart/render.png deleted file mode 100644 index bdd6f135c..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/FrequencyChart/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/FrequencyChart/renderDataOffset.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/FrequencyChart/renderDataOffset.png deleted file mode 100644 index ad1ca177d..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/FrequencyChart/renderDataOffset.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/FrequencyChart/renderDifferentSize.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/FrequencyChart/renderDifferentSize.png deleted file mode 100644 index 1de28ea5d..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/FrequencyChart/renderDifferentSize.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/FrequencyChart/renderTransparent.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/FrequencyChart/renderTransparent.png deleted file mode 100644 index bdd6f135c..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/FrequencyChart/renderTransparent.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/HistoryChart/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/HistoryChart/render.png deleted file mode 100644 index 8f450c76b..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/HistoryChart/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/HistoryChart/renderDataOffset.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/HistoryChart/renderDataOffset.png deleted file mode 100644 index 065542a55..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/HistoryChart/renderDataOffset.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/HistoryChart/renderDifferentSize.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/HistoryChart/renderDifferentSize.png deleted file mode 100644 index 5c5db36d9..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/HistoryChart/renderDifferentSize.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/HistoryChart/renderTransparent.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/HistoryChart/renderTransparent.png deleted file mode 100644 index 49e77eabf..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/HistoryChart/renderTransparent.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/RingView/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/RingView/render.png deleted file mode 100644 index 8108a73e5..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/RingView/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/RingView/renderDifferentParams.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/RingView/renderDifferentParams.png deleted file mode 100644 index fa41449a4..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/RingView/renderDifferentParams.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/render.png deleted file mode 100644 index c8d3c0e97..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/renderDataOffset.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/renderDataOffset.png deleted file mode 100644 index 3b6c105a1..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/renderDataOffset.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/renderDifferentSize.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/renderDifferentSize.png deleted file mode 100644 index c58cb89c2..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/renderDifferentSize.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/renderMonthly.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/renderMonthly.png deleted file mode 100644 index 78385ea39..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/renderMonthly.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/renderTransparent.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/renderTransparent.png deleted file mode 100644 index f4ce5a6c5..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/renderTransparent.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/renderYearly.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/renderYearly.png deleted file mode 100644 index 634870570..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/ScoreChart/renderYearly.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/StreakChart/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/StreakChart/render.png deleted file mode 100644 index 256e8f438..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/StreakChart/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/StreakChart/renderSmallSize.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/StreakChart/renderSmallSize.png deleted file mode 100644 index 1c23c4389..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/StreakChart/renderSmallSize.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/common/StreakChart/renderTransparent.png b/android/uhabits-android/src/androidTest/assets/views-v19/common/StreakChart/renderTransparent.png deleted file mode 100644 index 256e8f438..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/common/StreakChart/renderTransparent.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkButtonView/render_explicit_check.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkButtonView/render_explicit_check.png deleted file mode 100644 index 9fc5d89eb..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkButtonView/render_explicit_check.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkButtonView/render_implicit_check.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkButtonView/render_implicit_check.png deleted file mode 100644 index 373a1ed4e..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkButtonView/render_implicit_check.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkButtonView/render_unchecked.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkButtonView/render_unchecked.png deleted file mode 100644 index ebae1c98d..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkButtonView/render_unchecked.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkPanelView/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkPanelView/render.png deleted file mode 100644 index 31a12dc35..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkPanelView/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkPanelView/render_different_color.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkPanelView/render_different_color.png deleted file mode 100644 index 84302fe22..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkPanelView/render_different_color.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkPanelView/render_offset.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkPanelView/render_offset.png deleted file mode 100644 index 9abe0c353..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkPanelView/render_offset.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkPanelView/render_reversed.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkPanelView/render_reversed.png deleted file mode 100644 index cf312df77..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/CheckmarkPanelView/render_reversed.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HabitCardView/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HabitCardView/render.png deleted file mode 100644 index a9f6c4829..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HabitCardView/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HabitCardView/render_changed.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HabitCardView/render_changed.png deleted file mode 100644 index a9f6c4829..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HabitCardView/render_changed.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HabitCardView/render_numerical.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HabitCardView/render_numerical.png deleted file mode 100644 index ccbf8ba3c..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HabitCardView/render_numerical.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HabitCardView/render_selected.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HabitCardView/render_selected.png deleted file mode 100644 index 30f3e10f4..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HabitCardView/render_selected.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HeaderView/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HeaderView/render.png deleted file mode 100644 index 1672f3b36..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HeaderView/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HeaderView/render_reverse.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HeaderView/render_reverse.png deleted file mode 100644 index edd862a25..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HeaderView/render_reverse.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HintView/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HintView/render.png deleted file mode 100644 index 0e77526b3..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/HintView/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberButtonView/render_above.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberButtonView/render_above.png deleted file mode 100644 index 3cffcc2cf..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberButtonView/render_above.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberButtonView/render_below.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberButtonView/render_below.png deleted file mode 100644 index 1b06bee41..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberButtonView/render_below.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberButtonView/render_zero.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberButtonView/render_zero.png deleted file mode 100644 index cce921e53..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberButtonView/render_zero.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberPanelView/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberPanelView/render.png deleted file mode 100644 index e33641085..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberPanelView/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberPanelView/render_different_color.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberPanelView/render_different_color.png deleted file mode 100644 index 172a6434c..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberPanelView/render_different_color.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberPanelView/render_offset.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberPanelView/render_offset.png deleted file mode 100644 index 8e86904df..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberPanelView/render_offset.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberPanelView/render_reversed.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberPanelView/render_reversed.png deleted file mode 100644 index 1d8338f29..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/list/NumberPanelView/render_reversed.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/FrequencyCard/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/FrequencyCard/render.png deleted file mode 100644 index b249ced15..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/FrequencyCard/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/HistoryCard/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/HistoryCard/render.png deleted file mode 100644 index f91e0fa37..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/HistoryCard/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/OverviewCard/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/OverviewCard/render.png deleted file mode 100644 index 442cdaa74..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/OverviewCard/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/ScoreCard/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/ScoreCard/render.png deleted file mode 100644 index 10274dce1..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/ScoreCard/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/StreakCard/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/StreakCard/render.png deleted file mode 100644 index 724e62f4a..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/StreakCard/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/SubtitleCard/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/SubtitleCard/render.png deleted file mode 100644 index edde6d27b..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/habits/show/SubtitleCard/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/CheckmarkWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/widgets/CheckmarkWidget/render.png deleted file mode 100644 index 966957274..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/CheckmarkWidget/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/CheckmarkWidgetView/checked.png b/android/uhabits-android/src/androidTest/assets/views-v19/widgets/CheckmarkWidgetView/checked.png deleted file mode 100644 index 30df27f51..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/CheckmarkWidgetView/checked.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/CheckmarkWidgetView/implicitly_checked.png b/android/uhabits-android/src/androidTest/assets/views-v19/widgets/CheckmarkWidgetView/implicitly_checked.png deleted file mode 100644 index 087dd6580..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/CheckmarkWidgetView/implicitly_checked.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/CheckmarkWidgetView/large_size.png b/android/uhabits-android/src/androidTest/assets/views-v19/widgets/CheckmarkWidgetView/large_size.png deleted file mode 100644 index 352d7db77..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/CheckmarkWidgetView/large_size.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/CheckmarkWidgetView/unchecked.png b/android/uhabits-android/src/androidTest/assets/views-v19/widgets/CheckmarkWidgetView/unchecked.png deleted file mode 100644 index e597e7f74..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/CheckmarkWidgetView/unchecked.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/FrequencyWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/widgets/FrequencyWidget/render.png deleted file mode 100644 index effbaecf7..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/FrequencyWidget/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/HistoryWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/widgets/HistoryWidget/render.png deleted file mode 100644 index 6b9a21f94..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/HistoryWidget/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/ScoreWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/widgets/ScoreWidget/render.png deleted file mode 100644 index 0f61c79da..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/ScoreWidget/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/StreakWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v19/widgets/StreakWidget/render.png deleted file mode 100644 index c6c9faec7..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v19/widgets/StreakWidget/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/BarChart/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/BarChart/render.png deleted file mode 100644 index 6f3c86156..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/BarChart/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/BarChart/renderDataOffset.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/BarChart/renderDataOffset.png deleted file mode 100644 index 6dfa977bc..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/BarChart/renderDataOffset.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/BarChart/renderDifferentSize.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/BarChart/renderDifferentSize.png deleted file mode 100644 index 6aaffd2f4..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/BarChart/renderDifferentSize.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/BarChart/renderTransparent.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/BarChart/renderTransparent.png deleted file mode 100644 index fbc63a2e5..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/BarChart/renderTransparent.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/FrequencyChart/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/FrequencyChart/render.png deleted file mode 100644 index fbd196e8e..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/FrequencyChart/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/FrequencyChart/renderDataOffset.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/FrequencyChart/renderDataOffset.png deleted file mode 100644 index 66ac9f4a3..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/FrequencyChart/renderDataOffset.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/FrequencyChart/renderDifferentSize.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/FrequencyChart/renderDifferentSize.png deleted file mode 100644 index 1096a3d29..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/FrequencyChart/renderDifferentSize.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/FrequencyChart/renderTransparent.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/FrequencyChart/renderTransparent.png deleted file mode 100644 index fbd196e8e..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/FrequencyChart/renderTransparent.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/HistoryChart/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/HistoryChart/render.png deleted file mode 100644 index 4b79eb36d..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/HistoryChart/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/HistoryChart/renderDataOffset.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/HistoryChart/renderDataOffset.png deleted file mode 100644 index 05f3b7341..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/HistoryChart/renderDataOffset.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/HistoryChart/renderDifferentSize.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/HistoryChart/renderDifferentSize.png deleted file mode 100644 index 8bd6b25b4..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/HistoryChart/renderDifferentSize.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/HistoryChart/renderTransparent.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/HistoryChart/renderTransparent.png deleted file mode 100644 index 4ec75da79..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/HistoryChart/renderTransparent.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/RingView/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/RingView/render.png deleted file mode 100644 index 0bf125778..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/RingView/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/RingView/renderDifferentParams.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/RingView/renderDifferentParams.png deleted file mode 100644 index 5285c2626..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/RingView/renderDifferentParams.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/render.png deleted file mode 100644 index a14dec71b..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/renderDataOffset.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/renderDataOffset.png deleted file mode 100644 index e266b88a0..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/renderDataOffset.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/renderDifferentSize.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/renderDifferentSize.png deleted file mode 100644 index 3fa863353..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/renderDifferentSize.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/renderMonthly.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/renderMonthly.png deleted file mode 100644 index aab4da0c7..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/renderMonthly.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/renderTransparent.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/renderTransparent.png deleted file mode 100644 index 12c7f8e3d..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/renderTransparent.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/renderYearly.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/renderYearly.png deleted file mode 100644 index 068864ecb..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/ScoreChart/renderYearly.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/StreakChart/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/StreakChart/render.png deleted file mode 100644 index 5c6e059d2..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/StreakChart/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/StreakChart/renderSmallSize.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/StreakChart/renderSmallSize.png deleted file mode 100644 index 6fb3e3763..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/StreakChart/renderSmallSize.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/common/StreakChart/renderTransparent.png b/android/uhabits-android/src/androidTest/assets/views-v21/common/StreakChart/renderTransparent.png deleted file mode 100644 index 5c6e059d2..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/common/StreakChart/renderTransparent.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkButtonView/render_explicit_check.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkButtonView/render_explicit_check.png deleted file mode 100644 index f3e2d3bef..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkButtonView/render_explicit_check.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkButtonView/render_implicit_check.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkButtonView/render_implicit_check.png deleted file mode 100644 index 2f7529768..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkButtonView/render_implicit_check.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkButtonView/render_unchecked.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkButtonView/render_unchecked.png deleted file mode 100644 index 54f006b13..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkButtonView/render_unchecked.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkPanelView/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkPanelView/render.png deleted file mode 100644 index 39b3ebb2c..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkPanelView/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkPanelView/render_different_color.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkPanelView/render_different_color.png deleted file mode 100644 index b55300ece..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkPanelView/render_different_color.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkPanelView/render_offset.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkPanelView/render_offset.png deleted file mode 100644 index 574701417..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkPanelView/render_offset.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkPanelView/render_reversed.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkPanelView/render_reversed.png deleted file mode 100644 index 2965eb691..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/CheckmarkPanelView/render_reversed.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HabitCardView/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HabitCardView/render.png deleted file mode 100644 index bf0e6b59d..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HabitCardView/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HabitCardView/render_changed.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HabitCardView/render_changed.png deleted file mode 100644 index bf0e6b59d..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HabitCardView/render_changed.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HabitCardView/render_numerical.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HabitCardView/render_numerical.png deleted file mode 100644 index a35768436..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HabitCardView/render_numerical.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HabitCardView/render_selected.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HabitCardView/render_selected.png deleted file mode 100644 index 3cb0a817f..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HabitCardView/render_selected.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HeaderView/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HeaderView/render.png deleted file mode 100644 index 4273511d5..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HeaderView/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HeaderView/render_reverse.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HeaderView/render_reverse.png deleted file mode 100644 index ca045595e..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HeaderView/render_reverse.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HintView/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HintView/render.png deleted file mode 100644 index dae19beb8..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/HintView/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberButtonView/render_above.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberButtonView/render_above.png deleted file mode 100644 index 8d493812f..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberButtonView/render_above.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberButtonView/render_below.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberButtonView/render_below.png deleted file mode 100644 index bd837a767..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberButtonView/render_below.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberButtonView/render_zero.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberButtonView/render_zero.png deleted file mode 100644 index 6bb711855..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberButtonView/render_zero.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberPanelView/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberPanelView/render.png deleted file mode 100644 index 9e9857e18..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberPanelView/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberPanelView/render_different_color.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberPanelView/render_different_color.png deleted file mode 100644 index 165226baa..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberPanelView/render_different_color.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberPanelView/render_offset.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberPanelView/render_offset.png deleted file mode 100644 index 8460f9b4f..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberPanelView/render_offset.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberPanelView/render_reversed.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberPanelView/render_reversed.png deleted file mode 100644 index 2adf58c23..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/list/NumberPanelView/render_reversed.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/FrequencyCard/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/FrequencyCard/render.png deleted file mode 100644 index bc3330fb4..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/FrequencyCard/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/HistoryCard/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/HistoryCard/render.png deleted file mode 100644 index eccf19d41..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/HistoryCard/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/OverviewCard/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/OverviewCard/render.png deleted file mode 100644 index 31c1098a0..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/OverviewCard/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/ScoreCard/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/ScoreCard/render.png deleted file mode 100644 index eabe0c6c7..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/ScoreCard/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/StreakCard/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/StreakCard/render.png deleted file mode 100644 index 79f5d3051..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/StreakCard/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/SubtitleCard/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/SubtitleCard/render.png deleted file mode 100644 index 96e2310e0..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/habits/show/SubtitleCard/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/CheckmarkWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/widgets/CheckmarkWidget/render.png deleted file mode 100644 index 00c6fc72b..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/CheckmarkWidget/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/CheckmarkWidgetView/checked.png b/android/uhabits-android/src/androidTest/assets/views-v21/widgets/CheckmarkWidgetView/checked.png deleted file mode 100644 index 6d62e5a69..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/CheckmarkWidgetView/checked.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/CheckmarkWidgetView/implicitly_checked.png b/android/uhabits-android/src/androidTest/assets/views-v21/widgets/CheckmarkWidgetView/implicitly_checked.png deleted file mode 100644 index 2b933181a..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/CheckmarkWidgetView/implicitly_checked.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/CheckmarkWidgetView/large_size.png b/android/uhabits-android/src/androidTest/assets/views-v21/widgets/CheckmarkWidgetView/large_size.png deleted file mode 100644 index cfb423e98..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/CheckmarkWidgetView/large_size.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/CheckmarkWidgetView/unchecked.png b/android/uhabits-android/src/androidTest/assets/views-v21/widgets/CheckmarkWidgetView/unchecked.png deleted file mode 100644 index 94eaf4404..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/CheckmarkWidgetView/unchecked.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/FrequencyWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/widgets/FrequencyWidget/render.png deleted file mode 100644 index 0b15dd658..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/FrequencyWidget/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/HistoryWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/widgets/HistoryWidget/render.png deleted file mode 100644 index fa7ef2110..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/HistoryWidget/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/ScoreWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/widgets/ScoreWidget/render.png deleted file mode 100644 index 59d94daa4..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/ScoreWidget/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/StreakWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v21/widgets/StreakWidget/render.png deleted file mode 100644 index 19a2700ca..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v21/widgets/StreakWidget/render.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/BarChart/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/BarChart/render.png index 3b243ba6f..627a90a3f 100644 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/BarChart/render.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/BarChart/render.png differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/BarChart/renderDifferentSize.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/BarChart/renderDifferentSize.png index c6f70b9fd..84c876eea 100644 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/BarChart/renderDifferentSize.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/BarChart/renderDifferentSize.png differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/render.png index d7a66cc79..95b59ae9b 100644 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/render.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/render.png differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderDataOffset.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderDataOffset.png index b094d1e3d..46fda1fec 100644 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderDataOffset.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderDataOffset.png differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/CheckmarkButtonView/render_implicit_check.png b/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/CheckmarkButtonView/render_implicit_check.png index 2f7529768..70d550081 100644 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/CheckmarkButtonView/render_implicit_check.png and b/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/CheckmarkButtonView/render_implicit_check.png differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/CheckmarkPanelView/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/CheckmarkPanelView/render.png index 39b3ebb2c..7ac4ca21a 100644 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/CheckmarkPanelView/render.png and b/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/CheckmarkPanelView/render.png differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/CheckmarkPanelView/render_different_color.png b/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/CheckmarkPanelView/render_different_color.png deleted file mode 100644 index b55300ece..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/CheckmarkPanelView/render_different_color.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/CheckmarkPanelView/render_reversed.png b/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/CheckmarkPanelView/render_reversed.png deleted file mode 100644 index 2965eb691..000000000 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/CheckmarkPanelView/render_reversed.png and /dev/null differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render.png index c3f29a4f4..11fe6cb21 100644 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render.png and b/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render.png differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render_changed.png b/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render_changed.png index c3f29a4f4..11fe6cb21 100644 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render_changed.png and b/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render_changed.png differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render_numerical.png b/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render_numerical.png index 81c5aff0e..92ae7a9e9 100644 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render_numerical.png and b/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render_numerical.png differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render_selected.png b/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render_selected.png index dee63a2c3..b89868030 100644 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render_selected.png and b/android/uhabits-android/src/androidTest/assets/views-v26/habits/list/HabitCardView/render_selected.png differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/widgets/CheckmarkWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/widgets/CheckmarkWidget/render.png index 346a81e77..42147c832 100644 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/widgets/CheckmarkWidget/render.png and b/android/uhabits-android/src/androidTest/assets/views-v26/widgets/CheckmarkWidget/render.png differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/widgets/FrequencyWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/widgets/FrequencyWidget/render.png index 6991525e2..452e0dd68 100644 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/widgets/FrequencyWidget/render.png and b/android/uhabits-android/src/androidTest/assets/views-v26/widgets/FrequencyWidget/render.png differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/widgets/HistoryWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/widgets/HistoryWidget/render.png index 207910f1f..4239c0ca3 100644 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/widgets/HistoryWidget/render.png and b/android/uhabits-android/src/androidTest/assets/views-v26/widgets/HistoryWidget/render.png differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/widgets/ScoreWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/widgets/ScoreWidget/render.png index f06396cbf..96eae98da 100644 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/widgets/ScoreWidget/render.png and b/android/uhabits-android/src/androidTest/assets/views-v26/widgets/ScoreWidget/render.png differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/widgets/StreakWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/widgets/StreakWidget/render.png index 5833a07ad..999af4424 100644 Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/widgets/StreakWidget/render.png and b/android/uhabits-android/src/androidTest/assets/views-v26/widgets/StreakWidget/render.png differ diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/widgets/TargetWidget/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/widgets/TargetWidget/render.png new file mode 100644 index 000000000..489c34b83 Binary files /dev/null and b/android/uhabits-android/src/androidTest/assets/views-v26/widgets/TargetWidget/render.png differ diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseUserInterfaceTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseUserInterfaceTest.java index 6b5a1931c..02eb10c31 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseUserInterfaceTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseUserInterfaceTest.java @@ -31,9 +31,8 @@ import org.isoron.uhabits.core.ui.screens.habits.list.*; import org.isoron.uhabits.core.utils.*; import org.junit.*; -import static androidx.test.InstrumentationRegistry.getContext; -import static androidx.test.InstrumentationRegistry.getTargetContext; -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static androidx.test.core.app.ApplicationProvider.*; +import static androidx.test.platform.app.InstrumentationRegistry.*; import static androidx.test.uiautomator.UiDevice.*; public class BaseUserInterfaceTest @@ -59,18 +58,18 @@ public class BaseUserInterfaceTest intent.setComponent(new ComponentName(PKG, cls.getCanonicalName())); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - getContext().startActivity(intent); + getApplicationContext().startActivity(intent); } @Before public void setUp() throws Exception { device = getInstance(getInstrumentation()); - TestButler.setup(getTargetContext()); - TestButler.verifyAnimationsDisabled(getTargetContext()); + TestButler.setup(getApplicationContext()); + TestButler.verifyAnimationsDisabled(getApplicationContext()); HabitsApplication app = - (HabitsApplication) getTargetContext().getApplicationContext(); + (HabitsApplication) getApplicationContext().getApplicationContext(); component = app.getComponent(); habitList = component.getHabitList(); prefs = component.getPreferences(); @@ -83,7 +82,7 @@ public class BaseUserInterfaceTest public void tearDown() throws Exception { for (int i = 0; i < 10; i++) device.pressBack(); - TestButler.teardown(getTargetContext()); + TestButler.teardown(getApplicationContext()); } private void resetState() throws Exception diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseViewTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseViewTest.java index fcd6ff9f6..d871da334 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseViewTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseViewTest.java @@ -20,13 +20,11 @@ package org.isoron.uhabits; import android.graphics.*; - -import androidx.annotation.NonNull; -import androidx.test.*; import android.view.*; import android.widget.*; -import androidx.test.platform.app.InstrumentationRegistry; +import androidx.annotation.*; +import androidx.test.platform.app.*; import org.isoron.androidbase.*; import org.isoron.androidbase.utils.*; @@ -35,10 +33,9 @@ import org.isoron.uhabits.widgets.*; import java.io.*; import java.util.*; -import static android.os.Build.VERSION.SDK_INT; -import static android.os.Build.VERSION_CODES.KITKAT; -import static android.os.Build.VERSION_CODES.LOLLIPOP; -import static android.view.View.MeasureSpec.makeMeasureSpec; +import static android.os.Build.VERSION.*; +import static android.os.Build.VERSION_CODES.*; +import static android.view.View.MeasureSpec.*; public class BaseViewTest extends BaseAndroidTest { diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitFixtures.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitFixtures.java index 364166de7..262839c58 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitFixtures.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitFixtures.java @@ -78,15 +78,49 @@ public class HabitFixtures return habit; } + public Habit createVeryLongHabit() + { + Habit habit = createEmptyHabit(); + habit.setFrequency(new Frequency(1, 2)); + habit.setColor(11); + + Timestamp today = DateUtils.getToday(); + int marks[] = {0, 3, 5, 6, 7, 10, 13, 14, 15, 18, 21, 22, 23, 24, 27, 28, 30, 31, 34, 37, + 39, 42, 43, 46, 47, 48, 51, 52, 54, 55, 57, 59, 62, 65, 68, 71, 73, 76, 79, + 80, 81, 83, 85, 86, 89, 90, 91, 94, 96, 98, 100, 103, 104, 106, 109, 111, + 112, 113, 115, 117, 120, 123, 126, 129, 132, 134, 136, 139, 141, 142, 145, + 148, 149, 151, 152, 154, 156, 157, 159, 161, 162, 163, 164, 166, 168, 170, + 172, 173, 174, 175, 176, 178, 180, 181, 184, 185, 188, 189, 190, 191, 194, + 195, 197, 198, 199, 200, 202, 205, 208, 211, 213, 215, 216, 218, 220, 222, + 223, 225, 227, 228, 230, 231, 232, 234, 235, 238, 241, 242, 244, 247, 250, + 251, 253, 254, 257, 260, 261, 263, 264, 266, 269, 272, 273, 276, 279, 281, + 284, 285, 288, 291, 292, 294, 296, 297, 299, 300, 301, 303, 306, 307, 308, + 309, 310, 313, 316, 319, 322, 324, 326, 329, 330, 332, 334, 335, 337, 338, + 341, 344, 345, 346, 347, 350, 352, 355, 358, 360, 361, 362, 363, 365, 368, + 371, 373, 374, 376, 379, 380, 382, 384, 385, 387, 389, 390, 392, 393, 395, + 396, 399, 401, 404, 407, 410, 411, 413, 414, 416, 417, 419, 420, 423, 424, + 427, 429, 431, 433, 436, 439, 440, 442, 445, 447, 450, 453, 454, 456, 459, + 460, 461, 464, 466, 468, 470, 473, 474, 475, 477, 479, 481, 482, 483, 486, + 489, 491, 493, 495, 497, 498, 500, 503, 504, 507, 510, 511, 512, 515, 518, + 519, 521, 522, 525, 528, 531, 532, 534, 537, 539, 541, 543, 544, 547, 550, + 551, 554, 556, 557, 560, 561, 564, 567, 568, 569, 570, 572, 575, 576, 579, + 582, 583, 584, 586, 589}; + + for (int mark : marks) + habit.getRepetitions().toggle(today.minus(mark)); + + return habit; + } + public Habit createLongNumericalHabit() { Habit habit = modelFactory.buildHabit(); - habit.setName("Take a walk"); - habit.setQuestion("How many steps did you walk today?"); + habit.setName("Read"); + habit.setQuestion("How many pages did you walk today?"); habit.setType(Habit.NUMBER_HABIT); habit.setTargetType(Habit.AT_LEAST); habit.setTargetValue(200.0); - habit.setUnit("steps"); + habit.setUnit("pages"); habitList.add(habit); Timestamp timestamp = DateUtils.getToday(); diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/WidgetTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/WidgetTest.java index 2c0f89527..21b60e61a 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/WidgetTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/WidgetTest.java @@ -45,6 +45,7 @@ public class WidgetTest extends BaseUserInterfaceTest pressHome(); clickCheckmarkWidget(); + clickCheckmarkWidget(); launchApp(); clickText("Wake up early"); diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/CommonSteps.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/CommonSteps.java index 28e09a1b3..1e4b1199e 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/CommonSteps.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/CommonSteps.java @@ -19,16 +19,15 @@ package org.isoron.uhabits.acceptance.steps; -import android.view.View; +import android.view.*; -import androidx.annotation.StringRes; +import androidx.annotation.*; +import androidx.recyclerview.widget.*; import androidx.test.espresso.*; import androidx.test.espresso.contrib.*; import androidx.test.uiautomator.*; -import androidx.recyclerview.widget.RecyclerView; - -import org.hamcrest.Matcher; +import org.hamcrest.*; import org.isoron.uhabits.*; import org.isoron.uhabits.R; import org.isoron.uhabits.activities.habits.list.*; @@ -39,8 +38,8 @@ import static androidx.test.espresso.action.ViewActions.*; import static androidx.test.espresso.assertion.PositionAssertions.*; import static androidx.test.espresso.assertion.ViewAssertions.*; import static androidx.test.espresso.matcher.ViewMatchers.*; -import static junit.framework.Assert.*; import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; public class CommonSteps extends BaseUserInterfaceTest { @@ -125,7 +124,7 @@ public class CommonSteps extends BaseUserInterfaceTest verifyDisplaysText(text[0]); for(int i = 1; i < text.length; i++) { verifyDisplaysText(text[i]); - onView(withText(text[i])).check(isBelow(withText(text[i-1]))); + onView(withText(text[i])).check(isCompletelyBelow(withText(text[i-1]))); } } diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/WidgetSteps.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/WidgetSteps.java index 3cb5a201c..be7bc4429 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/WidgetSteps.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/WidgetSteps.java @@ -22,7 +22,7 @@ package org.isoron.uhabits.acceptance.steps; import androidx.test.uiautomator.*; import static android.os.Build.VERSION.SDK_INT; -import static junit.framework.Assert.*; +import static org.junit.Assert.*; import static org.isoron.uhabits.BaseUserInterfaceTest.*; public class WidgetSteps { @@ -70,7 +70,7 @@ public class WidgetSteps { button = device.findObject(new UiSelector().text("Widgets")); } button.click(); - if (SDK_INT == 28) { + if (SDK_INT >= 28) { new UiScrollable(new UiSelector().resourceId(list_id)) .scrollForward(); } diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/HistoryChartTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/HistoryChartTest.java index d3fef3755..1c88ed265 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/HistoryChartTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/HistoryChartTest.java @@ -19,17 +19,18 @@ package org.isoron.uhabits.activities.common.views; +import androidx.test.ext.junit.runners.*; import androidx.test.filters.*; -import androidx.test.runner.*; - -import androidx.test.ext.junit.runners.AndroidJUnit4; import org.isoron.uhabits.*; import org.isoron.uhabits.core.models.*; +import org.isoron.uhabits.core.utils.*; import org.isoron.uhabits.utils.*; import org.junit.*; import org.junit.runner.*; +import static org.mockito.Mockito.*; + @RunWith(AndroidJUnit4.class) @MediumTest public class HistoryChartTest extends BaseViewTest @@ -38,6 +39,12 @@ public class HistoryChartTest extends BaseViewTest private HistoryChart chart; + private Habit habit; + + Timestamp today; + + private HistoryChart.Controller controller; + @Override @Before public void setUp() @@ -45,50 +52,59 @@ public class HistoryChartTest extends BaseViewTest super.setUp(); fixtures.purgeHabits(habitList); - Habit habit = fixtures.createLongHabit(); + habit = fixtures.createLongHabit(); + today = new Timestamp(DateUtils.getStartOfToday()); chart = new HistoryChart(targetContext); chart.setCheckmarks(habit.getCheckmarks().getAllValues()); chart.setColor(PaletteUtils.getAndroidTestColor(habit.getColor())); measureView(chart, dpToPixels(400), dpToPixels(200)); + + controller = mock(HistoryChart.Controller.class); + chart.setController(controller); + } + + @Test + public void tapDate_atInvalidLocations() throws Throwable + { + chart.setIsEditable(true); + chart.tap(dpToPixels(118), dpToPixels(13)); // header + chart.tap(dpToPixels(336), dpToPixels(60)); // tomorrow's square + chart.tap(dpToPixels(370), dpToPixels(60)); // right axis + verifyNoMoreInteractions(controller); + } + + @Test + public void tapDate_withEditableView() throws Throwable + { + chart.setIsEditable(true); + + chart.tap(dpToPixels(340), dpToPixels(40)); + verify(controller).onToggleCheckmark(today, Checkmark.SKIP); + chart.tap(dpToPixels(340), dpToPixels(40)); + verify(controller).onToggleCheckmark(today, Checkmark.NO); + chart.tap(dpToPixels(340), dpToPixels(40)); + verify(controller).onToggleCheckmark(today, Checkmark.YES_MANUAL); + verifyNoMoreInteractions(controller); } -// @Test -// public void tapDate_atInvalidLocations() throws Throwable -// { -// int expectedCheckmarkValues[] = habit.getCheckmarks().getAllValues(); -// -// chart.setIsEditable(true); -// tap(chart, 118, 13); // header -// tap(chart, 336, 60); // tomorrow's square -// tap(chart, 370, 60); // right axis -// waitForAsyncTasks(); -// -// int actualCheckmarkValues[] = habit.getCheckmarks().getAllValues(); -// assertThat(actualCheckmarkValues, equalTo(expectedCheckmarkValues)); -// } -// -// @Test -// public void tapDate_withEditableView() throws Throwable -// { -// chart.setIsEditable(true); -// tap(chart, 340, 40); // today's square -// waitForAsyncTasks(); -// -// long today = DateUtils.getStartOfToday(); -// assertFalse(habit.getRepetitions().containsTimestamp(today)); -// } -// -// @Test -// public void tapDate_withReadOnlyView() throws Throwable -// { -// chart.setIsEditable(false); -// tap(chart, 340, 40); // today's square -// waitForAsyncTasks(); -// -// long today = DateUtils.getStartOfToday(); -// assertTrue(habit.getRepetitions().containsTimestamp(today)); -// } + @Test + public void tapDate_withEmptyHabit() + { + chart.setIsEditable(true); + chart.setCheckmarks(new int[]{}); + chart.tap(dpToPixels(340), dpToPixels(40)); + verify(controller).onToggleCheckmark(today, Checkmark.YES_MANUAL); + verifyNoMoreInteractions(controller); + } + + @Test + public void tapDate_withReadOnlyView() throws Throwable + { + chart.setIsEditable(false); + chart.tap(dpToPixels(340), dpToPixels(40)); + verifyNoMoreInteractions(controller); + } @Test public void testRender() throws Throwable diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonViewTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonViewTest.kt index 41dc80c0a..e9e4109c8 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonViewTest.kt +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonViewTest.kt @@ -21,7 +21,6 @@ package org.isoron.uhabits.activities.habits.list.views import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.* -import androidx.test.runner.* import org.isoron.uhabits.* import org.isoron.uhabits.core.models.* import org.isoron.uhabits.utils.* @@ -41,7 +40,7 @@ class CheckmarkButtonViewTest : BaseViewTest() { override fun setUp() { super.setUp() view = component.getCheckmarkButtonViewFactory().create().apply { - value = Checkmark.UNCHECKED + value = Checkmark.NO color = PaletteUtils.getAndroidTestColor(5) onToggle = { toggled = true } } @@ -50,19 +49,19 @@ class CheckmarkButtonViewTest : BaseViewTest() { @Test fun testRender_explicitCheck() { - view.value = Checkmark.CHECKED_EXPLICITLY + view.value = Checkmark.YES_MANUAL assertRendersCheckedExplicitly() } @Test fun testRender_implicitCheck() { - view.value = Checkmark.CHECKED_IMPLICITLY + view.value = Checkmark.YES_AUTO assertRendersCheckedImplicitly() } @Test fun testRender_unchecked() { - view.value = Checkmark.UNCHECKED + view.value = Checkmark.NO assertRendersUnchecked() } diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelViewTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelViewTest.kt index 024b0fa7f..f1f8301a4 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelViewTest.kt +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelViewTest.kt @@ -21,7 +21,6 @@ package org.isoron.uhabits.activities.habits.list.views import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.* -import androidx.test.runner.* import org.hamcrest.CoreMatchers.* import org.hamcrest.MatcherAssert.* import org.isoron.uhabits.* @@ -43,13 +42,13 @@ class CheckmarkPanelViewTest : BaseViewTest() { super.setUp() prefs.isCheckmarkSequenceReversed = false - val checkmarks = intArrayOf(CHECKED_EXPLICITLY, - CHECKED_EXPLICITLY, - CHECKED_IMPLICITLY, - UNCHECKED, - UNCHECKED, - UNCHECKED, - CHECKED_EXPLICITLY) + val checkmarks = intArrayOf(YES_MANUAL, + YES_MANUAL, + YES_AUTO, + NO, + NO, + NO, + YES_MANUAL) view = component.getCheckmarkPanelViewFactory().create().apply { values = checkmarks @@ -93,8 +92,8 @@ class CheckmarkPanelViewTest : BaseViewTest() { @Test fun testToggle() { - var timestamps = mutableListOf() - view.onToggle = { timestamps.add(it) } + val timestamps = mutableListOf() + view.onToggle = { t, _ -> timestamps.add(t) } view.buttons[0].performLongClick() view.buttons[2].performLongClick() view.buttons[3].performLongClick() @@ -105,7 +104,7 @@ class CheckmarkPanelViewTest : BaseViewTest() { fun testToggle_withOffset() { val timestamps = mutableListOf() view.dataOffset = 3 - view.onToggle = { timestamps += it } + view.onToggle = { t, _ -> timestamps += t } view.buttons[0].performLongClick() view.buttons[2].performLongClick() view.buttons[3].performLongClick() diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/regression/ListHabitsRegressionTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/regression/ListHabitsRegressionTest.kt index 18ed417ec..0700bf89f 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/regression/ListHabitsRegressionTest.kt +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/regression/ListHabitsRegressionTest.kt @@ -44,6 +44,9 @@ class ListHabitsRegressionTest : BaseUserInterfaceTest() { clickOK() clickMenu(ADD) + verifyShowsScreen(SELECT_HABIT_TYPE) + clickText("Yes or No") + verifyShowsScreen(EDIT_HABIT) typeName("Hello world") clickSave() diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/regression/SavedStateTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/regression/SavedStateTest.kt index 5d783ad1f..0bea8f819 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/regression/SavedStateTest.kt +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/regression/SavedStateTest.kt @@ -20,35 +20,15 @@ package org.isoron.uhabits.regression import androidx.test.filters.* - import org.isoron.uhabits.* -import org.junit.* - import org.isoron.uhabits.acceptance.steps.CommonSteps.* -import org.isoron.uhabits.acceptance.steps.EditHabitSteps.* -import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.* -import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.* -import org.isoron.uhabits.acceptance.steps.WidgetSteps.clickText import org.isoron.uhabits.activities.about.* -import org.isoron.uhabits.activities.habits.list.* +import org.junit.* import java.lang.Thread.* @LargeTest class SavedStateTest : BaseUserInterfaceTest() { - @Test - @Throws(Exception::class) - fun shouldNotCrashWhenRotatingWeekdayPickerDialog() { - // https://github.com/iSoron/uhabits/issues/534 - launchApp() - clickMenu(ADD) - setReminder() - clickReminderDays() - unselectAllDays() - rotateDevice() - clickText("Monday") - } - /** * Make sure that the main activity can be recreated by using * BundleSavedState after being destroyed. See bug: diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/CheckmarkWidgetTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/CheckmarkWidgetTest.java index 0aeb4762f..7b5cd30c5 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/CheckmarkWidgetTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/CheckmarkWidgetTest.java @@ -20,7 +20,7 @@ package org.isoron.uhabits.widgets; import androidx.test.filters.*; -import androidx.test.runner.*; + import android.widget.*; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -51,13 +51,14 @@ public class CheckmarkWidgetTest extends BaseViewTest { super.setUp(); setTheme(R.style.WidgetTheme); + prefs.setWidgetOpacity(255); - habit = fixtures.createShortHabit(); + habit = fixtures.createVeryLongHabit(); checkmarks = habit.getCheckmarks(); CheckmarkWidget widget = new CheckmarkWidget(targetContext, 0, habit); - view = convertToView(widget, 200, 250); + view = convertToView(widget, 150, 200); - assertThat(checkmarks.getTodayValue(), equalTo(CHECKED_EXPLICITLY)); + assertThat(checkmarks.getTodayValue(), equalTo(YES_MANUAL)); } @Test @@ -70,8 +71,11 @@ public class CheckmarkWidgetTest extends BaseViewTest // possible to capture intents sent to BroadcastReceivers. button.performClick(); sleep(1000); + assertThat(checkmarks.getTodayValue(), equalTo(SKIP)); - assertThat(checkmarks.getTodayValue(), equalTo(UNCHECKED)); + button.performClick(); + sleep(1000); + assertThat(checkmarks.getTodayValue(), equalTo(NO)); } @Test diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/FrequencyWidgetTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/FrequencyWidgetTest.java index 31a459af3..626e53b3b 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/FrequencyWidgetTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/FrequencyWidgetTest.java @@ -47,8 +47,9 @@ public class FrequencyWidgetTest extends BaseViewTest { super.setUp(); setTheme(R.style.WidgetTheme); + prefs.setWidgetOpacity(255); - habit = fixtures.createLongHabit(); + habit = fixtures.createVeryLongHabit(); FrequencyWidget widget = new FrequencyWidget(targetContext, 0, habit, Calendar.SUNDAY); view = convertToView(widget, 400, 400); } @@ -59,9 +60,9 @@ public class FrequencyWidgetTest extends BaseViewTest assertWidgetProviderIsInstalled(FrequencyWidgetProvider.class); } -// @Test -// public void testRender() throws Exception -// { -// assertRenders(view, PATH + "render.png"); -// } + @Test + public void testRender() throws Exception + { + assertRenders(view, PATH + "render.png"); + } } diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/HistoryWidgetTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/HistoryWidgetTest.java index f059d99c1..758e4f519 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/HistoryWidgetTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/HistoryWidgetTest.java @@ -27,6 +27,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import org.isoron.uhabits.*; import org.isoron.uhabits.core.models.*; +import org.isoron.uhabits.utils.*; import org.junit.*; import org.junit.runner.*; @@ -47,8 +48,9 @@ public class HistoryWidgetTest extends BaseViewTest { super.setUp(); setTheme(R.style.WidgetTheme); + prefs.setWidgetOpacity(255); - habit = fixtures.createLongHabit(); + habit = fixtures.createVeryLongHabit(); HistoryWidget widget = new HistoryWidget(targetContext, 0, habit, Calendar.SUNDAY); view = convertToView(widget, 400, 400); } @@ -59,9 +61,9 @@ public class HistoryWidgetTest extends BaseViewTest assertWidgetProviderIsInstalled(HistoryWidgetProvider.class); } -// @Test -// public void testRender() throws Exception -// { -// assertRenders(view, PATH + "render.png"); -// } + @Test + public void testRender() throws Exception + { + assertRenders(view, PATH + "render.png"); + } } diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/ScoreWidgetTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/ScoreWidgetTest.java index 550984b65..ff977b088 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/ScoreWidgetTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/ScoreWidgetTest.java @@ -45,8 +45,9 @@ public class ScoreWidgetTest extends BaseViewTest { super.setUp(); setTheme(R.style.WidgetTheme); + prefs.setWidgetOpacity(255); - habit = fixtures.createLongHabit(); + habit = fixtures.createVeryLongHabit(); ScoreWidget widget = new ScoreWidget(targetContext, 0, habit); view = convertToView(widget, 400, 400); } @@ -57,9 +58,9 @@ public class ScoreWidgetTest extends BaseViewTest assertWidgetProviderIsInstalled(ScoreWidgetProvider.class); } -// @Test -// public void testRender() throws Exception -// { -// assertRenders(view, PATH + "render.png"); -// } + @Test + public void testRender() throws Exception + { + assertRenders(view, PATH + "render.png"); + } } diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/StreakWidgetTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/StreakWidgetTest.java index a9d4e6f8f..9bd6f99c5 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/StreakWidgetTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/StreakWidgetTest.java @@ -45,8 +45,9 @@ public class StreakWidgetTest extends BaseViewTest { super.setUp(); setTheme(R.style.WidgetTheme); + prefs.setWidgetOpacity(255); - habit = fixtures.createLongHabit(); + habit = fixtures.createVeryLongHabit(); StreakWidget widget = new StreakWidget(targetContext, 0, habit); view = convertToView(widget, 400, 400); } @@ -57,9 +58,9 @@ public class StreakWidgetTest extends BaseViewTest assertWidgetProviderIsInstalled(StreakWidgetProvider.class); } -// @Test -// public void testRender() throws Exception -// { -// assertRenders(view, PATH + "render.png"); -// } + @Test + public void testRender() throws Exception + { + assertRenders(view, PATH + "render.png"); + } } diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/TargetWidgetTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/TargetWidgetTest.java new file mode 100644 index 000000000..3dc1086c6 --- /dev/null +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/TargetWidgetTest.java @@ -0,0 +1,66 @@ +/* + * 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.uhabits.widgets; + +import android.widget.*; + +import androidx.test.ext.junit.runners.*; +import androidx.test.filters.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.core.models.*; +import org.junit.*; +import org.junit.runner.*; + +@RunWith(AndroidJUnit4.class) +@MediumTest +public class TargetWidgetTest extends BaseViewTest +{ + private static final String PATH = "widgets/TargetWidget/"; + + private Habit habit; + + private FrameLayout view; + + @Override + public void setUp() + { + super.setUp(); + setTheme(R.style.WidgetTheme); + prefs.setWidgetOpacity(255); + + habit = fixtures.createLongNumericalHabit(); + habit.setColor(11); + TargetWidget widget = new TargetWidget(targetContext, 0, habit); + view = convertToView(widget, 400, 400); + } + + @Test + public void testIsInstalled() + { + assertWidgetProviderIsInstalled(TargetWidgetProvider.class); + } + + @Test + public void testRender() throws Exception + { + assertRenders(view, PATH + "render.png"); + } +} diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetViewTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetViewTest.java index de54d565c..38d71c5bd 100644 --- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetViewTest.java +++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetViewTest.java @@ -70,7 +70,7 @@ public class CheckmarkWidgetViewTest extends BaseViewTest // @Test // public void testRender_implicitlyChecked() throws IOException // { -// view.setCheckmarkValue(Checkmark.CHECKED_IMPLICITLY); +// view.setCheckmarkValue(Checkmark.YES_AUTO); // view.refresh(); // assertRenders(view, PATH + "implicitly_checked.png"); // } @@ -85,7 +85,7 @@ public class CheckmarkWidgetViewTest extends BaseViewTest // @Test // public void testRender_unchecked() throws IOException // { -// view.setCheckmarkValue(Checkmark.UNCHECKED); +// view.setCheckmarkValue(Checkmark.NO); // view.refresh(); // assertRenders(view, PATH + "unchecked.png"); // } diff --git a/android/uhabits-android/src/main/AndroidManifest.xml b/android/uhabits-android/src/main/AndroidManifest.xml index f2dbda768..1a268ce57 100644 --- a/android/uhabits-android/src/main/AndroidManifest.xml +++ b/android/uhabits-android/src/main/AndroidManifest.xml @@ -1,10 +1,27 @@ + - + + + + + + + + + + + + @@ -173,7 +206,7 @@ + android:resource="@xml/widget_target_info" /> @@ -248,12 +281,6 @@ android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> - - - \ No newline at end of file diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplication.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplication.kt index df162766c..a2c2b0e99 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplication.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplication.kt @@ -62,6 +62,7 @@ class HabitsApplication : Application() { widgetUpdater = component.widgetUpdater widgetUpdater.startListening() + widgetUpdater.scheduleStartDayWidgetUpdate() reminderScheduler = component.reminderScheduler reminderScheduler.startListening() diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java index 8c110ab26..cc5e55b20 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java @@ -34,7 +34,6 @@ import org.isoron.uhabits.core.ui.screens.habits.list.*; import org.isoron.uhabits.core.utils.*; import org.isoron.uhabits.intents.*; import org.isoron.uhabits.receivers.*; -import org.isoron.uhabits.sync.*; import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.widgets.*; @@ -81,8 +80,6 @@ public interface HabitsApplicationComponent ReminderController getReminderController(); - SyncManager getSyncManager(); - TaskRunner getTaskRunner(); WidgetPreferences getWidgetPreferences(); diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java index a3c232cc7..dd5bd5525 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java @@ -181,6 +181,8 @@ public class HistoryEditorDialog extends AppCompatDialogFragment int color = PaletteUtils.getColor(getContext(), habit.getColor()); historyChart.setColor(color); historyChart.setCheckmarks(checkmarks); + historyChart.setNumerical(habit.isNumerical()); + historyChart.setTarget(habit.getTargetValue() / habit.getFrequency().getDenominator()); } } } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt index 37d695545..d50907100 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt @@ -23,6 +23,7 @@ import android.content.* import androidx.appcompat.app.* import android.text.* import android.view.* +import android.view.WindowManager.LayoutParams.* import android.view.inputmethod.* import android.widget.* import org.isoron.androidbase.activities.* @@ -74,7 +75,10 @@ class NumberPickerFactory } .create() - dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) + dialog.setOnShowListener { + picker.getChildAt(0)?.requestFocus() + dialog.window?.setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE) + } InterfaceUtils.setupEditorAction(picker, TextView.OnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_DONE) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BarChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BarChart.java index 598246431..3a420c811 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BarChart.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BarChart.java @@ -77,8 +77,6 @@ public class BarChart extends ScrollableChart @Nullable private List checkmarks; - private int primaryColor; - private int bucketSize = 7; private int backgroundColor; @@ -99,6 +97,10 @@ public class BarChart extends ScrollableChart private double target; + private int primaryColor; + + private int darkerPrimaryColor; + public BarChart(Context context) { super(context); @@ -148,7 +150,10 @@ public class BarChart extends ScrollableChart public void setColor(int primaryColor) { + StyledResources res = new StyledResources(getContext()); + int backgroundColor = res.getColor(R.attr.cardBgColor); this.primaryColor = primaryColor; + this.darkerPrimaryColor = ColorUtils.mixColors(primaryColor, backgroundColor, 0.6f); postInvalidate(); } @@ -271,7 +276,7 @@ public class BarChart extends ScrollableChart float margin = baseSize * 0.225f; float round = dpToPixels(getContext(), 2); - int color = textColor; + int color = darkerPrimaryColor; if (value / 1000 >= target) color = primaryColor; rect.inset(-margin, 0); @@ -364,7 +369,7 @@ public class BarChart extends ScrollableChart { if (value == 0) return; - int activeColor = textColor; + int activeColor = darkerPrimaryColor; if (value / 1000 >= target) activeColor = primaryColor; diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java index 39fe209fe..309cbf1eb 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java @@ -20,14 +20,13 @@ package org.isoron.uhabits.activities.common.views; import android.os.*; -import androidx.core.os.*; -import androidx.customview.view.AbsSavedState; + +import androidx.customview.view.*; public class BundleSavedState extends AbsSavedState { public static final Parcelable.Creator CREATOR = - ParcelableCompat.newCreator( - new ParcelableCompatCreatorCallbacks() + new ClassLoaderCreator() { @Override public BundleSavedState createFromParcel(Parcel source, @@ -36,12 +35,18 @@ public class BundleSavedState extends AbsSavedState return new BundleSavedState(source, loader); } + @Override + public BundleSavedState createFromParcel(Parcel source) + { + return null; + } + @Override public BundleSavedState[] newArray(int size) { return new BundleSavedState[size]; } - }); + }; public final Bundle bundle; diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java index 93b488b78..611271cbd 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java @@ -44,7 +44,7 @@ public class HistoryChart extends ScrollableChart { private int[] checkmarks; - private int target; + private double target; private Paint pSquareBg, pSquareFg, pTextHeader; @@ -75,16 +75,18 @@ public class HistoryChart extends ScrollableChart private int colors[]; + private int textColors[]; + private RectF baseLocation; private int primaryColor; private boolean isBackgroundTransparent; - private int textColor; - private int reverseTextColor; + private int backgroundColor; + private boolean isEditable; private String previousMonth; @@ -121,11 +123,7 @@ public class HistoryChart extends ScrollableChart @Override public boolean onSingleTapUp(MotionEvent e) { - if (!isEditable) return false; - - performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); float x, y; - try { int pointerId = e.getPointerId(0); @@ -139,21 +137,30 @@ public class HistoryChart extends ScrollableChart // e.getPointerId. return false; } + return tap(x, y); + } + + public boolean tap(float x, float y) + { + if (!isEditable) return false; + performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); final Timestamp timestamp = positionToTimestamp(x, y); if (timestamp == null) return false; Timestamp today = DateUtils.getToday(); + int newValue = YES_MANUAL; int offset = timestamp.daysUntil(today); if (offset < checkmarks.length) { - boolean isChecked = checkmarks[offset] == CHECKED_EXPLICITLY; - checkmarks[offset] = (isChecked ? UNCHECKED : CHECKED_EXPLICITLY); + newValue = Repetition.nextToggleValue(checkmarks[offset]); + checkmarks[offset] = newValue; } - controller.onToggleCheckmark(timestamp); + controller.onToggleCheckmark(timestamp, newValue); postInvalidate(); return true; + } public void populateWithRandomData() @@ -195,6 +202,7 @@ public class HistoryChart extends ScrollableChart public void setNumerical(boolean numerical) { isNumerical = numerical; + postInvalidate(); } public void setIsBackgroundTransparent(boolean isBackgroundTransparent) @@ -208,7 +216,7 @@ public class HistoryChart extends ScrollableChart this.isEditable = isEditable; } - public void setTarget(int target) + public void setTarget(double target) { this.target = target; postInvalidate(); @@ -227,6 +235,7 @@ public class HistoryChart extends ScrollableChart pTextHeader.setAntiAlias(true); pSquareBg = new Paint(); + pSquareBg.setAntiAlias(true); pSquareFg = new Paint(); pSquareFg.setAntiAlias(true); @@ -245,7 +254,7 @@ public class HistoryChart extends ScrollableChart headerOverflow = 0; previousMonth = ""; previousYear = ""; - pTextHeader.setColor(textColor); + pTextHeader.setColor(textColors[1]); updateDate(); GregorianCalendar currentDate = (GregorianCalendar) baseDate.clone(); @@ -362,21 +371,56 @@ public class HistoryChart extends ScrollableChart GregorianCalendar date, int checkmarkOffset) { - if (checkmarkOffset >= checkmarks.length) pSquareBg.setColor(colors[0]); + + int checkmark = 0; + if (checkmarkOffset >= checkmarks.length) + { + pSquareBg.setColor(colors[0]); + pSquareFg.setColor(textColors[1]); + } else { - int checkmark = checkmarks[checkmarkOffset]; - if(checkmark == 0) pSquareBg.setColor(colors[0]); - else if(checkmark < target) + checkmark = checkmarks[checkmarkOffset]; + if(checkmark == 0) + { + pSquareBg.setColor(colors[0]); + pSquareFg.setColor(textColors[1]); + } + else if ((isNumerical && (checkmark / 1000f >= target) || (!isNumerical && checkmark == YES_MANUAL))) + { + pSquareBg.setColor(colors[2]); + pSquareFg.setColor(textColors[2]); + } + else { - pSquareBg.setColor(isNumerical ? textColor : colors[1]); + pSquareBg.setColor(colors[1]); + pSquareFg.setColor(textColors[2]); } - else pSquareBg.setColor(colors[2]); } - pSquareFg.setColor(reverseTextColor); float round = dpToPixels(getContext(), 2); canvas.drawRoundRect(location, round, round, pSquareBg); + + if (!isNumerical && checkmark == SKIP) + { + pSquareBg.setColor(backgroundColor); + pSquareBg.setStrokeWidth(columnWidth * 0.025f); + + canvas.save(); + canvas.clipRect(location); + float offset = - columnWidth; + for (int k = 0; k < 10; k++) + { + offset += columnWidth / 5; + canvas.drawLine(location.left + offset, + location.bottom, + location.right + offset, + location.top, + pSquareBg); + } + canvas.restore(); + } + String text = Integer.toString(date.get(Calendar.DAY_OF_MONTH)); canvas.drawText(text, location.centerX(), location.centerY() + squareTextOffset, pSquareFg); @@ -416,13 +460,19 @@ public class HistoryChart extends ScrollableChart int green = Color.green(primaryColor); int blue = Color.blue(primaryColor); + backgroundColor = res.getColor(R.attr.cardBgColor); + if (isBackgroundTransparent) { colors = new int[3]; colors[0] = Color.argb(16, 255, 255, 255); colors[1] = Color.argb(128, red, green, blue); colors[2] = primaryColor; - textColor = Color.WHITE; + + textColors = new int[3]; + textColors[0] = Color.WHITE; + textColors[1] = Color.WHITE; + textColors[2] = Color.WHITE; reverseTextColor = Color.WHITE; } else @@ -431,9 +481,12 @@ public class HistoryChart extends ScrollableChart colors[0] = res.getColor(R.attr.lowContrastTextColor); colors[1] = Color.argb(127, red, green, blue); colors[2] = primaryColor; - textColor = res.getColor(R.attr.mediumContrastTextColor); - reverseTextColor = - res.getColor(R.attr.highContrastReverseTextColor); + + textColors = new int[3]; + textColors[0] = res.getColor(R.attr.lowContrastReverseTextColor); + textColors[1] = res.getColor(R.attr.mediumContrastTextColor); + textColors[2] = res.getColor(R.attr.highContrastReverseTextColor); + reverseTextColor = res.getColor(R.attr.highContrastReverseTextColor); } } @@ -492,6 +545,6 @@ public class HistoryChart extends ScrollableChart public interface Controller { - default void onToggleCheckmark(Timestamp timestamp) {} + default void onToggleCheckmark(Timestamp timestamp, int value) {} } } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/StreakChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/StreakChart.java index 705537ba4..70f6c05d7 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/StreakChart.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/StreakChart.java @@ -46,6 +46,8 @@ public class StreakChart extends View private int[] colors; + private int[] textColors; + private RectF rect; private int baseSize; @@ -70,8 +72,6 @@ public class StreakChart extends View private int textColor; - private int reverseTextColor; - public StreakChart(Context context) { super(context); @@ -213,7 +213,7 @@ public class StreakChart extends View float yOffset = rect.centerY() + 0.3f * em; - paint.setColor(reverseTextColor); + paint.setColor(percentageToTextColor(percentage)); paint.setTextAlign(Paint.Align.CENTER); canvas.drawText(Long.toString(streak.getLength()), rect.centerX(), yOffset, paint); @@ -223,7 +223,7 @@ public class StreakChart extends View String startLabel = dateFormat.format(streak.getStart().toJavaDate()); String endLabel = dateFormat.format(streak.getEnd().toJavaDate()); - paint.setColor(textColor); + paint.setColor(textColors[1]); paint.setTextAlign(Paint.Align.RIGHT); canvas.drawText(startLabel, gap - textMargin, yOffset, paint); @@ -258,8 +258,11 @@ public class StreakChart extends View colors[2] = Color.argb(192, red, green, blue); colors[1] = Color.argb(96, red, green, blue); colors[0] = res.getColor(R.attr.lowContrastTextColor); - textColor = res.getColor(R.attr.mediumContrastTextColor); - reverseTextColor = res.getColor(R.attr.highContrastReverseTextColor); + + textColors = new int[3]; + textColors[2] = res.getColor(R.attr.highContrastReverseTextColor); + textColors[1] = res.getColor(R.attr.mediumContrastTextColor); + textColors[0] = res.getColor(R.attr.lowContrastReverseTextColor); } private void initPaints() @@ -277,6 +280,12 @@ public class StreakChart extends View return colors[0]; } + private int percentageToTextColor(float percentage) + { + if (percentage >= 0.5f) return textColors[2]; + return textColors[1]; + } + private void updateMaxMinLengths() { maxLength = 0; diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/TaskProgressBar.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/TaskProgressBar.kt index 13a23fc07..452c60369 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/TaskProgressBar.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/TaskProgressBar.kt @@ -20,8 +20,8 @@ package org.isoron.uhabits.activities.common.views import android.content.* +import android.view.* import android.widget.* -import org.isoron.androidbase.activities.* import org.isoron.uhabits.core.tasks.* class TaskProgressBar( @@ -34,7 +34,7 @@ class TaskProgressBar( ), TaskRunner.Listener { init { - visibility = BaseRootView.GONE + visibility = View.GONE isIndeterminate = true } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt index 266939988..cfa03d75c 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt @@ -37,7 +37,6 @@ import org.isoron.uhabits.core.commands.* import org.isoron.uhabits.core.models.* import org.isoron.uhabits.databinding.* import org.isoron.uhabits.utils.* -import kotlin.math.* class EditHabitActivity : AppCompatActivity() { @@ -212,7 +211,10 @@ class EditHabitActivity : AppCompatActivity() { habit.color = paletteColor if (reminderHour >= 0) { habit.setReminder(Reminder(reminderHour, reminderMin, reminderDays)) + } else { + habit.setReminder(null) } + habit.frequency = Frequency(freqNum, freqDen) if (habitType == Habit.NUMBER_HABIT) { habit.targetValue = targetInput.text.toString().toDouble() @@ -233,16 +235,16 @@ class EditHabitActivity : AppCompatActivity() { private fun validate(): Boolean { var isValid = true if (nameInput.text.isEmpty()) { - nameInput.error = getString(R.string.validation_should_not_be_blank) + nameInput.error = getString(R.string.validation_cannot_be_blank) isValid = false } if (habitType == Habit.NUMBER_HABIT) { if(unitInput.text.isEmpty()) { - unitInput.error = getString(R.string.validation_should_not_be_blank) + unitInput.error = getString(R.string.validation_cannot_be_blank) isValid = false } if(targetInput.text.isEmpty()) { - targetInput.error = getString(R.string.validation_should_not_be_blank) + targetInput.error = getString(R.string.validation_cannot_be_blank) isValid = false } } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt index 77981995a..eded35446 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonView.kt @@ -27,6 +27,7 @@ import android.view.View.MeasureSpec.* import com.google.auto.factory.* import org.isoron.androidbase.activities.* import org.isoron.uhabits.* +import org.isoron.uhabits.core.models.* import org.isoron.uhabits.core.models.Checkmark.* import org.isoron.uhabits.core.preferences.* import org.isoron.uhabits.utils.* @@ -51,7 +52,7 @@ class CheckmarkButtonView( invalidate() } - var onToggle: () -> Unit = {} + var onToggle: (Int) -> Unit = {} private var drawer = Drawer() init { @@ -61,11 +62,8 @@ class CheckmarkButtonView( } fun performToggle() { - onToggle() - value = when (value) { - CHECKED_EXPLICITLY -> UNCHECKED - else -> CHECKED_EXPLICITLY - } + value = Repetition.nextToggleValue(value) + onToggle(value) performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) invalidate() } @@ -105,11 +103,13 @@ class CheckmarkButtonView( fun draw(canvas: Canvas) { paint.color = when (value) { - CHECKED_EXPLICITLY -> color + YES_MANUAL -> color + SKIP -> color else -> lowContrastColor } val id = when (value) { - UNCHECKED -> R.string.fa_times + SKIP -> R.string.fa_skipped + NO -> R.string.fa_times else -> R.string.fa_check } val label = resources.getString(id) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt index c0e8ae2e9..53194abae 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelView.kt @@ -46,7 +46,7 @@ class CheckmarkPanelView( setupButtons() } - var onToggle: (Timestamp) -> Unit = {} + var onToggle: (Timestamp, Int) -> Unit = {_, _ ->} set(value) { field = value setupButtons() @@ -62,10 +62,10 @@ class CheckmarkPanelView( val timestamp = today.minus(index + dataOffset) button.value = when { index + dataOffset < values.size -> values[index + dataOffset] - else -> UNCHECKED + else -> NO } button.color = color - button.onToggle = { onToggle(timestamp) } + button.onToggle = { value -> onToggle(timestamp, value) } } } } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/EmptyListView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/EmptyListView.kt index 3d4ddafb1..c92eb341f 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/EmptyListView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/EmptyListView.kt @@ -24,15 +24,14 @@ import android.view.* import android.view.Gravity.* import android.view.ViewGroup.LayoutParams.* import android.widget.* -import org.isoron.androidbase.activities.* import org.isoron.uhabits.* import org.isoron.uhabits.utils.* class EmptyListView(context: Context) : LinearLayout(context) { init { orientation = VERTICAL - gravity = Gravity.CENTER - visibility = BaseRootView.GONE + gravity = CENTER + visibility = View.GONE addView(TextView(context).apply { text = str(R.string.fa_star_half_o) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.java index ad4f8013e..62394690e 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.java @@ -319,6 +319,12 @@ public class HabitCardListAdapter preferences.setDefaultOrder(order); } + @Override + public HabitList.Order getOrder() + { + return cache.getOrder(); + } + /** * Selects or deselects the item at a given position. * diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.kt index 259d1f14a..02f1d6421 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.kt @@ -79,7 +79,7 @@ class HabitCardListView( cardView.dataOffset = dataOffset cardView.score = score cardView.unit = habit.unit - cardView.threshold = habit.targetValue + cardView.threshold = habit.targetValue / habit.frequency.denominator val detector = GestureDetector(context, CardViewGestureDetector(holder)) cardView.setOnTouchListener { _, ev -> diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt index f6adfcc16..e7891253d 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardView.kt @@ -121,9 +121,9 @@ class HabitCardView( } checkmarkPanel = checkmarkPanelFactory.create().apply { - onToggle = { timestamp -> + onToggle = { timestamp, value -> triggerRipple(timestamp) - habit?.let { behavior.onToggle(it, timestamp) } + habit?.let { behavior.onToggle(it, timestamp, value) } } } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitRootView.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitRootView.java index e1a986294..a49430346 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitRootView.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitRootView.java @@ -158,6 +158,7 @@ public class ShowHabitRootView extends BaseRootView if(habit.isNumerical()) { overviewCard.setVisibility(View.GONE); + streakCard.setVisibility(View.GONE); } else { targetCard.setVisibility(View.GONE); } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java index d19eb7cb5..cf1dcfbd2 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java @@ -26,9 +26,9 @@ import androidx.annotation.NonNull; import org.isoron.androidbase.activities.*; import org.isoron.uhabits.*; import org.isoron.uhabits.activities.common.dialogs.*; -import org.isoron.uhabits.activities.habits.edit.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.core.ui.callbacks.*; +import org.isoron.uhabits.core.ui.screens.habits.list.*; import org.isoron.uhabits.core.ui.screens.habits.show.*; import org.isoron.uhabits.intents.*; @@ -54,6 +54,9 @@ public class ShowHabitScreen extends BaseScreen @NonNull private final IntentFactory intentFactory; + @NonNull + private final NumberPickerFactory numberPickerFactory; + @Inject public ShowHabitScreen(@NonNull BaseActivity activity, @NonNull Habit habit, @@ -61,6 +64,7 @@ public class ShowHabitScreen extends BaseScreen @NonNull ShowHabitsMenu menu, @NonNull ConfirmDeleteDialogFactory confirmDeleteDialogFactory, @NonNull IntentFactory intentFactory, + @NonNull NumberPickerFactory numberPickerFactory, @NonNull Lazy behavior) { super(activity); @@ -71,6 +75,7 @@ public class ShowHabitScreen extends BaseScreen this.habit = habit; this.behavior = behavior; this.confirmDeleteDialogFactory = confirmDeleteDialogFactory; + this.numberPickerFactory = numberPickerFactory; view.setController(this); } @@ -81,9 +86,17 @@ public class ShowHabitScreen extends BaseScreen } @Override - public void onToggleCheckmark(Timestamp timestamp) + public void showNumberPicker(double value, + @NonNull String unit, + @NonNull ListHabitsBehavior.NumberPickerCallback callback) + { + numberPickerFactory.create(value, unit, callback).show(); + } + + @Override + public void onToggleCheckmark(Timestamp timestamp, int value) { - behavior.get().onToggleCheckmark(timestamp); + behavior.get().onToggleCheckmark(timestamp, value); } @Override diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCard.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCard.java index 6b5dfe394..621286665 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCard.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCard.java @@ -149,7 +149,7 @@ public class BarCard extends HabitCard if (habit.isNumerical()) { boolSpinner.setVisibility(GONE); - chart.setTarget(habit.getTargetValue() * bucketSize); + chart.setTarget(habit.getTargetValue() / habit.getFrequency().getDenominator() * bucketSize); } else { diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCard.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCard.java index 7e1f4948b..82110d410 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCard.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCard.java @@ -132,8 +132,8 @@ public class HistoryCard extends HabitCard chart.setColor(color); if(habit.isNumerical()) { - chart.setTarget((int) (habit.getTargetValue() * 1000)); chart.setNumerical(true); + chart.setTarget(habit.getTargetValue() / habit.getFrequency().getDenominator()); } } } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java index 049f6e319..7f9430b7b 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java @@ -155,8 +155,6 @@ public class SettingsFragment extends PreferenceFragmentCompat // Temporarily disable this; we now always ask findPreference("reminderSound").setVisible(false); findPreference("pref_snooze_interval").setVisible(false); - - updateSync(); } private void updateWeekdayPreference() @@ -183,7 +181,6 @@ public class SettingsFragment extends PreferenceFragmentCompat } if (key.equals("pref_first_weekday")) updateWeekdayPreference(); BackupManager.dataChanged("org.isoron.uhabits"); - updateSync(); } private void setResultOnPreferenceClick(String key, final int result) @@ -218,24 +215,4 @@ public class SettingsFragment extends PreferenceFragmentCompat Preference ringtonePreference = findPreference("reminderSound"); ringtonePreference.setSummary(ringtoneName); } - - private void updateSync() - { - if (prefs == null) return; - boolean enabled = prefs.isSyncEnabled(); - - Preference syncKey = findPreference("pref_sync_key"); - if (syncKey != null) - { - syncKey.setSummary(prefs.getSyncKey()); - syncKey.setVisible(enabled); - } - - Preference syncAddress = findPreference("pref_sync_address"); - if (syncAddress != null) - { - syncAddress.setSummary(prefs.getSyncAddress()); - syncAddress.setVisible(enabled); - } - } } \ No newline at end of file diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingController.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingController.kt index 2fdd01250..9f234dc9c 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingController.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingController.kt @@ -22,8 +22,7 @@ package org.isoron.uhabits.automation import android.app.* import android.content.* import android.os.* -import org.isoron.uhabits.* -import org.isoron.uhabits.automation.FireSettingReceiver.* +import org.isoron.uhabits.R import org.isoron.uhabits.core.models.* class EditSettingController(private val activity: Activity) { @@ -45,11 +44,13 @@ class EditSettingController(private val activity: Activity) { } private fun getActionName(action: Int): String { - when (action) { - ACTION_CHECK -> return activity.getString(R.string.check) - ACTION_UNCHECK -> return activity.getString(R.string.uncheck) - ACTION_TOGGLE -> return activity.getString(R.string.toggle) - else -> return "???" + return when (action) { + ACTION_CHECK -> activity.getString(R.string.check) + ACTION_UNCHECK -> activity.getString(R.string.uncheck) + ACTION_TOGGLE -> activity.getString(R.string.toggle) + ACTION_INCREMENT -> activity.getString(R.string.increment) + ACTION_DECREMENT -> activity.getString(R.string.decrement) + else -> "???" } } } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingRootView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingRootView.kt index 83de784e1..0d6979ec1 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingRootView.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingRootView.kt @@ -21,6 +21,7 @@ package org.isoron.uhabits.automation import android.R.layout.* import android.content.* +import android.view.* import androidx.appcompat.widget.* import androidx.appcompat.widget.Toolbar import android.widget.* @@ -51,10 +52,17 @@ class EditSettingRootView( addView(inflate(getContext(), R.layout.automation, null)) ButterKnife.bind(this) populateHabitSpinner() - + habitSpinner.onItemSelectedListener = object: AdapterView.OnItemSelectedListener { + override fun onNothingSelected(parent: AdapterView<*>?) { + } + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + populateActionSpinner(habitList.getByPosition(position).isNumerical) + } + } args?.let { habitSpinner.setSelection(habitList.indexOf(it.habit)) - actionSpinner.setSelection(it.action) + populateActionSpinner(it.habit.isNumerical) + actionSpinner.setSelection(mapActionToSpinnerPosition(it.action)) } } @@ -72,16 +80,49 @@ class EditSettingRootView( @OnClick(R.id.buttonSave) fun onClickSave() { - val action = actionSpinner.selectedItemPosition - val habitPosition = habitSpinner.selectedItemPosition - val habit = habitList.getByPosition(habitPosition) + val habit = habitList.getByPosition(habitSpinner.selectedItemPosition) + val action = mapSpinnerPositionToAction(habit.isNumerical, + actionSpinner.selectedItemPosition) controller.onSave(habit, action) } + private fun mapSpinnerPositionToAction(isNumerical: Boolean, itemPosition: Int): Int { + return if (isNumerical) { + when (itemPosition) { + 0 -> ACTION_INCREMENT + else -> ACTION_DECREMENT + } + } else { + when (itemPosition) { + 0 -> ACTION_CHECK + 1 -> ACTION_UNCHECK + else -> ACTION_TOGGLE + } + } + } + + private fun mapActionToSpinnerPosition(action: Int): Int { + return when(action) { + ACTION_CHECK -> 0 + ACTION_UNCHECK -> 1 + ACTION_TOGGLE -> 2 + ACTION_INCREMENT -> 0 + ACTION_DECREMENT -> 1 + else -> 0 + } + } + private fun populateHabitSpinner() { - val names = habitList.mapTo(LinkedList()) { it.name } + val names = habitList.mapTo(LinkedList()) { it.name } val adapter = ArrayAdapter(context, simple_spinner_item, names) adapter.setDropDownViewResource(simple_spinner_dropdown_item) habitSpinner.adapter = adapter } + + private fun populateActionSpinner(isNumerical: Boolean) { + val entries = (if (isNumerical) R.array.actions_numerical else R.array.actions_yes_no) + val adapter = ArrayAdapter.createFromResource(context, entries, simple_spinner_item) + adapter.setDropDownViewResource(simple_spinner_dropdown_item) + actionSpinner.adapter = adapter + } } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/FireSettingReceiver.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/FireSettingReceiver.kt index a5d63ade5..eb33a4515 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/FireSettingReceiver.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/FireSettingReceiver.kt @@ -30,6 +30,9 @@ import org.isoron.uhabits.receivers.* const val ACTION_CHECK = 0 const val ACTION_UNCHECK = 1 const val ACTION_TOGGLE = 2 +const val ACTION_INCREMENT = 3 +const val ACTION_DECREMENT = 4 + const val EXTRA_BUNDLE = "com.twofortyfouram.locale.intent.extra.BUNDLE" const val EXTRA_STRING_BLURB = "com.twofortyfouram.locale.intent.extra.BLURB" @@ -52,11 +55,13 @@ class FireSettingReceiver : BroadcastReceiver() { ACTION_CHECK -> controller.onAddRepetition(args.habit, timestamp) ACTION_UNCHECK -> controller.onRemoveRepetition(args.habit, timestamp) ACTION_TOGGLE -> controller.onToggleRepetition(args.habit, timestamp) + ACTION_INCREMENT -> controller.onIncrement(args.habit, timestamp, 1000) + ACTION_DECREMENT -> controller.onDecrement(args.habit, timestamp, 1000) } } @ReceiverScope - @Component(dependencies = arrayOf(HabitsApplicationComponent::class)) + @Component(dependencies = [HabitsApplicationComponent::class]) internal interface ReceiverComponent { val widgetController: WidgetBehavior } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/SettingUtils.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/SettingUtils.kt index 7a8b0310a..1a56c91b8 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/SettingUtils.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/SettingUtils.kt @@ -9,7 +9,7 @@ object SettingUtils { fun parseIntent(intent: Intent, allHabits: HabitList): Arguments? { val bundle = intent.getBundleExtra(EXTRA_BUNDLE) ?: return null val action = bundle.getInt("action") - if (action < 0 || action > 2) return null + if (action < 0 || action > 4) return null val habit = allHabits.getById(bundle.getLong("habit")) ?: return null return Arguments(action, habit) } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentScheduler.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentScheduler.kt index 934942f15..2882d3022 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentScheduler.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentScheduler.kt @@ -45,7 +45,7 @@ class IntentScheduler private val manager = context.getSystemService(ALARM_SERVICE) as AlarmManager - fun schedule(timestamp: Long, intent: PendingIntent) { + fun schedule(timestamp: Long, intent: PendingIntent, alarmType: Int) { Log.d("IntentScheduler", "timestamp=" + timestamp + " current=" + System.currentTimeMillis()) if (timestamp < System.currentTimeMillis()) { @@ -54,19 +54,24 @@ class IntentScheduler return; } if (SDK_INT >= M) - manager.setExactAndAllowWhileIdle(RTC_WAKEUP, timestamp, intent) + manager.setExactAndAllowWhileIdle(alarmType, timestamp, intent) else - manager.setExact(RTC_WAKEUP, timestamp, intent) + manager.setExact(alarmType, timestamp, intent) } override fun scheduleShowReminder(reminderTime: Long, habit: Habit, timestamp: Long) { val intent = pendingIntents.showReminder(habit, reminderTime, timestamp) - schedule(reminderTime, intent) + schedule(reminderTime, intent, RTC_WAKEUP) logReminderScheduled(habit, reminderTime) } + override fun scheduleWidgetUpdate(updateTime: Long) { + val intent = pendingIntents.updateWidgets() + schedule(updateTime, intent, RTC) + } + override fun log(componentName: String, msg: String) { Log.d(componentName, msg) } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt index cb2f84532..2e36dd231 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt @@ -27,6 +27,7 @@ import org.isoron.androidbase.* import org.isoron.uhabits.core.* import org.isoron.uhabits.core.models.* import org.isoron.uhabits.receivers.* +import org.isoron.uhabits.widgets.* import javax.inject.* @AppScope @@ -118,4 +119,12 @@ class PendingIntentFactory if (timestamp != null) putExtra("timestamp", timestamp) }, FLAG_UPDATE_CURRENT) + + fun updateWidgets(): PendingIntent = + PendingIntent.getBroadcast( + context, 0, + Intent(context, WidgetReceiver::class.java).apply { + action = WidgetReceiver.ACTION_UPDATE_WIDGETS_VALUE + }, + FLAG_UPDATE_CURRENT) } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt index 3fff2b615..4589c5057 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt @@ -51,7 +51,6 @@ class AndroidNotificationTray Log.d("AndroidNotificationTray", msg) } - override fun removeNotification(id: Int) { val manager = NotificationManagerCompat.from(context) manager.cancel(id) @@ -63,8 +62,6 @@ class AndroidNotificationTray timestamp: Timestamp, reminderTime: Long) { val notificationManager = NotificationManagerCompat.from(context) - //val summary = buildSummary(habit, reminderTime) - //notificationManager.notify(Int.MAX_VALUE, summary) val notification = buildNotification(habit, reminderTime, timestamp) createAndroidNotificationChannel(context) try { @@ -98,30 +95,41 @@ class AndroidNotificationTray context.getString(R.string.no), pendingIntents.removeRepetition(habit)) + val enterAction = Action( + R.drawable.ic_action_check, + context.getString(R.string.enter), + pendingIntents.setNumericalValue(context, habit, 0, null)) + val wearableBg = decodeResource(context.resources, R.drawable.stripe) // Even though the set of actions is the same on the phone and // on the watch, Pebble requires us to add them to the // WearableExtender. - val wearableExtender = WearableExtender() - .setBackground(wearableBg) - .addAction(addRepetitionAction) - .addAction(removeRepetitionAction) + val wearableExtender = WearableExtender().setBackground(wearableBg) val defaultText = context.getString(R.string.default_reminder_question) - val builder = NotificationCompat.Builder(context, REMINDERS_CHANNEL_ID) + val builder = Builder(context, REMINDERS_CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) .setContentTitle(habit.name) .setContentText(if(habit.question.isBlank()) defaultText else habit.question) .setContentIntent(pendingIntents.showHabit(habit)) .setDeleteIntent(pendingIntents.dismissNotification(habit)) - .addAction(addRepetitionAction) - .addAction(removeRepetitionAction) .setSound(null) .setWhen(reminderTime) .setShowWhen(true) .setOngoing(preferences.shouldMakeNotificationsSticky()) - .setGroup("group" + habit.getId()) + + if (habit.isNumerical) { + wearableExtender.addAction(enterAction) + builder.addAction(enterAction) + } else { + wearableExtender + .addAction(addRepetitionAction) + .addAction(removeRepetitionAction) + builder + .addAction(addRepetitionAction) + .addAction(removeRepetitionAction) + } if (!disableSound) builder.setSound(ringtoneManager.getURI()) @@ -139,18 +147,6 @@ class AndroidNotificationTray return builder.build() } - private fun buildSummary(habit: Habit, - reminderTime: Long): Notification { - return NotificationCompat.Builder(context, REMINDERS_CHANNEL_ID) - .setSmallIcon(R.drawable.ic_notification) - .setContentTitle(context.getString(R.string.app_name)) - .setWhen(reminderTime) - .setShowWhen(true) - .setGroup("group" + habit.getId()) - .setGroupSummary(true) - .build() - } - companion object { private const val REMINDERS_CHANNEL_ID = "REMINDERS" fun createAndroidNotificationChannel(context: Context) { diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java index 0beeb9ca2..a66cc34a5 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java @@ -18,6 +18,7 @@ import com.android.datetimepicker.time.TimePickerDialog; import org.isoron.uhabits.*; import org.isoron.uhabits.core.models.*; import org.isoron.uhabits.receivers.*; +import org.isoron.uhabits.utils.*; import java.util.*; @@ -55,6 +56,8 @@ public class SnoozeDelayPickerActivity extends FragmentActivity dialog.getListView().setOnItemClickListener(this); dialog.setOnDismissListener(d -> finish()); dialog.show(); + + SystemUtils.unlockScreen(this); } private void showTimePicker() diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ConnectivityReceiver.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ConnectivityReceiver.kt deleted file mode 100644 index 0f861ae57..000000000 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ConnectivityReceiver.kt +++ /dev/null @@ -1,38 +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.uhabits.receivers - -import android.content.* -import android.content.Context.* -import android.net.* -import org.isoron.uhabits.* - -class ConnectivityReceiver : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - if (context == null || intent == null) return - val app = context.applicationContext as HabitsApplication - val networkInfo = (context.getSystemService(CONNECTIVITY_SERVICE) - as ConnectivityManager).activeNetworkInfo - val isConnected = (networkInfo != null) && - networkInfo.isConnectedOrConnecting - val syncManager = app.component.syncManager - syncManager.onNetworkStatusChanged(isConnected) - } -} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java index 20e0f65a7..a0af53a7e 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java @@ -26,7 +26,7 @@ import org.isoron.uhabits.*; import org.isoron.uhabits.core.preferences.*; import org.isoron.uhabits.core.ui.widgets.*; import org.isoron.uhabits.intents.*; -import org.isoron.uhabits.sync.*; +import org.isoron.uhabits.widgets.*; import org.isoron.uhabits.widgets.activities.*; import dagger.*; @@ -53,6 +53,9 @@ public class WidgetReceiver extends BroadcastReceiver public static final String ACTION_SET_NUMERICAL_VALUE = "org.isoron.uhabits.ACTION_SET_NUMERICAL_VALUE"; + public static final String ACTION_UPDATE_WIDGETS_VALUE = + "org.isoron.uhabits.ACTION_UPDATE_WIDGETS_VALUE"; + private static final String TAG = "WidgetReceiver"; @Override @@ -69,16 +72,17 @@ public class WidgetReceiver extends BroadcastReceiver IntentParser parser = app.getComponent().getIntentParser(); WidgetBehavior controller = component.getWidgetController(); Preferences prefs = app.getComponent().getPreferences(); + WidgetUpdater widgetUpdater = app.getComponent().getWidgetUpdater(); Log.i(TAG, String.format("Received intent: %s", intent.toString())); - if (prefs.isSyncEnabled()) - context.startService(new Intent(context, SyncService.class)); - try { - IntentParser.CheckmarkIntentData data; - data = parser.parseCheckmarkIntent(intent); + IntentParser.CheckmarkIntentData data = null; + if (intent.getAction() != ACTION_UPDATE_WIDGETS_VALUE) + { + data = parser.parseCheckmarkIntent(intent); + } switch (intent.getAction()) { @@ -109,6 +113,7 @@ public class WidgetReceiver extends BroadcastReceiver data.getTimestamp()); break; case ACTION_SET_NUMERICAL_VALUE: + context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); Intent numberSelectorIntent = new Intent(context, NumericalCheckmarkWidgetActivity.class); numberSelectorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); numberSelectorIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); @@ -116,6 +121,10 @@ public class WidgetReceiver extends BroadcastReceiver parser.copyIntentData(intent,numberSelectorIntent); context.startActivity(numberSelectorIntent); break; + case ACTION_UPDATE_WIDGETS_VALUE: + widgetUpdater.updateWidgets(); + widgetUpdater.scheduleStartDayWidgetUpdate(); + break; } } catch (RuntimeException e) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/Event.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/Event.java deleted file mode 100644 index 3d15d0d3e..000000000 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/Event.java +++ /dev/null @@ -1,59 +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.uhabits.sync; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.isoron.uhabits.core.database.*; - -@Table(name = "Events") -public class Event -{ - @Nullable - @Column - public Long id; - - @NonNull - @Column(name = "timestamp") - public Long timestamp; - - @NonNull - @Column(name = "message") - public String message; - - @NonNull - @Column(name = "server_id") - public String serverId; - - public Event() - { - timestamp = 0L; - message = ""; - serverId = ""; - } - - public Event(@NonNull String serverId, long timestamp, @NonNull String message) - { - this.serverId = serverId; - this.timestamp = timestamp; - this.message = message; - } -} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncManager.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncManager.java deleted file mode 100644 index 88fecb6bf..000000000 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncManager.java +++ /dev/null @@ -1,380 +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.uhabits.sync; - -import android.util.*; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.isoron.androidbase.*; -import org.isoron.uhabits.BuildConfig; -import org.isoron.uhabits.core.*; -import org.isoron.uhabits.core.commands.*; -import org.isoron.uhabits.core.database.*; -import org.isoron.uhabits.core.preferences.*; -import org.isoron.uhabits.database.*; -import org.isoron.uhabits.utils.*; -import org.json.*; - -import java.net.*; -import java.util.*; - -import javax.inject.*; - -import io.socket.client.*; -import io.socket.client.Socket; -import io.socket.emitter.*; - -import static io.socket.client.Socket.EVENT_CONNECT; -import static io.socket.client.Socket.EVENT_CONNECTING; -import static io.socket.client.Socket.EVENT_CONNECT_ERROR; -import static io.socket.client.Socket.EVENT_CONNECT_TIMEOUT; -import static io.socket.client.Socket.EVENT_DISCONNECT; -import static io.socket.client.Socket.EVENT_PING; -import static io.socket.client.Socket.EVENT_PONG; -import static io.socket.client.Socket.EVENT_RECONNECT; -import static io.socket.client.Socket.EVENT_RECONNECT_ATTEMPT; -import static io.socket.client.Socket.EVENT_RECONNECT_ERROR; -import static io.socket.client.Socket.EVENT_RECONNECT_FAILED; - -@AppScope -public class SyncManager implements CommandRunner.Listener -{ - public static final String EVENT_AUTH = "auth"; - - public static final String EVENT_AUTH_OK = "authOK"; - - public static final String EVENT_EXECUTE_EVENT = "execute"; - - public static final String EVENT_FETCH = "fetch"; - - public static final String EVENT_FETCH_OK = "fetchOK"; - - public static final String EVENT_POST_EVENT = "postEvent"; - - @NonNull - private String clientId; - - @NonNull - private String groupKey; - - @NonNull - private Socket socket; - - @NonNull - private LinkedList pendingConfirmation; - - @NonNull - private LinkedList pendingEmit; - - private boolean readyToEmit = false; - - @NonNull - private final Preferences prefs; - - @NonNull - private CommandRunner commandRunner; - - @NonNull - private CommandParser commandParser; - - private boolean isListening; - - private SSLContextProvider sslProvider; - - private final Repository repository; - - @Inject - public SyncManager(@NonNull SSLContextProvider sslProvider, - @NonNull Preferences prefs, - @NonNull CommandRunner commandRunner, - @NonNull CommandParser commandParser) - { - Log.i("SyncManager", this.toString()); - - this.sslProvider = sslProvider; - this.prefs = prefs; - this.commandRunner = commandRunner; - this.commandParser = commandParser; - this.isListening = false; - - repository = new Repository<>(Event.class, - new AndroidDatabase(DatabaseUtils.openDatabase())); - pendingConfirmation = new LinkedList<>(); - pendingEmit = new LinkedList<>(repository.findAll("order by timestamp")); - - groupKey = prefs.getSyncKey(); - clientId = prefs.getSyncClientId(); - String serverURL = prefs.getSyncAddress(); - - Log.d("SyncManager", clientId); - connect(serverURL); - } - - @Override - public void onCommandExecuted(@NonNull Command command, - @Nullable Long refreshKey) - { - if (command.isRemote()) return; - - JSONObject msg = toJSONObject(command.toJson()); - Long now = new Date().getTime(); - Event e = new Event(command.getId(), now, msg.toString()); - repository.save(e); - - Log.i("SyncManager", "Adding to outbox: " + msg.toString()); - - pendingEmit.add(e); - if (readyToEmit) emitPending(); - } - - public void onNetworkStatusChanged(boolean isConnected) - { - if (!isListening) return; - if (isConnected) socket.connect(); - else socket.disconnect(); - } - - public void startListening() - { - if (!prefs.isSyncEnabled()) return; - if (groupKey.isEmpty()) return; - if (isListening) return; - - isListening = true; - socket.connect(); - commandRunner.addListener(this); - } - - public void stopListening() - { - if (!isListening) return; - - commandRunner.removeListener(this); - socket.close(); - isListening = false; - } - - private void connect(String serverURL) - { - try - { - IO.setDefaultSSLContext(sslProvider.getCACertSSLContext()); - socket = IO.socket(serverURL); - - logSocketEvent(socket, EVENT_CONNECT, "Connected"); - logSocketEvent(socket, EVENT_CONNECT_TIMEOUT, "Connect timeout"); - logSocketEvent(socket, EVENT_CONNECTING, "Connecting..."); - logSocketEvent(socket, EVENT_CONNECT_ERROR, "Connect error"); - logSocketEvent(socket, EVENT_DISCONNECT, "Disconnected"); - logSocketEvent(socket, EVENT_RECONNECT, "Reconnected"); - logSocketEvent(socket, EVENT_RECONNECT_ATTEMPT, "Reconnecting..."); - logSocketEvent(socket, EVENT_RECONNECT_ERROR, "Reconnect error"); - logSocketEvent(socket, EVENT_RECONNECT_FAILED, "Reconnect failed"); - logSocketEvent(socket, EVENT_DISCONNECT, "Disconnected"); - logSocketEvent(socket, EVENT_PING, "Ping"); - logSocketEvent(socket, EVENT_PONG, "Pong"); - - socket.on(EVENT_CONNECT, new OnConnectListener()); - socket.on(EVENT_DISCONNECT, new OnDisconnectListener()); - socket.on(EVENT_EXECUTE_EVENT, new OnExecuteCommandListener()); - socket.on(EVENT_AUTH_OK, new OnAuthOKListener()); - socket.on(EVENT_FETCH_OK, new OnFetchOKListener()); - } - catch (URISyntaxException e) - { - throw new RuntimeException(e); - } - } - - private void emitPending() - { - try - { - for (Event e : pendingEmit) - { - Log.i("SyncManager", "Emitting: " + e.message); - socket.emit(EVENT_POST_EVENT, new JSONObject(e.message)); - pendingConfirmation.add(e); - } - - pendingEmit.clear(); - } - catch (JSONException e) - { - throw new RuntimeException(e); - } - } - - private void logSocketEvent(Socket socket, String event, final String msg) - { - socket.on(event, args -> - { - Log.i("SyncManager", msg); - for (Object o : args) - if (o instanceof SocketIOException) - ((SocketIOException) o).printStackTrace(); - }); - } - - private JSONObject toJSONObject(String json) - { - try - { - return new JSONObject(json); - } - catch (JSONException e) - { - throw new RuntimeException(e); - } - } - - private void updateLastSync(Long timestamp) - { - prefs.setLastSync(timestamp + 1); - } - - private class OnAuthOKListener implements Emitter.Listener - { - @Override - public void call(Object... args) - { - Log.i("SyncManager", "Auth OK"); - Log.i("SyncManager", "Requesting commands since last sync"); - - Long lastSync = prefs.getLastSync(); - socket.emit(EVENT_FETCH, buildFetchMessage(lastSync)); - } - - private JSONObject buildFetchMessage(Long lastSync) - { - try - { - JSONObject json = new JSONObject(); - json.put("since", lastSync); - return json; - } - catch (JSONException e) - { - throw new RuntimeException(e); - } - } - } - - private class OnConnectListener implements Emitter.Listener - { - @Override - public void call(Object... args) - { - Log.i("SyncManager", "Sending auth message"); - socket.emit(EVENT_AUTH, buildAuthMessage()); - } - - private JSONObject buildAuthMessage() - { - try - { - JSONObject json = new JSONObject(); - json.put("groupKey", groupKey); - json.put("clientId", clientId); - json.put("version", BuildConfig.VERSION_NAME); - return json; - } - catch (JSONException e) - { - throw new RuntimeException(e); - } - } - } - - private class OnDisconnectListener implements Emitter.Listener - { - @Override - public void call(Object... args) - { - readyToEmit = false; - for (Event e : pendingConfirmation) pendingEmit.add(e); - pendingConfirmation.clear(); - } - } - - private class OnExecuteCommandListener implements Emitter.Listener - { - @Override - public void call(Object... args) - { - try - { - Log.d("SyncManager", - String.format("Received command: %s", args[0].toString())); - JSONObject root = new JSONObject(args[0].toString()); - updateLastSync(root.getLong("timestamp")); - executeCommand(root); - } - catch (JSONException e) - { - throw new RuntimeException(e); - } - } - - private void executeCommand(JSONObject root) throws JSONException - { - Command received = commandParser.parse(root.toString()); - received.setRemote(true); - - for (Event e : pendingConfirmation) - { - if (e.serverId.equals(received.getId())) - { - Log.i("SyncManager", "Pending command confirmed"); - pendingConfirmation.remove(e); - repository.remove(e); - return; - } - } - - Log.d("SyncManager", "Executing received command"); - commandRunner.execute(received, null); - } - } - - private class OnFetchOKListener implements Emitter.Listener - { - @Override - public void call(Object... args) - { - try - { - Log.i("SyncManager", "Fetch OK"); - - JSONObject json = (JSONObject) args[0]; - updateLastSync(json.getLong("timestamp")); - - emitPending(); - readyToEmit = true; - } - catch (JSONException e) - { - throw new RuntimeException(e); - } - } - } -} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncService.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncService.java deleted file mode 100644 index 12adf46f2..000000000 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncService.java +++ /dev/null @@ -1,90 +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.uhabits.sync; - -import android.app.*; -import android.content.*; -import android.net.*; -import android.os.*; -import androidx.core.app.*; - -import org.isoron.uhabits.*; -import org.isoron.uhabits.core.preferences.*; -import org.isoron.uhabits.receivers.*; - -public class SyncService extends Service implements Preferences.Listener -{ - private SyncManager syncManager; - - private Preferences prefs; - - private ConnectivityReceiver connectivityReceiver; - - public SyncService() - { - } - - @Override - public IBinder onBind(Intent intent) - { - return null; - } - - @Override - public void onCreate() - { - Intent notificationIntent = new Intent(this, SyncService.class); - PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); - - Notification notification = new NotificationCompat.Builder(this) - .setContentTitle("Loop Habit Tracker") - .setContentText("Sync service running") - .setSmallIcon(R.drawable.ic_notification) - .setPriority(NotificationCompat.PRIORITY_MIN) - .setContentIntent(pendingIntent) - .build(); - - startForeground(99999, notification); - - connectivityReceiver = new ConnectivityReceiver(); - IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); - this.registerReceiver(connectivityReceiver, filter); - - HabitsApplication app = (HabitsApplication) getApplicationContext(); - syncManager = app.getComponent().getSyncManager(); - syncManager.startListening(); - - prefs = app.getComponent().getPreferences(); - prefs.addListener(this); - } - - @Override - public void onSyncFeatureChanged() - { - if(!prefs.isSyncEnabled()) stopSelf(); - } - - @Override - public void onDestroy() - { - unregisterReceiver(connectivityReceiver); - syncManager.stopListening(); - } -} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/SystemUtils.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/SystemUtils.java new file mode 100644 index 000000000..81adbc05e --- /dev/null +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/SystemUtils.java @@ -0,0 +1,26 @@ +package org.isoron.uhabits.utils; + +import android.app.*; +import android.content.*; +import android.os.*; +import android.view.*; + + +public class SystemUtils +{ + public static boolean isAndroidOOrLater() + { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; + } + + public static void unlockScreen(Activity activity) + { + if (isAndroidOOrLater()) { + KeyguardManager km = + (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE); + km.requestDismissKeyguard(activity, null); + } else { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); + } + } +} diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java index 98718da33..69e8437da 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java @@ -81,6 +81,7 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider if (manager == null) throw new RuntimeException("manager is null"); if (options == null) throw new RuntimeException("options is null"); updateDependencies(context); + context.setTheme(R.style.WidgetTheme); BaseWidget widget = getWidgetFromId(context, widgetId); diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidget.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidget.kt index 588a7fa25..4b97b38ba 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidget.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidget.kt @@ -67,9 +67,9 @@ open class CheckmarkWidget( private fun getNumericalCheckmarkState(): Int { return if (habit.isCompletedToday) { - Checkmark.CHECKED_EXPLICITLY + Checkmark.YES_MANUAL } else { - Checkmark.UNCHECKED + Checkmark.NO } } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/FrequencyWidget.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/FrequencyWidget.kt index bdc60a3f6..2829f812d 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/FrequencyWidget.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/FrequencyWidget.kt @@ -40,6 +40,7 @@ class FrequencyWidget( val widgetView = v as GraphWidgetView widgetView.setTitle(habit.name) widgetView.setBackgroundAlpha(preferedBackgroundAlpha) + if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f) (widgetView.dataView as FrequencyChart).apply { setFirstWeekday(firstWeekday) setColor(PaletteUtils.getColor(context, habit.color)) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt index 9860b3e15..a1abb02b2 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HistoryWidget.kt @@ -41,10 +41,13 @@ class HistoryWidget( override fun refreshData(view: View) { val widgetView = view as GraphWidgetView widgetView.setBackgroundAlpha(preferedBackgroundAlpha) + if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f) (widgetView.dataView as HistoryChart).apply { setFirstWeekday(firstWeekday) setColor(PaletteUtils.getColor(context, habit.color)) setCheckmarks(habit.checkmarks.allValues) + setNumerical(habit.isNumerical) + setTarget(habit.targetValue / habit.frequency.denominator) } } diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/ScoreWidget.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/ScoreWidget.kt index 93dbf6792..a7510ac14 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/ScoreWidget.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/ScoreWidget.kt @@ -46,6 +46,7 @@ class ScoreWidget( val widgetView = view as GraphWidgetView widgetView.setBackgroundAlpha(preferedBackgroundAlpha) + if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f) (widgetView.dataView as ScoreChart).apply { setIsTransparencyEnabled(true) setBucketSize(size) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StreakWidget.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StreakWidget.kt index 73d823c31..8cb0a4c6b 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StreakWidget.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StreakWidget.kt @@ -40,6 +40,7 @@ class StreakWidget( override fun refreshData(view: View) { val widgetView = view as GraphWidgetView widgetView.setBackgroundAlpha(preferedBackgroundAlpha) + if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f) (widgetView.dataView as StreakChart).apply { setColor(PaletteUtils.getColor(context, habit.color)) setStreaks(habit.streaks.getBest(maxStreakCount)) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/TargetWidget.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/TargetWidget.kt index e38e25f9e..9eae5c685 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/TargetWidget.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/TargetWidget.kt @@ -40,6 +40,7 @@ class TargetWidget( override fun refreshData(view: View) { val widgetView = view as GraphWidgetView widgetView.setBackgroundAlpha(preferedBackgroundAlpha) + if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f) val chart = (widgetView.dataView as TargetChart) with(TargetCard.RefreshTask(context, habit, prefs.firstWeekday, chart, null)) { onPreExecute() diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/WidgetUpdater.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/WidgetUpdater.kt index 17d6d5d77..e05bb48db 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/WidgetUpdater.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/WidgetUpdater.kt @@ -25,6 +25,8 @@ import org.isoron.androidbase.* import org.isoron.uhabits.core.commands.* import org.isoron.uhabits.core.preferences.* import org.isoron.uhabits.core.tasks.* +import org.isoron.uhabits.core.utils.* +import org.isoron.uhabits.intents.* import javax.inject.* /** @@ -36,7 +38,8 @@ class WidgetUpdater @AppContext private val context: Context, private val commandRunner: CommandRunner, private val taskRunner: TaskRunner, - private val widgetPrefs: WidgetPreferences + private val widgetPrefs: WidgetPreferences, + private val intentScheduler: IntentScheduler ) : CommandRunner.Listener { override fun onCommandExecuted(command: Command, refreshKey: Long?) { @@ -60,6 +63,11 @@ class WidgetUpdater commandRunner.removeListener(this) } + fun scheduleStartDayWidgetUpdate() { + val timestamp = DateUtils.getStartOfTomorrow() + intentScheduler.scheduleWidgetUpdate(timestamp); + } + fun updateWidgets(modifiedHabitId: Long?) { taskRunner.execute { updateWidgets(modifiedHabitId, CheckmarkWidgetProvider::class.java) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HabitPickerDialog.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/activities/HabitPickerDialog.kt similarity index 77% rename from android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HabitPickerDialog.kt rename to android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/activities/HabitPickerDialog.kt index 0ff16d9ef..1f5c5ee7c 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HabitPickerDialog.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/activities/HabitPickerDialog.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Álinson Santos Xavier + * Copyright (C) 2016-2020 Álinson Santos Xavier * * This file is part of Loop Habit Tracker. * @@ -17,7 +17,7 @@ * with this program. If not, see . */ -package org.isoron.uhabits.widgets +package org.isoron.uhabits.widgets.activities import android.app.* import android.appwidget.AppWidgetManager.* @@ -27,14 +27,29 @@ import android.widget.* import android.widget.AbsListView.* import org.isoron.uhabits.* import org.isoron.uhabits.core.preferences.* +import org.isoron.uhabits.widgets.* import java.util.* -class HabitPickerDialog : Activity() { +class BooleanHabitPickerDialog : HabitPickerDialog() { + override fun shouldHideNumerical() = true + override fun getEmptyMessage() = R.string.no_boolean_habits +} + +class NumericalHabitPickerDialog : HabitPickerDialog() { + override fun shouldHideBoolean() = true + override fun getEmptyMessage() = R.string.no_numerical_habits +} + +open class HabitPickerDialog : Activity() { private var widgetId = 0 private lateinit var widgetPreferences: WidgetPreferences private lateinit var widgetUpdater: WidgetUpdater + protected open fun shouldHideNumerical() = false + protected open fun shouldHideBoolean() = false + protected open fun getEmptyMessage() = R.string.no_habits + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val component = (applicationContext as HabitsApplication).component @@ -48,10 +63,18 @@ class HabitPickerDialog : Activity() { val habitNames = ArrayList() for (h in habitList) { if (h.isArchived) continue + if (h.isNumerical and shouldHideNumerical()) continue + if (!h.isNumerical and shouldHideBoolean()) continue habitIds.add(h.id!!) habitNames.add(h.name) } + if (habitNames.isEmpty()) { + setContentView(R.layout.widget_empty_activity) + findViewById(R.id.message).setText(getEmptyMessage()) + return; + } + setContentView(R.layout.widget_configure_activity) val listView = findViewById(R.id.listView) val saveButton = findViewById