" + getString(R.string.privacy_permissiondialog_prompt)));
+ requestDialog.setPositiveButton(getString(android.R.string.ok), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ //noinspection ConstantConditions
+ if (Build.VERSION.SDK_INT >= 16) {
+ ActivityCompat.requestPermissions(AlarmEditActivity.this, new String[] { android.Manifest.permission.READ_EXTERNAL_STORAGE }, REQUEST_STORAGE_PERMISSION );
+ }
+ }
+ });
+ requestDialog.setNegativeButton(getString(R.string.privacy_permissiondialog_ignore), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ ringtonePicker(item);
+ }
+ });
+ requestDialog.show();
+
+ } else {
+ ActivityCompat.requestPermissions(this, new String[] { android.Manifest.permission.READ_EXTERNAL_STORAGE }, REQUEST_STORAGE_PERMISSION );
+ }
+ } else ringtonePicker(item);
+ } else ringtonePicker(item);
+ }
+
+ protected void ringtonePicker(@NonNull AlarmClockItem item)
+ {
+ int ringtoneType = RingtoneManager.TYPE_RINGTONE;
+ if (!AlarmSettings.loadPrefAllRingtones(this)) {
+ ringtoneType = (item.type == AlarmClockItem.AlarmType.NOTIFICATION ? RingtoneManager.TYPE_NOTIFICATION : RingtoneManager.TYPE_ALARM);
+ }
+
+ Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, ringtoneType);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, item.type.getDisplayString());
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, AlarmSettings.getDefaultRingtoneUri(this, item.type));
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, (item.ringtoneURI != null ? Uri.parse(item.ringtoneURI) : null));
+ startActivityForResult(intent, REQUEST_RINGTONE);
+ }
+
+ protected void onRingtonePermissionResult(@NonNull String[] permissions, @NonNull int[] grantResults)
+ {
+ if (editor != null) {
+ ringtonePicker(editor.getItem());
+ }
+ }
+
+ protected void onRingtoneResult(int resultCode, Intent data)
+ {
+ if (resultCode == RESULT_OK && editor != null && data != null)
+ {
+ AlarmClockItem item = editor.getItem();
+ Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
+ if (uri != null)
+ {
+ Ringtone ringtone = RingtoneManager.getRingtone(this, uri);
+ if (ringtone != null)
+ {
+ String ringtoneName = ringtone.getTitle(this);
+ ringtone.stop();
+
+ item.ringtoneName = ringtoneName;
+ item.ringtoneURI = uri.toString();
+ Log.d(TAG, "onActivityResult: uri: " + item.ringtoneURI + ", title: " + ringtoneName);
+
+ } else {
+ item.ringtoneName = null;
+ item.ringtoneURI = null;
+ Log.d(TAG, "onActivityResult: uri: " + uri + " ");
+ }
+
+ } else {
+ item.ringtoneName = null;
+ item.ringtoneURI = null;
+ Log.d(TAG, "onActivityResult: null uri");
+ }
+ editor.notifyItemChanged();
+
+ } else {
+ Log.d(TAG, "onActivityResult: bad result: " + resultCode + ", " + data);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * pickLabel
+ */
+ protected void pickLabel(@NonNull AlarmClockItem item)
+ {
+ AlarmLabelDialog dialog = new AlarmLabelDialog();
+ dialog.setAccentColor(colorAlarmEnabled);
+ dialog.setOnAcceptedListener(onLabelChanged);
+ dialog.setLabel(item.label);
+ dialog.show(getSupportFragmentManager(), DIALOGTAG_LABEL);
+ }
+ private DialogInterface.OnClickListener onLabelChanged = new DialogInterface.OnClickListener()
+ {
+ @Override
+ public void onClick(DialogInterface d, int which)
+ {
+ FragmentManager fragments = getSupportFragmentManager();
+ AlarmLabelDialog dialog = (AlarmLabelDialog) fragments.findFragmentByTag(DIALOGTAG_LABEL);
+ if (editor != null && dialog != null)
+ {
+ AlarmClockItem item = editor.getItem();
+ item.label = dialog.getLabel();
+ editor.notifyItemChanged();
+ }
+ }
+ };
+
+ /**
+ * pickOffset
+ */
+ protected void pickOffset(@NonNull AlarmClockItem item)
+ {
+ if (Build.VERSION.SDK_INT >= 11)
+ {
+ int eventType = item.event != null ? item.event.getType() : -1;
+ AlarmOffsetDialog offsetDialog = new AlarmOffsetDialog();
+ offsetDialog.setShowDays(eventType == SolarEvents.TYPE_MOONPHASE || eventType == SolarEvents.TYPE_SEASON);
+ offsetDialog.setOffset(item.offset);
+ offsetDialog.setOnAcceptedListener(onOffsetChanged);
+ offsetDialog.show(getSupportFragmentManager(), DIALOGTAG_OFFSET + 1);
+
+ } else {
+ Toast.makeText(getApplicationContext(), getString(R.string.feature_not_supported_by_api, Integer.toString(Build.VERSION.SDK_INT)), Toast.LENGTH_SHORT).show(); // TODO: support api10 requires alternative to TimePicker
+ }
+ }
+
+ private DialogInterface.OnClickListener onOffsetChanged = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which)
+ {
+ FragmentManager fragments = getSupportFragmentManager();
+ AlarmOffsetDialog offsetDialog = (AlarmOffsetDialog) fragments.findFragmentByTag(DIALOGTAG_OFFSET + 1);
+ if (editor != null && offsetDialog != null)
+ {
+ AlarmClockItem item = editor.getItem();
+ item.offset = offsetDialog.getOffset();
+ AlarmNotifications.updateAlarmTime(AlarmEditActivity.this, item);
+ editor.notifyItemChanged();
+ editor.triggerPreviewOffset();
+ }
+ }
+ };
+
+ /**
+ * pickSolarEvent
+ */
+ protected void pickSolarEvent(@NonNull AlarmClockItem item)
+ {
+ final AlarmCreateDialog dialog = new AlarmCreateDialog();
+ dialog.loadSettings(AlarmEditActivity.this);
+ dialog.setAlarmType(item.type);
+ dialog.setDialogMode(item.event != null ? 0 : 1);
+ dialog.setEvent(item.event, item.location);
+ dialog.setAlarmTime(item.hour, item.minute, item.timezone);
+ dialog.setOffset(item.offset);
+ dialog.setOnAcceptedListener(onPickEventAccepted);
+ dialog.setOnNeutralListener(onPickEventCanceled);
+ dialog.setOnCanceledListener(onPickEventCanceled);
+ dialog.show(getSupportFragmentManager(), DIALOGTAG_EVENT);
+ }
+ private DialogInterface.OnClickListener onPickEventAccepted = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface d, int which)
+ {
+ FragmentManager fragments = getSupportFragmentManager();
+ AlarmCreateDialog dialog = (AlarmCreateDialog) fragments.findFragmentByTag(DIALOGTAG_EVENT);
+ if (editor != null && dialog != null)
+ {
+ AlarmClockItem item = editor.getItem();
+ AlarmCreateDialog.updateAlarmItem(dialog, item);
+ AlarmNotifications.updateAlarmTime(AlarmEditActivity.this, item);
+ editor.notifyItemChanged();
+ editor.triggerPreviewOffset();
+ invalidateOptionsMenu();
+ }
+ }
+ };
+ private DialogInterface.OnClickListener onPickEventCanceled = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface d, int which)
+ {
+ FragmentManager fragments = getSupportFragmentManager();
+ AlarmCreateDialog dialog = (AlarmCreateDialog) fragments.findFragmentByTag(DIALOGTAG_EVENT);
+ if (editor != null && dialog != null) {
+ dialog.dismiss();
+ }
+ }
+ };
+
+
+ /**
+ * pickLocation
+ */
+ protected void pickLocation(@NonNull AlarmClockItem item)
+ {
+ final LocationConfigDialog dialog = new LocationConfigDialog();
+ dialog.setHideTitle(true);
+ dialog.setHideMode(true);
+ dialog.setLocation(this, item.location);
+ dialog.setDialogListener(onLocationChanged);
+ dialog.show(getSupportFragmentManager(), DIALOGTAG_LOCATION + 1);
+ }
+ private LocationConfigDialog.LocationConfigDialogListener onLocationChanged = new LocationConfigDialog.LocationConfigDialogListener()
+ {
+ @Override
+ public boolean saveSettings(Context context, WidgetSettings.LocationMode locationMode, Location location)
+ {
+ FragmentManager fragments = getSupportFragmentManager();
+ if (editor != null)
+ {
+ AlarmClockItem item = editor.getItem();
+ item.location = location;
+ AlarmNotifications.updateAlarmTime(AlarmEditActivity.this, item);
+ editor.notifyItemChanged();
+ editor.triggerPreviewOffset();
+ invalidateOptionsMenu();
+ return true;
+ }
+ return false;
+ }
+ };
+
+ /**
+ * pickRepetition
+ */
+ protected void pickRepetition(@NonNull AlarmClockItem item)
+ {
+ AlarmRepeatDialog repeatDialog = new AlarmRepeatDialog();
+ repeatDialog.setColorOverrides(colorOn, colorOff, colorDisabled, colorPressed);
+ repeatDialog.setRepetition(item.repeating, item.repeatingDays);
+ repeatDialog.setOnAcceptedListener(onRepetitionChanged);
+ repeatDialog.show(getSupportFragmentManager(), DIALOGTAG_REPEAT + 1);
+ }
+ private DialogInterface.OnClickListener onRepetitionChanged = new DialogInterface.OnClickListener()
+ {
+ public void onClick(DialogInterface dialog, int whichButton)
+ {
+ FragmentManager fragments = getSupportFragmentManager();
+ AlarmRepeatDialog repeatDialog = (AlarmRepeatDialog) fragments.findFragmentByTag(DIALOGTAG_REPEAT + 1);
+
+ if (editor != null && repeatDialog != null)
+ {
+ AlarmClockItem item = editor.getItem();
+ item.repeating = repeatDialog.getRepetition();
+ item.repeatingDays = repeatDialog.getRepetitionDays();
+ AlarmNotifications.updateAlarmTime(AlarmEditActivity.this, item);
+ editor.notifyItemChanged();
+ }
+ }
+ };
+
+ /**
+ * pickAction
+ */
+ protected void pickAction(@NonNull final AlarmClockItem item, final int actionNum)
+ {
+ Intent intent = new Intent(AlarmEditActivity.this, ActionListActivity.class);
+ intent.putExtra(ActionListActivity.PARAM_NOSELECT, false);
+ intent.putExtra(ActionListActivity.PARAM_SELECTED, item.getActionID(actionNum));
+ startActivityForResult(intent, getActionRequestCode(actionNum));
+ }
+ protected void onActionResult(int resultCode, Intent data, int actionNum)
+ {
+ if (resultCode == RESULT_OK && editor != null && data != null)
+ {
+ AlarmClockItem item = editor.getItem();
+ String actionID = data.getStringExtra(ActionListActivity.SELECTED_ACTIONID);
+ item.setActionID(actionNum, actionID);
+ editor.notifyItemChanged();
+
+ } else {
+ Log.d(TAG, "onActivityResult: bad result: " + resultCode + ", " + data);
+ }
+ }
+ protected int getActionRequestCode(int actionNum) {
+ switch (actionNum) {
+ case 1: return REQUEST_ACTION1;
+ case 0: default: return REQUEST_ACTION0;
+ }
+ }
+
+ @Override
+ public void onTypeChanged(AlarmClockItem forItem)
+ {
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setTitle(forItem != null ? forItem.type.getDisplayString() : "");
+ }
+ }
+
+ @Override
+ public void onRequestLabel(AlarmClockItem forItem) {
+ pickLabel(forItem);
+ }
+
+ @Override
+ public void onRequestRingtone(AlarmClockItem forItem) {
+ pickRingtone(forItem);
+ }
+
+ @Override
+ public void onRequestSolarEvent(AlarmClockItem forItem) {
+ pickSolarEvent(forItem);
+ }
+
+ @Override
+ public void onRequestLocation(AlarmClockItem forItem) {
+ pickLocation(forItem);
+ }
+
+ @Override
+ public void onRequestTime(AlarmClockItem forItem) {
+ // TODO
+ }
+
+ @Override
+ public void onRequestOffset(AlarmClockItem forItem) {
+ pickOffset(forItem);
+ }
+
+ @Override
+ public void onRequestRepetition(AlarmClockItem forItem) {
+ pickRepetition(forItem);
+ }
+
+ @Override
+ public void onRequestAction(AlarmClockItem forItem, int actionNum) {
+ pickAction(forItem, actionNum);
+ }
+
+ @Override
+ public void onRequestDialog(AlarmClockItem forItem) { /* EMPTY */ }
+
+}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmEditDialog.java b/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmEditDialog.java
new file mode 100644
index 000000000..b5edb53c3
--- /dev/null
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmEditDialog.java
@@ -0,0 +1,607 @@
+/**
+ Copyright (C) 2020 Forrest Guice
+ This file is part of SuntimesWidget.
+
+ SuntimesWidget 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.
+
+ SuntimesWidget 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 SuntimesWidget. If not, see .
+*/
+package com.forrestguice.suntimeswidget.alarmclock.ui;
+
+import android.animation.Animator;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Vibrator;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.widget.PopupMenu;
+import android.transition.TransitionInflater;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.ImageButton;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.forrestguice.suntimeswidget.R;
+import com.forrestguice.suntimeswidget.SuntimesUtils;
+import com.forrestguice.suntimeswidget.alarmclock.AlarmClockItem;
+import com.forrestguice.suntimeswidget.alarmclock.AlarmNotifications;
+import com.forrestguice.suntimeswidget.settings.AppSettings;
+
+import java.util.Calendar;
+
+@SuppressWarnings("Convert2Diamond")
+public class AlarmEditDialog extends DialogFragment
+{
+ public static final String EXTRA_SHOW_FRAME = "show_frame";
+ public static final String EXTRA_SHOW_OVERFLOW = "show_overflow";
+
+ protected View dialogFrame;
+ protected TextView text_title;
+ protected AlarmClockItem item = null, original = null;
+ protected AlarmEditViewHolder itemView;
+
+ public AlarmEditDialog()
+ {
+ super();
+ setArguments(new Bundle());
+ }
+
+ public void initFromItem(AlarmClockItem item, boolean addItem)
+ {
+ this.original = (addItem ? null : item);
+ this.item = new AlarmClockItem(item);
+ this.item.modified = false;
+ bindItemToHolder(item);
+ }
+ public AlarmClockItem getItem() {
+ return item;
+ }
+ public AlarmClockItem getOriginal() {
+ return original;
+ }
+
+ public boolean isModified() {
+ return ((item != null && item.modified) || original == null);
+ }
+
+ public void notifyItemChanged() {
+ item.modified = true;
+ bindItemToHolder(item);
+ itemView.bindDataToPosition(getActivity(), item, 0);
+ }
+
+ protected void bindItemToHolder(AlarmClockItem item)
+ {
+ if (itemView != null)
+ {
+ detachClickListeners(itemView);
+ itemView.bindDataToPosition(getActivity(), item, 0);
+ itemView.menu_overflow.setVisibility(getArguments().getBoolean(EXTRA_SHOW_OVERFLOW, true) ? View.VISIBLE : View.GONE);
+ attachClickListeners(itemView, 0);
+ }
+ if (text_title != null) {
+ text_title.setText(item != null ? item.type.getDisplayString() : "");
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedState)
+ {
+ super.onCreate(savedState);
+ setStyle(DialogFragment.STYLE_NO_FRAME, R.style.AppTheme);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move));
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle savedState)
+ {
+ ContextThemeWrapper contextWrapper = new ContextThemeWrapper(getActivity(), AppSettings.loadTheme(getContext()));
+ View dialogContent = inflater.cloneInContext(contextWrapper).inflate(R.layout.layout_dialog_alarmitem, parent, false);
+
+ initViews(getContext(), dialogContent);
+ if (savedState != null) {
+ loadSettings(savedState);
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) // transition animation
+ {
+ if (item != null && itemView != null) {
+ itemView.text_datetime.setTransitionName("transition_" + item.rowID);
+ startPostponedEnterTransition();
+ }
+ }
+
+ return dialogContent;
+ }
+
+ protected void initViews(Context context, View dialogContent)
+ {
+ itemView = new AlarmEditViewHolder(dialogContent);
+ text_title = (TextView) dialogContent.findViewById(R.id.dialog_title);
+
+ ImageButton btn_cancel = (ImageButton) dialogContent.findViewById(R.id.dialog_button_cancel);
+ if (btn_cancel != null) {
+ btn_cancel.setOnClickListener(onDialogCancelClick);
+ }
+
+ ImageButton btn_accept = (ImageButton) dialogContent.findViewById(R.id.dialog_button_accept);
+ if (btn_accept != null) {
+ btn_accept.setOnClickListener(onDialogAcceptClick);
+ }
+
+ Button btn_neutral = (Button) dialogContent.findViewById(R.id.dialog_button_neutral);
+ if (btn_neutral != null) {
+ btn_neutral.setOnClickListener(onDialogNeutralClick);
+ }
+
+ dialogFrame = dialogContent.findViewById(R.id.dialog_frame);
+ setShowDialogFrame(getArguments().getBoolean(EXTRA_SHOW_FRAME, true));
+
+ bindItemToHolder(item);
+ }
+
+ public void setShowOverflow(boolean value)
+ {
+ getArguments().putBoolean(EXTRA_SHOW_OVERFLOW, value);
+ bindItemToHolder(getItem());
+ }
+
+ public void setShowDialogFrame(boolean value)
+ {
+ getArguments().putBoolean(EXTRA_SHOW_FRAME, value);
+ if (dialogFrame != null) {
+ dialogFrame.setVisibility(value ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ @SuppressWarnings({"deprecation","RestrictedApi"})
+ @NonNull @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState)
+ {
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.setOnShowListener(onDialogShow);
+ return dialog;
+ }
+
+ @Override
+ public void onSaveInstanceState( Bundle outState )
+ {
+ saveSettings(outState);
+ super.onSaveInstanceState(outState);
+ }
+
+ protected void loadSettings(Bundle bundle)
+ {
+ this.item = bundle.getParcelable("item");
+ this.original = bundle.getParcelable("original");
+ bindItemToHolder(item);
+ }
+
+ protected void saveSettings(Bundle bundle)
+ {
+ bundle.putParcelable("item", item);
+ bundle.putParcelable("original", original);
+ }
+
+ private DialogInterface.OnClickListener onAccepted = null;
+ public void setOnAcceptedListener( DialogInterface.OnClickListener listener ) {
+ onAccepted = listener;
+ }
+
+ private DialogInterface.OnClickListener onCanceled = null;
+ public void setOnCanceledListener( DialogInterface.OnClickListener listener ) {
+ onCanceled = listener;
+ }
+
+ @Override
+ public void onResume()
+ {
+ super.onResume();
+ }
+
+ private DialogInterface.OnShowListener onDialogShow = new DialogInterface.OnShowListener()
+ {
+ @Override
+ public void onShow(DialogInterface dialog) {
+ // EMPTY; placeholder
+ }
+ };
+
+ private View.OnClickListener onDialogNeutralClick = new View.OnClickListener()
+ {
+ @Override
+ public void onClick(View v)
+ {
+ // TODO: neutral click
+ }
+ };
+
+ private View.OnClickListener onDialogCancelClick = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Dialog dialog = getDialog();
+ if (dialog != null) {
+ dialog.cancel();
+ }
+ }
+ };
+
+ @Override
+ public void onCancel(DialogInterface dialog)
+ {
+ if (onCanceled != null) {
+ onCanceled.onClick(getDialog(), 0);
+ }
+ }
+
+ private View.OnClickListener onDialogAcceptClick = new View.OnClickListener()
+ {
+ @Override
+ public void onClick(View v)
+ {
+ if (onAccepted != null) {
+ onAccepted.onClick(getDialog(), 0);
+ }
+ dismiss();
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ protected void showAlarmTypeMenu(Context context, final AlarmClockItem item, final View buttonView)
+ {
+ PopupMenu menu = new PopupMenu(context, buttonView);
+ MenuInflater inflater = menu.getMenuInflater();
+ inflater.inflate(R.menu.alarmtype, menu.getMenu());
+
+ menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener()
+ {
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem)
+ {
+ switch (menuItem.getItemId())
+ {
+ case R.id.alarmTypeNotification:
+ item.type = AlarmClockItem.AlarmType.NOTIFICATION;
+ if (listener != null) {
+ listener.onTypeChanged(item);
+ }
+ notifyItemChanged();
+ return true;
+
+ case R.id.alarmTypeAlarm:
+ default:
+ item.type = AlarmClockItem.AlarmType.ALARM;
+ if (listener != null) {
+ listener.onTypeChanged(item);
+ }
+ notifyItemChanged();
+ return true;
+ }
+ }
+ });
+
+ SuntimesUtils.forceActionBarIcons(menu.getMenu());
+ menu.show();
+ }
+
+ protected void showOverflowMenu(final Context context, final AlarmClockItem item, final View buttonView)
+ {
+ PopupMenu menu = new PopupMenu(context, buttonView);
+ MenuInflater inflater = menu.getMenuInflater();
+ inflater.inflate(R.menu.alarmcontext1, menu.getMenu());
+
+ if (Build.VERSION.SDK_INT < 11) // TODO: add support for api10
+ {
+ MenuItem[] notSupportedMenuItems = new MenuItem[] { // not supported by api level
+ menu.getMenu().findItem(R.id.setAlarmTime),
+ menu.getMenu().findItem(R.id.setAlarmOffset)
+ };
+ for (MenuItem menuItem : notSupportedMenuItems) {
+ menuItem.setEnabled(false);
+ }
+ }
+
+ menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener()
+ {
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem)
+ {
+ switch (menuItem.getItemId())
+ {
+ case R.id.deleteAlarm:
+ confirmDeleteAlarm(getActivity(), item, onDeleteConfirmed(item));
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ });
+
+ SuntimesUtils.forceActionBarIcons(menu.getMenu());
+ menu.show();
+ }
+
+ public static void confirmDeleteAlarm(final Context context, final AlarmClockItem item, DialogInterface.OnClickListener onDeleteConfirmed)
+ {
+ int[] attrs = { R.attr.icActionDelete };
+ TypedArray a = context.obtainStyledAttributes(attrs);
+ int iconResID = a.getResourceId(0, R.drawable.ic_action_discard);
+ a.recycle();
+
+ String message = context.getString(R.string.deletealarm_dialog_message, AlarmEditViewHolder.displayAlarmLabel(context, item), AlarmEditViewHolder.displayAlarmTime(context, item), AlarmEditViewHolder.displayEvent(context, item));
+ AlertDialog.Builder confirm = new AlertDialog.Builder(context)
+ .setTitle(context.getString(R.string.deletealarm_dialog_title)).setMessage(message).setIcon(iconResID)
+ .setPositiveButton(context.getString(R.string.deletealarm_dialog_ok), onDeleteConfirmed)
+ .setNegativeButton(context.getString(R.string.deletealarm_dialog_cancel), null);
+ confirm.show();
+ }
+
+ protected DialogInterface.OnClickListener onDeleteConfirmed( final AlarmClockItem item ) {
+ return new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ getActivity().sendBroadcast(AlarmNotifications.getAlarmIntent(getActivity(), AlarmNotifications.ACTION_DELETE, item.getUri()));
+ dialog.cancel();
+ }
+ };
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private void attachClickListeners(@NonNull AlarmEditViewHolder holder, int position)
+ {
+ holder.menu_type.setOnClickListener(showAlarmTypeMenu());
+ holder.menu_overflow.setOnClickListener(showOverflowMenu());
+ holder.edit_label.setOnClickListener(pickLabel());
+ holder.chip_offset.setOnClickListener(pickOffset());
+ holder.chip_event.setOnClickListener(pickEvent());
+ holder.chip_location.setOnClickListener(pickLocation());
+ holder.chip_repeat.setOnClickListener(pickRepeating());
+ holder.chip_ringtone.setOnClickListener(pickRingtone());
+ holder.check_vibrate.setOnCheckedChangeListener(pickVibrating());
+ holder.chip_action0.setOnClickListener(pickAction(0));
+ holder.chip_action1.setOnClickListener(pickAction(1));
+ holder.layout_datetime.setOnClickListener(triggerPreviewOffsetListener(holder));
+ }
+
+ private void detachClickListeners(@NonNull AlarmEditViewHolder holder) {
+ holder.detachClickListeners();
+ }
+
+ protected AlarmItemAdapterListener listener;
+ public void setAlarmClockAdapterListener( AlarmItemAdapterListener listener ) {
+ this.listener = listener;
+ }
+
+ private View.OnClickListener showAlarmTypeMenu()
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showAlarmTypeMenu(getActivity(), item, v);
+ }
+ };
+ }
+
+ private View.OnClickListener showOverflowMenu()
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showOverflowMenu(getActivity(), item, v);
+ }
+ };
+ }
+
+ private View.OnClickListener pickLabel()
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (listener != null) {
+ listener.onRequestLabel(item);
+ }
+ }
+ };
+ }
+
+ private View.OnClickListener pickOffset()
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (listener != null) {
+ listener.onRequestOffset(item);
+ }
+ }
+ };
+ }
+
+ private View.OnClickListener pickEvent()
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (listener != null) {
+ listener.onRequestSolarEvent(item);
+ }
+ }
+ };
+ }
+
+ private View.OnClickListener pickLocation()
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (listener != null) {
+ listener.onRequestLocation(item);
+ }
+ }
+ };
+ }
+
+ private View.OnClickListener pickRepeating()
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (listener != null) {
+ listener.onRequestRepetition(item);
+ }
+ }
+ };
+ }
+
+ private View.OnClickListener pickRingtone()
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (listener != null) {
+ listener.onRequestRingtone(item);
+ }
+ }
+ };
+ }
+
+ public void triggerPreviewOffset() {
+ triggerPreviewOffset(itemView);
+ }
+
+ protected void triggerPreviewOffset(AlarmEditViewHolder holder)
+ {
+ if (!holder.preview_offset && item.offset != 0) {
+ holder.preview_offset = true;
+ animatePreviewOffset(holder, true);
+ }
+ }
+
+ private View.OnClickListener triggerPreviewOffsetListener(final AlarmEditViewHolder holder)
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ triggerPreviewOffset(holder);
+ }
+ };
+ }
+ protected void animatePreviewOffset(final AlarmEditViewHolder holder, final boolean enable)
+ {
+ if (holder == null || getActivity() == null || !isAdded()) {
+ return;
+ }
+ boolean isSchedulable = AlarmNotifications.updateAlarmTime(getActivity(), item, Calendar.getInstance(), false);
+
+ if (holder.text_datetime != null) {
+ holder.text_datetime.setText(isSchedulable ? AlarmEditViewHolder.displayAlarmTime(getActivity(), item, enable) : "");
+ }
+ if (holder.text_date != null) {
+ holder.text_date.setText(isSchedulable ? AlarmEditViewHolder.displayAlarmDate(getActivity(), item, enable): "");
+ }
+
+ if (holder.text_datetime_offset != null)
+ {
+ if (Build.VERSION.SDK_INT >= 14)
+ {
+ if (!enable) {
+ holder.text_datetime_offset.setAlpha(0.0f);
+ holder.text_datetime_offset.setVisibility(View.VISIBLE);
+ }
+
+ holder.icon_datetime_offset.setVisibility(enable ? View.VISIBLE : View.INVISIBLE);
+ holder.text_datetime_offset.animate().translationY((enable ? 2 * holder.text_datetime_offset.getHeight() : 0))
+ .alpha(enable ? 0.0f : 1.0f).setListener(new Animator.AnimatorListener() {
+ public void onAnimationCancel(Animator animation) {}
+ public void onAnimationRepeat(Animator animation) {}
+ public void onAnimationStart(Animator animation) {}
+ public void onAnimationEnd(Animator animation) {
+ onAnimatePreviewOffsetEnd(holder, enable);
+ }
+ });
+
+ } else {
+ onAnimatePreviewOffsetEnd(holder, enable);
+ }
+ }
+ }
+ public static final int PREVIEW_OFFSET_DURATION_MILLIS = 1500;
+
+ protected void onAnimatePreviewOffsetEnd(final AlarmEditViewHolder holder, final boolean enable)
+ {
+ holder.text_datetime_offset.setVisibility(enable ? View.INVISIBLE : View.VISIBLE);
+ if (enable)
+ {
+ holder.text_datetime_offset.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ holder.preview_offset = false;
+ animatePreviewOffset(holder,false);
+ }
+ }, PREVIEW_OFFSET_DURATION_MILLIS);
+ }
+ }
+
+ private CompoundButton.OnCheckedChangeListener pickVibrating()
+ {
+ return new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
+ {
+ if (isChecked && !item.vibrate)
+ {
+ Context context = getActivity();
+ Vibrator vibrate = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+ if (vibrate != null) {
+ vibrate.vibrate(500);
+ }
+ }
+ item.vibrate = isChecked;
+ item.modified = true;
+ }
+ };
+ }
+
+ private View.OnClickListener pickAction(final int actionNum)
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (listener != null) {
+ listener.onRequestAction(item, actionNum);
+ }
+ }
+ };
+ }
+
+}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmEditViewHolder.java b/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmEditViewHolder.java
new file mode 100644
index 000000000..96cb6447a
--- /dev/null
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmEditViewHolder.java
@@ -0,0 +1,422 @@
+/**
+ Copyright (C) 2020 Forrest Guice
+ This file is part of SuntimesWidget.
+
+ SuntimesWidget 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.
+
+ SuntimesWidget 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 SuntimesWidget. If not, see .
+*/
+package com.forrestguice.suntimeswidget.alarmclock.ui;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.ViewCompat;
+import android.support.v7.widget.RecyclerView;
+import android.text.SpannableString;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextSwitcher;
+import android.widget.TextView;
+
+import com.forrestguice.suntimeswidget.R;
+import com.forrestguice.suntimeswidget.SuntimesUtils;
+import com.forrestguice.suntimeswidget.alarmclock.AlarmClockItem;
+import com.forrestguice.suntimeswidget.alarmclock.AlarmNotifications;
+import com.forrestguice.suntimeswidget.settings.SolarEventIcons;
+import com.forrestguice.suntimeswidget.settings.SolarEvents;
+import com.forrestguice.suntimeswidget.settings.WidgetActions;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+@SuppressWarnings("Convert2Diamond")
+public class AlarmEditViewHolder extends RecyclerView.ViewHolder
+{
+ public static SuntimesUtils utils = new SuntimesUtils();
+
+ public int position = RecyclerView.NO_POSITION;
+ public boolean selected = true;
+ public boolean preview_offset = false;
+
+ public View layout_datetime;
+ public ImageView icon_datetime_offset;
+ public TextView text_datetime_offset;
+ public TextSwitcher text_datetime;
+ public TextView text_date;
+ public TextView text_note;
+
+ public ImageButton menu_type, menu_overflow;
+ public EditText edit_label;
+
+ public View chip_offset;
+ public TextView text_offset;
+
+ public View chip_event;
+ public TextView text_event;
+
+ public View chip_location;
+ public TextView text_location;
+
+ public View chip_repeat;
+ public TextView text_repeat;
+
+ public View chip_ringtone;
+ public TextView text_ringtone;
+
+ public View chip_vibrate;
+ public CheckBox check_vibrate;
+
+ public View chip_action0;
+ public TextView text_action0;
+
+ public View chip_action1;
+ public TextView text_action1;
+
+ public int res_icAlarm, res_icNotification;
+ public int res_icSoundOn, res_icSoundOff;
+ public int res_colorEnabled;
+ public int res_icOffset;
+
+ public AlarmEditViewHolder(View parent)
+ {
+ super(parent);
+ Context context = parent.getContext();
+ SuntimesUtils.initDisplayStrings(context);
+
+ layout_datetime = parent.findViewById(R.id.layout_datetime);
+ icon_datetime_offset = (ImageView) parent.findViewById(R.id.icon_datetime_offset);
+ text_datetime_offset = (TextView) parent.findViewById(R.id.text_datetime_offset);
+ text_datetime = (TextSwitcher) parent.findViewById(R.id.text_datetime);
+ text_date = (TextView) parent.findViewById(R.id.text_date);
+ text_note = (TextView) parent.findViewById(R.id.text_note);
+
+ menu_type = (ImageButton) parent.findViewById(R.id.type_menu);
+ menu_overflow = (ImageButton) parent.findViewById(R.id.overflow_menu);
+ edit_label = (EditText) parent.findViewById(R.id.edit_label);
+
+ chip_offset = parent.findViewById(R.id.chip_offset);
+ text_offset = (TextView) parent.findViewById(R.id.text_offset);
+
+ chip_event = parent.findViewById(R.id.chip_event);
+ text_event = (TextView) parent.findViewById(R.id.text_event);
+
+ chip_location = parent.findViewById(R.id.chip_location);
+ text_location = (TextView) parent.findViewById(R.id.text_location);
+
+ chip_repeat = parent.findViewById(R.id.chip_repeat);
+ text_repeat = (TextView) parent.findViewById(R.id.text_repeat);
+
+ chip_ringtone = parent.findViewById(R.id.chip_ringtone);
+ text_ringtone = (TextView) parent.findViewById(R.id.text_ringtone);
+
+ chip_vibrate = parent.findViewById(R.id.chip_vibrate);
+ check_vibrate = (CheckBox) parent.findViewById(R.id.check_vibrate);
+
+ chip_action0 = parent.findViewById(R.id.chip_action0);
+ text_action0 = (TextView) parent.findViewById(R.id.text_action0);
+
+ chip_action1 = parent.findViewById(R.id.chip_action1);
+ text_action1 = (TextView) parent.findViewById(R.id.text_action1);
+
+ themeHolder(context);
+ }
+
+ @SuppressLint("ResourceType")
+ public void themeHolder(Context context)
+ {
+ int[] attrs = { R.attr.icActionAlarm, R.attr.icActionNotification, R.attr.icActionSoundEnabled, R.attr.icActionSoundDisabled, R.attr.alarmColorEnabled, R.attr.icActionTimeReset };
+ TypedArray a = context.obtainStyledAttributes(attrs);
+ res_icAlarm = a.getResourceId(0, R.drawable.ic_action_extension);
+ res_icNotification = a.getResourceId(1, R.drawable.ic_action_notification);
+ res_icSoundOn = a.getResourceId(2, R.drawable.ic_action_soundenabled);
+ res_icSoundOff = a.getResourceId(3, R.drawable.ic_action_sounddisabled);
+ res_colorEnabled = a.getResourceId(4, R.color.alarm_enabled_dark);
+ res_icOffset = a.getResourceId(5, R.drawable.ic_action_timereset);;
+ a.recycle();
+ }
+
+ public void bindDataToPosition(Context context, AlarmClockItem item, int position)
+ {
+ this.position = position;
+
+ if (item != null)
+ {
+ boolean isSchedulable = AlarmNotifications.updateAlarmTime(context, item, Calendar.getInstance(), false);
+ float iconSize = context.getResources().getDimension(R.dimen.eventIcon_width);
+
+ menu_type.setImageDrawable(ContextCompat.getDrawable(context, (item.type == AlarmClockItem.AlarmType.ALARM ? res_icAlarm : res_icNotification)));
+ menu_type.setContentDescription(item.type.getDisplayString());
+
+ edit_label.setText(item.getLabel(context));
+
+ text_offset.setText(displayOffset(context, item));
+
+ if (item.offset != 0)
+ {
+ int iconMargin = (int)context.getResources().getDimension(R.dimen.eventIcon_margin1);
+ Drawable offsetIcon = ContextCompat.getDrawable(context, res_icOffset).mutate();
+ offsetIcon = new InsetDrawable(offsetIcon, iconMargin, iconMargin, iconMargin, iconMargin);
+ offsetIcon.setBounds(0, 0, (int)iconSize, (int)iconSize);
+ text_offset.setCompoundDrawablePadding(iconMargin);
+ text_offset.setCompoundDrawables(offsetIcon, null, null, null);
+
+ } else {
+ text_offset.setCompoundDrawables(null, null, null, null);
+ }
+
+ text_location.setText(item.location.getLabel());
+ text_repeat.setText( displayRepeating(context, item, selected));
+
+ text_event.setText(displayEvent(context, item));
+
+ if (item.event != null)
+ {
+ Drawable eventIcon = SolarEventIcons.getIconDrawable(context, item.event, (int)iconSize, (int)iconSize);
+ text_event.setCompoundDrawablePadding(SolarEventIcons.getIconDrawablePadding(context, item.event));
+ text_event.setCompoundDrawables(eventIcon, null, null, null);
+
+ } else {
+ Drawable eventIcon = SolarEventIcons.getIconDrawable(context, item.timezone, (int)iconSize, (int)iconSize);
+ text_event.setCompoundDrawablePadding(SolarEventIcons.getIconDrawablePadding(context, item.timezone));
+ text_event.setCompoundDrawables(eventIcon, null, null, null);
+ }
+
+ Drawable ringtoneIcon = ContextCompat.getDrawable(context, (item.ringtoneName != null ? res_icSoundOn : res_icSoundOff));
+ text_ringtone.setCompoundDrawablesWithIntrinsicBounds(ringtoneIcon, null, null, null);
+ text_ringtone.setText( displayRingtone(context, item, selected) );
+
+ check_vibrate.setChecked(item.vibrate);
+ text_action0.setText(displayAction(context, item, 0));
+ text_action1.setText(displayAction(context, item, 1));
+
+ text_datetime_offset.setText(isSchedulable ? text_offset.getText() : "");
+ text_datetime_offset.setVisibility(preview_offset ? View.INVISIBLE : View.VISIBLE);
+ icon_datetime_offset.setVisibility(preview_offset ? View.VISIBLE : View.INVISIBLE);
+ icon_datetime_offset.setContentDescription(context.getString( item.offset < 0 ? R.string.offset_button_before : R.string.offset_button_after));
+
+ text_datetime.setText(isSchedulable ? displayAlarmTime(context, item, preview_offset) : "");
+ ViewCompat.setTransitionName(text_datetime, "transition_" + item.rowID);
+
+ text_date.setText(isSchedulable ? displayAlarmDate(context, item, preview_offset) : "");
+ text_date.setVisibility(isSchedulable && AlarmEditViewHolder.showAlarmDate(context, item) ? View.VISIBLE : View.GONE);
+
+ text_note.setText(AlarmEditViewHolder.displayAlarmNote(context, item, isSchedulable));
+
+
+ /*if (item.enabled) {
+ TextView v = (TextView)text_datetime.getCurrentView();
+ v.setTextColor(ContextCompat.getColor(context, res_colorEnabled));
+ v = (TextView)text_datetime.getNextView();
+ v.setTextColor(ContextCompat.getColor(context, res_colorEnabled));
+ }*/
+
+ } else {
+ text_datetime_offset.setText("");
+ text_datetime.setText("");
+ ViewCompat.setTransitionName(text_datetime, null);
+ text_date.setText("");
+ text_note.setText("");
+ edit_label.setText("");
+ text_offset.setText("");
+ text_event.setText("");
+ text_location.setText("");
+ text_repeat.setText("");
+ text_repeat.setText("");
+ check_vibrate.setChecked(false);
+ text_action0.setText("");
+ text_action1.setText("");
+ }
+ }
+
+ public void detachClickListeners()
+ {
+ layout_datetime.setOnClickListener(null);
+ menu_type.setOnClickListener(null);
+ menu_overflow.setOnClickListener(null);
+ edit_label.setOnClickListener(null);
+ chip_offset.setOnClickListener(null);
+ chip_offset.setOnClickListener(null);
+ chip_event.setOnClickListener(null);
+ chip_location.setOnClickListener(null);
+ chip_repeat.setOnClickListener(null);
+ chip_ringtone.setOnClickListener(null);
+ check_vibrate.setOnClickListener(null);
+ check_vibrate.setOnCheckedChangeListener(null);
+ chip_action0.setOnClickListener(null);
+ chip_action1.setOnClickListener(null);
+ }
+
+ public static CharSequence displayAlarmLabel(Context context, AlarmClockItem item)
+ {
+ String emptyLabel = ((item.type == AlarmClockItem.AlarmType.ALARM) ? context.getString(R.string.alarmMode_alarm) : context.getString(R.string.alarmMode_notification));
+ return (item.label == null || item.label.isEmpty()) ? emptyLabel : item.label;
+ }
+
+ public static CharSequence displayAlarmTime(Context context, AlarmClockItem item) {
+ return displayAlarmTime(context, item, false);
+ }
+ public static CharSequence displayAlarmTime(Context context, AlarmClockItem item, boolean withOffset)
+ {
+ Calendar alarmTime = Calendar.getInstance(TimeZone.getDefault());
+ alarmTime.setTimeInMillis(item.timestamp + (withOffset ? item.offset : 0));
+
+ CharSequence alarmDesc;
+ SuntimesUtils utils = new SuntimesUtils();
+ SuntimesUtils.initDisplayStrings(context);
+ SuntimesUtils.TimeDisplayText timeText = utils.calendarTimeShortDisplayString(context, alarmTime, false);
+ if (SuntimesUtils.is24()) {
+ alarmDesc = timeText.getValue();
+
+ } else {
+ String timeString = timeText.getValue() + " " + timeText.getSuffix();
+ alarmDesc = SuntimesUtils.createRelativeSpan(null, timeString, " " + timeText.getSuffix(), 0.40f);
+ }
+ return alarmDesc;
+ }
+
+ public static CharSequence displayAlarmNote(Context context, AlarmClockItem item, boolean isSchedulable)
+ {
+ if (isSchedulable)
+ {
+ int[] attrs = { android.R.attr.textColorPrimary }; // TODO: from SuntimesTheme
+ TypedArray a = context.obtainStyledAttributes(attrs);
+ int noteColor = ContextCompat.getColor(context, a.getResourceId(0, R.color.text_accent_dark));
+ a.recycle();
+
+ String timeString = " " + utils.timeDeltaLongDisplayString(System.currentTimeMillis(), item.timestamp + item.offset).getValue() + " ";
+ String displayString = context.getString(R.string.schedalarm_dialog_note1, timeString);
+ return SuntimesUtils.createBoldColorSpan(null, displayString, timeString, noteColor);
+
+ } else if (item.event != null) {
+ String eventString = item.event.getLongDisplayString();
+ String displayString = context.getString(R.string.schedalarm_dialog_note2, eventString);
+ return SuntimesUtils.createBoldSpan(null, displayString, eventString);
+
+ } else {
+ return "";
+ }
+ }
+
+ public static CharSequence displayAlarmDate(Context context, AlarmClockItem item) {
+ return displayAlarmDate(context, item, false);
+ }
+ public static CharSequence displayAlarmDate(Context context, AlarmClockItem item, boolean withOffset)
+ {
+ Calendar alarmTime = Calendar.getInstance();
+ alarmTime.setTimeInMillis(item.timestamp + (withOffset ? item.offset : 0));
+
+ CharSequence alarmDesc;
+ SuntimesUtils.TimeDisplayText timeText = utils.calendarDateDisplayString(context, alarmTime, true);
+ if (SuntimesUtils.is24()) {
+ alarmDesc = timeText.getValue();
+
+ } else {
+ String timeString = timeText.getValue() + " " + timeText.getSuffix();
+ alarmDesc = SuntimesUtils.createRelativeSpan(null, timeString, " " + timeText.getSuffix(), 0.40f);
+ }
+ return alarmDesc;
+ }
+
+ public static boolean showAlarmDate(Context context, AlarmClockItem item)
+ {
+ int eventType = item.event == null ? -1 : item.event.getType();
+ long now = Calendar.getInstance(TimeZone.getDefault()).getTimeInMillis();
+ long delta = item.timestamp - now;
+ boolean isDistant = (delta >= (48 * 60 * 60 * 1000));
+ return (eventType == SolarEvents.TYPE_MOONPHASE || eventType == SolarEvents.TYPE_SEASON || isDistant);
+ }
+
+ public static CharSequence displayOffset(Context context, AlarmClockItem item)
+ {
+ Calendar alarmTime = Calendar.getInstance();
+ alarmTime.setTimeInMillis(item.timestamp);
+ int alarmHour = SuntimesUtils.is24() ? alarmTime.get(Calendar.HOUR_OF_DAY) : alarmTime.get(Calendar.HOUR);
+
+ if (item.offset == 0) {
+ return context.getResources().getQuantityString(R.plurals.offset_at_plural, alarmHour);
+
+ } else {
+ boolean isBefore = (item.offset <= 0);
+ String offsetText = utils.timeDeltaLongDisplayString(0, item.offset).getValue();
+ String offsetDisplay = context.getResources().getQuantityString((isBefore ? R.plurals.offset_before_plural : R.plurals.offset_after_plural), alarmHour, offsetText);
+ return SuntimesUtils.createBoldSpan(null, offsetDisplay, offsetText);
+ }
+ }
+
+ public static CharSequence displayRepeating(Context context, AlarmClockItem item, boolean isSelected)
+ {
+ int eventType = item.event == null ? -1 : item.event.getType();
+ boolean noRepeat = item.repeatingDays == null || item.repeatingDays.isEmpty();
+ String repeatText = AlarmClockItem.repeatsEveryDay(item.repeatingDays)
+ ? context.getString(R.string.alarmOption_repeat_all)
+ : noRepeat
+ ? context.getString(R.string.alarmOption_repeat_none)
+ : AlarmRepeatDialog.getDisplayString(context, item.repeatingDays);
+ if (item.repeating && (eventType == SolarEvents.TYPE_MOONPHASE || eventType == SolarEvents.TYPE_SEASON)) {
+ repeatText = context.getString(R.string.alarmOption_repeat);
+ }
+ return (isSelected || !noRepeat ? repeatText : "");
+ }
+
+ public static CharSequence displayRingtone(Context context, AlarmClockItem item, boolean isSelected)
+ {
+ final String noRingtone = context.getString(R.string.alarmOption_ringtone_none);
+ return (isSelected ? (item.ringtoneName != null ? item.ringtoneName : noRingtone) : "");
+ }
+
+ public static CharSequence displayAction(Context context, AlarmClockItem item, int actionNum)
+ {
+ String noAction = context.getString(R.string.configLabel_action_item_none);
+ String actionID = item.getActionID(actionNum);
+ String actionTitle = WidgetActions.loadActionLaunchPref(context, 0, actionID, WidgetActions.PREF_KEY_ACTION_LAUNCH_TITLE);
+ String actionDesc = WidgetActions.loadActionLaunchPref(context, 0, actionID, WidgetActions.PREF_KEY_ACTION_LAUNCH_DESC);
+ String desc = context.getString(R.string.configLabel_action_item_desc, actionDesc);
+ String label = context.getString(R.string.configLabel_action_item, actionTitle, desc);
+
+ int[] attrs = { R.attr.text_disabledColor };
+ TypedArray a = context.obtainStyledAttributes(attrs);
+ int descColor = ContextCompat.getColor(context, a.getResourceId(0, R.color.text_disabled_dark));
+ a.recycle();
+
+ SpannableString s = SuntimesUtils.createRelativeSpan(null, label, desc, 0.75f);
+ s = SuntimesUtils.createColorSpan(s, label, desc, descColor);
+ return ((actionID != null) ? s : noAction);
+ }
+
+ public static CharSequence displayEvent(Context context, AlarmClockItem item)
+ {
+ if (item.event != null) {
+ return item.event.getLongDisplayString();
+
+ } else if (item.timezone != null) {
+ Calendar adjustedTime = Calendar.getInstance(AlarmClockItem.AlarmTimeZone.getTimeZone(item.timezone, item.location));
+ adjustedTime.set(Calendar.HOUR_OF_DAY, item.hour);
+ adjustedTime.set(Calendar.MINUTE, item.minute);
+ return utils.calendarTimeShortDisplayString(context, adjustedTime) + "\n" + AlarmClockItem.AlarmTimeZone.displayString(item.timezone);
+
+ } else {
+ Calendar adjustedTime = Calendar.getInstance(AlarmClockItem.AlarmTimeZone.getTimeZone(item.timezone, item.location));
+ adjustedTime.set(Calendar.HOUR_OF_DAY, item.hour);
+ adjustedTime.set(Calendar.MINUTE, item.minute);
+ return utils.calendarTimeShortDisplayString(context, adjustedTime) + "\n" + context.getString(R.string.alarmOption_solarevent_none);
+ }
+ }
+
+
+}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmItemAdapterListener.java b/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmItemAdapterListener.java
new file mode 100644
index 000000000..5316205f9
--- /dev/null
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmItemAdapterListener.java
@@ -0,0 +1,38 @@
+/**
+ Copyright (C) 2018-2020 Forrest Guice
+ This file is part of SuntimesWidget.
+
+ SuntimesWidget 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.
+
+ SuntimesWidget 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 SuntimesWidget. If not, see .
+*/
+
+package com.forrestguice.suntimeswidget.alarmclock.ui;
+
+import com.forrestguice.suntimeswidget.alarmclock.AlarmClockItem;
+
+/**
+ * AlarmClockAdapterListener
+ */
+public interface AlarmItemAdapterListener
+{
+ void onTypeChanged(AlarmClockItem forItem);
+ void onRequestLabel(AlarmClockItem forItem);
+ void onRequestRingtone(AlarmClockItem forItem);
+ void onRequestSolarEvent(AlarmClockItem forItem);
+ void onRequestLocation(AlarmClockItem forItem);
+ void onRequestTime(AlarmClockItem forItem);
+ void onRequestOffset(AlarmClockItem forItem);
+ void onRequestRepetition(AlarmClockItem forItem);
+ void onRequestAction(AlarmClockItem forItem, int actionNum);
+ void onRequestDialog(AlarmClockItem forItem);
+}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmClockAdapter.java b/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmItemArrayAdapter.java
similarity index 53%
rename from app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmClockAdapter.java
rename to app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmItemArrayAdapter.java
index e1bbf2e07..fe150fa1e 100644
--- a/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmClockAdapter.java
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmItemArrayAdapter.java
@@ -27,8 +27,6 @@
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import android.os.Vibrator;
import android.support.annotation.NonNull;
@@ -40,7 +38,6 @@
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.SwitchCompat;
import android.text.Spannable;
-import android.text.SpannableStringBuilder;
import android.text.style.ImageSpan;
import android.util.Log;
import android.util.TypedValue;
@@ -76,36 +73,49 @@
* AlarmClockAdapter
*/
@SuppressWarnings("Convert2Diamond")
-public class AlarmClockAdapter extends ArrayAdapter
+public class AlarmItemArrayAdapter extends ArrayAdapter
{
private static final SuntimesUtils utils = new SuntimesUtils();
private Context context;
private long selectedItem;
private ArrayList items;
- private int iconAlarm, iconNotification, iconSoundEnabled, iconSoundDisabled;
+ private int iconAlarm, iconNotification, iconSoundEnabled, iconSoundDisabled, iconAction;
private Drawable alarmEnabledBG, alarmDisabledBG;
private int alarmSelectedColor, alarmEnabledColor;
private int onColor, offColor, disabledColor, pressedColor;
private SuntimesTheme suntimesTheme = null;
- public AlarmClockAdapter(Context context)
+ protected int resource = R.layout.layout_listitem_alarmclock;
+
+ public AlarmItemArrayAdapter(Context context)
{
super(context, R.layout.layout_listitem_alarmclock);
+ this.resource = resource;
+ initAdapter(context);
+ this.items = new ArrayList<>();
+ }
+
+ public AlarmItemArrayAdapter(Context context, int resource)
+ {
+ super(context, resource);
+ this.resource = resource;
initAdapter(context);
this.items = new ArrayList<>();
}
- public AlarmClockAdapter(Context context, ArrayList items)
+ public AlarmItemArrayAdapter(Context context, int resource, ArrayList items)
{
- super(context, R.layout.layout_listitem_alarmclock, items);
+ super(context, resource, items);
+ this.resource = resource;
initAdapter(context);
this.items = items;
}
- public AlarmClockAdapter(Context context, ArrayList items, SuntimesTheme theme)
+ public AlarmItemArrayAdapter(Context context, int resource, ArrayList items, SuntimesTheme theme)
{
- super(context, R.layout.layout_listitem_alarmclock, items);
+ super(context, resource, items);
+ this.resource = resource;
suntimesTheme = theme;
initAdapter(context);
this.items = items;
@@ -126,7 +136,8 @@ private void themeAdapterViews()
{
int[] attrs = { R.attr.alarmCardEnabled, R.attr.alarmCardDisabled,
R.attr.icActionAlarm, R.attr.icActionNotification, R.attr.icActionSoundEnabled, R.attr.icActionSoundDisabled,
- android.R.attr.textColorPrimary, android.R.attr.textColor, R.attr.text_disabledColor, R.attr.gridItemSelected, R.attr.buttonPressColor, R.attr.alarmColorEnabled};
+ android.R.attr.textColorPrimary, android.R.attr.textColor, R.attr.text_disabledColor, R.attr.gridItemSelected, R.attr.buttonPressColor, R.attr.alarmColorEnabled,
+ R.attr.icActionExtension };
TypedArray a = context.obtainStyledAttributes(attrs);
alarmEnabledBG = ContextCompat.getDrawable(context, a.getResourceId(0, R.drawable.card_alarmitem_enabled_dark));
alarmDisabledBG = ContextCompat.getDrawable(context, a.getResourceId(1, R.drawable.card_alarmitem_disabled_dark));
@@ -140,6 +151,7 @@ private void themeAdapterViews()
alarmSelectedColor = ContextCompat.getColor(context, a.getResourceId(9, R.color.grid_selected_dark));
pressedColor = ContextCompat.getColor(context, a.getResourceId(10, R.color.sunIcon_color_rising_dark));
alarmEnabledColor = ContextCompat.getColor(context, a.getResourceId(11, R.color.alarm_enabled_dark));
+ iconAction = a.getResourceId(12, R.drawable.ic_action_extension);
a.recycle();
}
@@ -216,7 +228,7 @@ private View itemView(int position, View convertView, @NonNull final ViewGroup p
{
if (convertView == null) {
LayoutInflater inflater = LayoutInflater.from(context);
- convertView = inflater.inflate(R.layout.layout_listitem_alarmclock, parent, false);
+ convertView = inflater.inflate(resource, parent, false);
}
AlarmClockItemView itemView = new AlarmClockItemView(convertView);
@@ -239,105 +251,123 @@ private void setListeners( final AlarmClockItemView view, final AlarmClockItem i
final boolean isSelected = (item.rowID == selectedItem);
// type button
- view.typeButton.setOnClickListener(new View.OnClickListener()
+ if (view.typeButton != null)
{
- @Override
- public void onClick(View v)
+ view.typeButton.setOnClickListener(new View.OnClickListener()
{
- if (isSelected)
+ @Override
+ public void onClick(View v)
{
- if (item.enabled)
- AlarmNotifications.showTimeUntilToast(context, v, item);
- else showAlarmTypeMenu(item, view.typeButton, view.card);
+ if (isSelected)
+ {
+ if (item.enabled)
+ AlarmNotifications.showTimeUntilToast(context, v, item);
+ else showAlarmTypeMenu(item, view.typeButton, view.card);
- } else {
- setSelectedItem(item.rowID);
+ } else {
+ setSelectedItem(item.rowID);
+ }
}
- }
- });
+ });
+ }
// label
- view.text.setOnClickListener(new View.OnClickListener()
+ if (view.text_label != null)
{
- @Override
- public void onClick(View v)
+ view.text_label.setOnClickListener(new View.OnClickListener()
{
- if (isSelected) {
- if (adapterListener != null) {
- adapterListener.onRequestLabel(item);
+ @Override
+ public void onClick(View v)
+ {
+ if (isSelected) {
+ if (adapterListener != null) {
+ adapterListener.onRequestLabel(item);
+ }
+ } else {
+ setSelectedItem(item.rowID);
}
- } else {
- setSelectedItem(item.rowID);
}
- }
- });
+ });
+ }
// event
- view.text2.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v)
- {
- if (isSelected) {
- if (adapterListener != null && !item.enabled) {
- adapterListener.onRequestSolarEvent(item);
+ if (view.text_event != null)
+ {
+ view.text_event.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v)
+ {
+ if (isSelected) {
+ if (adapterListener != null && !item.enabled) {
+ adapterListener.onRequestSolarEvent(item);
+ } else {
+ AlarmNotifications.showTimeUntilToast(context, v, item);
+ }
} else {
- AlarmNotifications.showTimeUntilToast(context, v, item);
+ setSelectedItem(item.rowID);
}
- } else {
- setSelectedItem(item.rowID);
}
- }
- });
+ });
+ }
// time
- view.text_datetime.setOnClickListener(new View.OnClickListener()
+ if (view.text_datetime != null)
{
- @Override
- public void onClick(View v)
+ view.text_datetime.setOnClickListener(new View.OnClickListener()
{
- if (isSelected) {
- if (adapterListener != null && !item.enabled) {
- adapterListener.onRequestTime(item);
+ @Override
+ public void onClick(View v)
+ {
+ if (isSelected) {
+ if (adapterListener != null && !item.enabled) {
+ adapterListener.onRequestTime(item);
+ } else {
+ AlarmNotifications.showTimeUntilToast(context, v, item);
+ }
} else {
- AlarmNotifications.showTimeUntilToast(context, v, item);
+ setSelectedItem(item.rowID);
}
- } else {
- setSelectedItem(item.rowID);
}
- }
- });
+ });
+ }
- view.text_date.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v)
- {
- if (isSelected) {
- if (item.enabled) {
- AlarmNotifications.showTimeUntilToast(context, v, item);
+ if (view.text_date != null)
+ {
+ view.text_date.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v)
+ {
+ if (isSelected) {
+ if (item.enabled) {
+ AlarmNotifications.showTimeUntilToast(context, v, item);
+ }
+ } else {
+ setSelectedItem(item.rowID);
}
- } else {
- setSelectedItem(item.rowID);
}
- }
- });
+ });
+ }
// location
- view.text_location.setOnClickListener(new View.OnClickListener()
+ if (view.text_location != null)
{
- @Override
- public void onClick(View v)
+ view.text_location.setOnClickListener(new View.OnClickListener()
{
- if (isSelected) {
- if (adapterListener != null && !item.enabled) {
- adapterListener.onRequestLocation(item);
+ @Override
+ public void onClick(View v)
+ {
+ if (isSelected) {
+ if (adapterListener != null && !item.enabled) {
+ adapterListener.onRequestLocation(item);
+ } else {
+ AlarmNotifications.showTimeUntilToast(context, v, item);
+ }
} else {
- AlarmNotifications.showTimeUntilToast(context, v, item);
+ setSelectedItem(item.rowID);
}
- } else {
- setSelectedItem(item.rowID);
}
- }
- });
+ });
+ }
// enabled / disabled
if (view.switch_enabled != null) {
@@ -348,103 +378,170 @@ public void onClick(View v)
}
// ringtone
- view.text_ringtone.setOnClickListener(new View.OnClickListener()
+ if (view.text_ringtone != null)
{
- @Override
- public void onClick(View v)
+ view.text_ringtone.setOnClickListener(new View.OnClickListener()
{
- if (isSelected) {
- if (adapterListener != null) {
- adapterListener.onRequestRingtone(item);
+ @Override
+ public void onClick(View v)
+ {
+ if (isSelected) {
+ if (adapterListener != null) {
+ adapterListener.onRequestRingtone(item);
+ }
+ } else {
+ setSelectedItem(item.rowID);
}
- } else {
- setSelectedItem(item.rowID);
}
- }
- });
+ });
+ }
+
+ // actions
+ if (view.text_action0 != null) {
+ view.text_action0.setOnClickListener(onRequestActionClickListener(item, 0));
+ }
+ if (view.text_action1 != null) {
+ view.text_action1.setOnClickListener(onRequestActionClickListener(item, 1));
+ }
// vibrate
- view.check_vibrate.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
+ if (view.check_vibrate != null)
{
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
+ view.check_vibrate.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
{
- item.vibrate = isChecked;
- item.modified = true;
- AlarmDatabaseAdapter.AlarmUpdateTask task = new AlarmDatabaseAdapter.AlarmUpdateTask(context, false, false);
- task.execute(item);
-
- if (isChecked) {
- Vibrator vibrate = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
- if (vibrate != null) {
- vibrate.vibrate(500);
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
+ {
+ item.vibrate = isChecked;
+ item.modified = true;
+ AlarmDatabaseAdapter.AlarmUpdateTask task = new AlarmDatabaseAdapter.AlarmUpdateTask(context, false, false);
+ task.execute(item);
+
+ if (isChecked) {
+ Vibrator vibrate = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+ if (vibrate != null) {
+ vibrate.vibrate(500);
+ }
}
- }
- if (!isSelected) {
- setSelectedItem(item.rowID);
+ if (!isSelected) {
+ setSelectedItem(item.rowID);
+ }
}
- }
- });
+ });
+ }
// repeating
- view.option_repeat.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v)
- {
- if (isSelected) {
- if (adapterListener != null && !item.enabled) {
- adapterListener.onRequestRepetition(item);
+ if (view.text_repeat != null)
+ {
+ view.text_repeat.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v)
+ {
+ if (isSelected) {
+ if (adapterListener != null && !item.enabled) {
+ adapterListener.onRequestRepetition(item);
+ } else {
+ AlarmNotifications.showTimeUntilToast(context, v, item);
+ }
} else {
- AlarmNotifications.showTimeUntilToast(context, v, item);
+ setSelectedItem(item.rowID);
}
- } else {
- setSelectedItem(item.rowID);
}
- }
- });
+ });
+ }
// offset (before / after)
- view.option_offset.setOnClickListener(new View.OnClickListener()
+ if (view.text_offset != null)
{
- @Override
- public void onClick(View v)
+ view.text_offset.setOnClickListener(new View.OnClickListener()
{
- if (isSelected) {
- if (adapterListener != null && !item.enabled) {
- adapterListener.onRequestOffset(item);
+ @Override
+ public void onClick(View v)
+ {
+ if (isSelected) {
+ if (adapterListener != null && !item.enabled) {
+ adapterListener.onRequestOffset(item);
+ } else {
+ AlarmNotifications.showTimeUntilToast(context, v, item);
+ }
} else {
- AlarmNotifications.showTimeUntilToast(context, v, item);
+ setSelectedItem(item.rowID);
}
- } else {
- setSelectedItem(item.rowID);
}
- }
- });
+ });
+ }
// overflow menu
- view.overflow.setOnClickListener(new View.OnClickListener()
+ if (view.overflow != null)
+ {
+ view.overflow.setOnClickListener(new View.OnClickListener()
+ {
+ @Override
+ public void onClick(View v) {
+ showOverflowMenu(item, v, view.card);
+ }
+ });
+ }
+ }
+
+ private View.OnClickListener onRequestActionClickListener(final AlarmClockItem item, final int actionNum)
+ {
+ return new View.OnClickListener()
{
@Override
public void onClick(View v) {
- showOverflowMenu(item, v, view.card);
+ if ((item.rowID == selectedItem))
+ {
+ if (adapterListener != null) {
+ adapterListener.onRequestAction(item, actionNum);
+ }
+ } else {
+ setSelectedItem(item.rowID);
+ }
}
- });
+ };
}
+
private void clearListeners( AlarmClockItemView view )
{
- view.typeButton.setOnClickListener(null);
- view.text.setOnClickListener(null);
- view.text2.setOnClickListener(null);
- view.text_datetime.setOnClickListener(null);
- view.text_location.setOnClickListener(null);
- view.text_ringtone.setOnClickListener(null);
- view.check_vibrate.setOnCheckedChangeListener(null);
- view.option_repeat.setOnClickListener(null);
- view.option_offset.setOnClickListener(null);
- view.overflow.setOnClickListener(null);
-
+ if (view.typeButton != null) {
+ view.typeButton.setOnClickListener(null);
+ }
+ if (view.text_label != null) {
+ view.text_label.setOnClickListener(null);
+ }
+ if (view.text_event != null) {
+ view.text_event.setOnClickListener(null);
+ }
+ if (view.text_datetime != null) {
+ view.text_datetime.setOnClickListener(null);
+ }
+ if (view.text_location != null) {
+ view.text_location.setOnClickListener(null);
+ }
+ if (view.text_ringtone != null) {
+ view.text_ringtone.setOnClickListener(null);
+ }
+ if (view.text_action0 != null) {
+ view.text_action0.setOnClickListener(null);
+ }
+ if (view.text_action1 != null) {
+ view.text_action1.setOnClickListener(null);
+ }
+ if (view.check_vibrate != null) {
+ view.check_vibrate.setOnCheckedChangeListener(null);
+ }
+ if (view.text_repeat != null) {
+ view.text_repeat.setOnClickListener(null);
+ }
+ if (view.text_offset != null) {
+ view.text_offset.setOnClickListener(null);
+ }
+ if (view.overflow != null) {
+ view.overflow.setOnClickListener(null);
+ }
if (view.switch_enabled != null) {
view.switch_enabled.setOnCheckedChangeListener(null);
}
@@ -463,21 +560,24 @@ private void updateView(AlarmClockItemView view, @NonNull final AlarmClockItem i
view.cardBackdrop.setBackgroundColor( isSelected ? ColorUtils.setAlphaComponent(alarmSelectedColor, 170) : Color.TRANSPARENT ); // 66% alpha
// enabled / disabled
- if (Build.VERSION.SDK_INT >= 14)
+ if (view.switch_enabled != null)
{
- view.switch_enabled.setChecked(item.enabled);
- view.switch_enabled.setThumbTintList(SuntimesUtils.colorStateList(alarmEnabledColor, offColor, disabledColor, pressedColor));
- view.switch_enabled.setTrackTintList(SuntimesUtils.colorStateList(
- ColorUtils.setAlphaComponent(alarmEnabledColor, 85), ColorUtils.setAlphaComponent(offColor, 85),
- ColorUtils.setAlphaComponent(disabledColor, 85), ColorUtils.setAlphaComponent(pressedColor, 85))); // 33% alpha (85 / 255)
- } else {
- view.check_enabled.setChecked(item.enabled);
- CompoundButtonCompat.setButtonTintList(view.check_enabled, SuntimesUtils.colorStateList(alarmEnabledColor, offColor, disabledColor, pressedColor));
+ if (Build.VERSION.SDK_INT >= 14)
+ {
+ view.switch_enabled.setChecked(item.enabled);
+ view.switch_enabled.setThumbTintList(SuntimesUtils.colorStateList(alarmEnabledColor, offColor, disabledColor, pressedColor));
+ view.switch_enabled.setTrackTintList(SuntimesUtils.colorStateList(
+ ColorUtils.setAlphaComponent(alarmEnabledColor, 85), ColorUtils.setAlphaComponent(offColor, 85),
+ ColorUtils.setAlphaComponent(disabledColor, 85), ColorUtils.setAlphaComponent(pressedColor, 85))); // 33% alpha (85 / 255)
+ } else {
+ view.check_enabled.setChecked(item.enabled);
+ CompoundButtonCompat.setButtonTintList(view.check_enabled, SuntimesUtils.colorStateList(alarmEnabledColor, offColor, disabledColor, pressedColor));
+ }
}
- LayerDrawable alarmEnabledLayers = (LayerDrawable)alarmEnabledBG;
- GradientDrawable alarmEnabledLayers0 = (GradientDrawable)alarmEnabledLayers.getDrawable(0);
- alarmEnabledLayers0.setStroke((int)(3 * context.getResources().getDisplayMetrics().density), alarmEnabledColor);
+ //LayerDrawable alarmEnabledLayers = (LayerDrawable)alarmEnabledBG;
+ //GradientDrawable alarmEnabledLayers0 = (GradientDrawable)alarmEnabledLayers.getDrawable(0);
+ //alarmEnabledLayers0.setStroke((int)(3 * context.getResources().getDisplayMetrics().density), alarmEnabledColor);
if (Build.VERSION.SDK_INT >= 16) {
view.card.setBackground(item.enabled ? alarmEnabledBG : alarmDisabledBG);
} else {
@@ -485,139 +585,200 @@ private void updateView(AlarmClockItemView view, @NonNull final AlarmClockItem i
}
// type button
- view.typeButton.setImageDrawable(ContextCompat.getDrawable(context, (item.type == AlarmClockItem.AlarmType.ALARM ? iconAlarm : iconNotification)));
- view.typeButton.setContentDescription(item.type.getDisplayString());
+ if (view.typeButton != null)
+ {
+ view.typeButton.setImageDrawable(ContextCompat.getDrawable(context, (item.type == AlarmClockItem.AlarmType.ALARM ? iconAlarm : iconNotification)));
+ view.typeButton.setContentDescription(item.type.getDisplayString());
- if (!isSelected && !item.enabled) {
- ImageViewCompat.setImageTintList(view.typeButton, SuntimesUtils.colorStateList(disabledColor, disabledColor, disabledColor));
- } else if (item.enabled) {
- ImageViewCompat.setImageTintList(view.typeButton, SuntimesUtils.colorStateList(alarmEnabledColor, disabledColor, pressedColor));
- } else {
- ImageViewCompat.setImageTintList(view.typeButton, SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ if (!isSelected && !item.enabled) {
+ ImageViewCompat.setImageTintList(view.typeButton, SuntimesUtils.colorStateList(disabledColor, disabledColor, disabledColor));
+ } else if (item.enabled) {
+ ImageViewCompat.setImageTintList(view.typeButton, SuntimesUtils.colorStateList(alarmEnabledColor, disabledColor, pressedColor));
+ } else {
+ ImageViewCompat.setImageTintList(view.typeButton, SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ }
}
// label
- view.text.setText(getAlarmLabel(context, item));
- if (!isSelected && !item.enabled) {
- view.text.setTextColor(disabledColor);
- } else if (item.enabled) {
- view.text.setTextColor(SuntimesUtils.colorStateList(alarmEnabledColor, alarmEnabledColor, pressedColor));
- } else {
- view.text.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ if (view.text_label != null)
+ {
+ view.text_label.setText(AlarmEditViewHolder.displayAlarmLabel(context, item));
+ if (!isSelected && !item.enabled) {
+ view.text_label.setTextColor(disabledColor);
+ } else if (item.enabled) {
+ view.text_label.setTextColor(SuntimesUtils.colorStateList(alarmEnabledColor, alarmEnabledColor, pressedColor));
+ } else {
+ view.text_label.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ }
}
// event
- view.text2.setText(getAlarmEvent(context, item));
- if (!isSelected || item.enabled) {
- view.text2.setTextColor(disabledColor);
- } else {
- view.text2.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ if (view.text_event != null)
+ {
+ view.text_event.setText(AlarmEditViewHolder.displayEvent(context, item));
+ if (!isSelected || item.enabled) {
+ view.text_event.setTextColor(disabledColor);
+ } else {
+ view.text_event.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ }
}
// time
- view.text_datetime.setText(getAlarmTime(context, item));
- if (!isSelected && !item.enabled) {
- view.text_datetime.setTextColor(disabledColor);
- } else if (item.enabled) {
- view.text_datetime.setTextColor(SuntimesUtils.colorStateList(alarmEnabledColor, alarmEnabledColor, pressedColor));
- } else {
- view.text_datetime.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ if (view.text_datetime != null)
+ {
+ view.text_datetime.setText(AlarmEditViewHolder.displayAlarmTime(context, item));
+ if (!isSelected && !item.enabled) {
+ view.text_datetime.setTextColor(disabledColor);
+ } else if (item.enabled) {
+ view.text_datetime.setTextColor(SuntimesUtils.colorStateList(alarmEnabledColor, alarmEnabledColor, pressedColor));
+ } else {
+ view.text_datetime.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ }
}
// date
- view.text_date.setText(getAlarmDate(context, item));
- view.text_date.setVisibility((eventType == SolarEvents.TYPE_MOONPHASE || eventType == SolarEvents.TYPE_SEASON) ? View.VISIBLE : View.GONE);
- if (!isSelected && !item.enabled) {
- view.text_date.setTextColor(disabledColor);
- } else if (item.enabled){
- view.text_date.setTextColor(SuntimesUtils.colorStateList(alarmEnabledColor, alarmEnabledColor, pressedColor));
- } else {
- view.text_date.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ if (view.text_date != null)
+ {
+ view.text_date.setText(AlarmEditViewHolder.displayAlarmDate(context, item));
+ view.text_date.setVisibility((eventType == SolarEvents.TYPE_MOONPHASE || eventType == SolarEvents.TYPE_SEASON) ? View.VISIBLE : View.GONE);
+ if (!isSelected && !item.enabled) {
+ view.text_date.setTextColor(disabledColor);
+ } else if (item.enabled){
+ view.text_date.setTextColor(SuntimesUtils.colorStateList(alarmEnabledColor, alarmEnabledColor, pressedColor));
+ } else {
+ view.text_date.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ }
}
// location
- view.text_location.setVisibility((item.event == null && item.timezone == null) ? View.INVISIBLE : View.VISIBLE);
- AlarmDialog.updateLocationLabel(context, view.text_location, item.location);
+ if (view.text_location != null)
+ {
+ view.text_location.setVisibility((item.event == null && item.timezone == null) ? View.INVISIBLE : View.VISIBLE);
+ AlarmDialog.updateLocationLabel(context, view.text_location, item.location);
- if (!isSelected || item.enabled) {
- Drawable[] d = SuntimesUtils.tintCompoundDrawables(view.text_location.getCompoundDrawables(), disabledColor);
- view.text_location.setCompoundDrawables(d[0], d[1], d[2], d[3]);
- view.text_location.setTextColor(disabledColor);
- } else {
- Drawable[] d = SuntimesUtils.tintCompoundDrawables(view.text_location.getCompoundDrawables(), onColor);
- view.text_location.setCompoundDrawables(d[0], d[1], d[2], d[3]);
- view.text_location.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ if (!isSelected || item.enabled) {
+ Drawable[] d = SuntimesUtils.tintCompoundDrawables(view.text_location.getCompoundDrawables(), disabledColor);
+ view.text_location.setCompoundDrawables(d[0], d[1], d[2], d[3]);
+ view.text_location.setTextColor(disabledColor);
+ } else {
+ Drawable[] d = SuntimesUtils.tintCompoundDrawables(view.text_location.getCompoundDrawables(), onColor);
+ view.text_location.setCompoundDrawables(d[0], d[1], d[2], d[3]);
+ view.text_location.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ }
}
// ringtone
- int iconID = item.ringtoneName != null ? iconSoundEnabled : iconSoundDisabled;
- int iconDimen = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,20, context.getResources().getDisplayMetrics());
- ImageSpan icon = isSelected || item.enabled ? SuntimesUtils.createImageSpan(context, iconID, iconDimen, iconDimen, item.enabled ? alarmEnabledColor : 0)
- : SuntimesUtils.createImageSpan(context, iconID, iconDimen, iconDimen, disabledColor, PorterDuff.Mode.MULTIPLY);
-
- final String none = context.getString(R.string.alarmOption_ringtone_none);
- String ringtoneName = isSelected ? (item.ringtoneName != null ? item.ringtoneName : none) : "";
+ if (view.text_ringtone != null)
+ {
+ view.text_ringtone.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ view.text_ringtone.setText( ringtoneDisplayChip(item, isSelected) );
+ }
- String ringtoneLabel = context.getString(R.string.alarmOption_ringtone_label, ringtoneName);
- SpannableStringBuilder ringtoneDisplay = SuntimesUtils.createSpan(context, ringtoneLabel, "[icon]", icon);
+ // action
+ if (view.text_action0 != null)
+ {
+ view.text_action0.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ view.text_action0.setText( actionDisplayChip(item, 0, isSelected));
+ view.text_action0.setVisibility( item.actionID0 != null ? View.VISIBLE : View.GONE );
+ }
- view.text_ringtone.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
- view.text_ringtone.setText(ringtoneDisplay);
+ if (view.text_action1 != null)
+ {
+ view.text_action1.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ view.text_action1.setText( actionDisplayChip(item, 1, isSelected));
+ view.text_action1.setVisibility( item.actionID1 != null ? View.VISIBLE : View.GONE );
+ }
// vibrate
- view.check_vibrate.setChecked(item.vibrate);
- view.check_vibrate.setText( isSelected ? context.getString(R.string.alarmOption_vibrate) : "");
- if (item.enabled)
- CompoundButtonCompat.setButtonTintList(view.check_vibrate, SuntimesUtils.colorStateList(alarmEnabledColor, disabledColor, pressedColor));
- else CompoundButtonCompat.setButtonTintList(view.check_vibrate, SuntimesUtils.colorStateList((isSelected ? alarmEnabledColor : disabledColor), disabledColor, pressedColor));
+ if (view.check_vibrate != null)
+ {
+ view.check_vibrate.setChecked(item.vibrate);
+ view.check_vibrate.setText( isSelected ? context.getString(R.string.alarmOption_vibrate) : "");
+ if (item.enabled)
+ CompoundButtonCompat.setButtonTintList(view.check_vibrate, SuntimesUtils.colorStateList(alarmEnabledColor, disabledColor, pressedColor));
+ else CompoundButtonCompat.setButtonTintList(view.check_vibrate, SuntimesUtils.colorStateList((isSelected ? alarmEnabledColor : disabledColor), disabledColor, pressedColor));
+ }
// repeating
- boolean noRepeat = item.repeatingDays == null || item.repeatingDays.isEmpty();
- String repeatText = AlarmClockItem.repeatsEveryDay(item.repeatingDays)
- ? context.getString(R.string.alarmOption_repeat_all)
- : noRepeat
- ? context.getString(R.string.alarmOption_repeat_none)
- : AlarmRepeatDialog.getDisplayString(context, item.repeatingDays);
-
- if (item.repeating && (eventType == SolarEvents.TYPE_MOONPHASE || eventType == SolarEvents.TYPE_SEASON)) {
- repeatText = context.getString(R.string.alarmOption_repeat);
- }
+ if (view.text_repeat != null)
+ {
+ boolean noRepeat = item.repeatingDays == null || item.repeatingDays.isEmpty();
+ String repeatText = AlarmClockItem.repeatsEveryDay(item.repeatingDays)
+ ? context.getString(R.string.alarmOption_repeat_all)
+ : noRepeat
+ ? context.getString(R.string.alarmOption_repeat_none)
+ : AlarmRepeatDialog.getDisplayString(context, item.repeatingDays);
+
+ if (item.repeating && (eventType == SolarEvents.TYPE_MOONPHASE || eventType == SolarEvents.TYPE_SEASON)) {
+ repeatText = context.getString(R.string.alarmOption_repeat);
+ }
- view.option_repeat.setText( isSelected || !noRepeat ? repeatText : "" );
+ view.text_repeat.setText( isSelected || !noRepeat ? repeatText : "" );
- if (!isSelected || item.enabled) {
- view.option_repeat.setTextColor(disabledColor);
- } else {
- view.option_repeat.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ if (!isSelected || item.enabled) {
+ view.text_repeat.setTextColor(disabledColor);
+ } else {
+ view.text_repeat.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ }
}
// offset (before / after)
+ if (view.text_offset != null)
+ {
+ Calendar alarmTime = Calendar.getInstance();
+ alarmTime.setTimeInMillis(item.timestamp);
+ int alarmHour = SuntimesUtils.is24() ? alarmTime.get(Calendar.HOUR_OF_DAY) : alarmTime.get(Calendar.HOUR);
- Calendar alarmTime = Calendar.getInstance();
- alarmTime.setTimeInMillis(item.timestamp);
- int alarmHour = SuntimesUtils.is24() ? alarmTime.get(Calendar.HOUR_OF_DAY) : alarmTime.get(Calendar.HOUR);
+ if (item.offset == 0)
+ {
+ String offsetDisplay = context.getResources().getQuantityString(R.plurals.offset_at_plural, alarmHour);
+ view.text_offset.setText(offsetDisplay);
- if (item.offset == 0)
- {
- String offsetDisplay = context.getResources().getQuantityString(R.plurals.offset_at_plural, alarmHour);
- view.option_offset.setText(offsetDisplay);
+ } else {
+ boolean isBefore = (item.offset <= 0);
+ String offsetText = utils.timeDeltaLongDisplayString(0, item.offset).getValue();
+ String offsetDisplay = context.getResources().getQuantityString((isBefore ? R.plurals.offset_before_plural : R.plurals.offset_after_plural), alarmHour, offsetText);
+ Spannable offsetSpan = SuntimesUtils.createBoldSpan(null, offsetDisplay, offsetText);
+ view.text_offset.setText(offsetSpan);
+ }
- } else {
- boolean isBefore = (item.offset <= 0);
- String offsetText = utils.timeDeltaLongDisplayString(0, item.offset).getValue();
- String offsetDisplay = context.getResources().getQuantityString((isBefore ? R.plurals.offset_before_plural : R.plurals.offset_after_plural), alarmHour, offsetText);
- Spannable offsetSpan = SuntimesUtils.createBoldSpan(null, offsetDisplay, offsetText);
- view.option_offset.setText(offsetSpan);
+ if (!isSelected || item.enabled) {
+ view.text_offset.setTextColor(disabledColor);
+ } else {
+ view.text_offset.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ }
}
- if (!isSelected || item.enabled) {
- view.option_offset.setTextColor(disabledColor);
- } else {
- view.option_offset.setTextColor(SuntimesUtils.colorStateList(onColor, disabledColor, pressedColor));
+ // overflow menu
+ if (view.overflow != null)
+ {
+ view.overflow.setVisibility(isSelected ? View.VISIBLE : View.INVISIBLE);
}
+ }
- // overflow menu
- view.overflow.setVisibility(isSelected ? View.VISIBLE : View.INVISIBLE);
+ private CharSequence ringtoneDisplayChip(AlarmClockItem item, boolean isSelected)
+ {
+ int iconDimen = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,20, context.getResources().getDisplayMetrics());
+ int ringtoneIconID = item.ringtoneName != null ? iconSoundEnabled : iconSoundDisabled;
+ ImageSpan ringtonIcon = isSelected || item.enabled
+ ? SuntimesUtils.createImageSpan(context, ringtoneIconID, iconDimen, iconDimen, item.enabled ? alarmEnabledColor : 0)
+ : SuntimesUtils.createImageSpan(context, ringtoneIconID, iconDimen, iconDimen, disabledColor, PorterDuff.Mode.MULTIPLY);
+ final String none = context.getString(R.string.alarmOption_ringtone_none);
+ String ringtoneName = isSelected ? (item.ringtoneName != null ? item.ringtoneName : none) : "";
+ String ringtoneLabel = context.getString(R.string.alarmOption_ringtone_label, ringtoneName);
+ return SuntimesUtils.createSpan(context, ringtoneLabel, "[icon]", ringtonIcon);
+ }
+
+ private CharSequence actionDisplayChip(AlarmClockItem item, int actionNum, boolean isSelected)
+ {
+ int iconDimen = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,20, context.getResources().getDisplayMetrics());
+ ImageSpan actionIcon = (isSelected || item.enabled)
+ ? SuntimesUtils.createImageSpan(context, iconAction, iconDimen, iconDimen, item.enabled ? alarmEnabledColor : 0)
+ : SuntimesUtils.createImageSpan(context, iconAction, iconDimen, iconDimen, disabledColor, PorterDuff.Mode.MULTIPLY);
+ String actionName = item.getActionID(actionNum); // TODO
+ String actionString = isSelected ? (item.actionID1 != null ? actionName : "") : "";
+ String actionLabel = context.getString(R.string.alarmOption_action_label, actionString);
+ return SuntimesUtils.createSpan(context, actionLabel, "[icon]", actionIcon);
}
/**
@@ -665,6 +826,12 @@ public boolean onMenuItemClick(MenuItem menuItem)
showAlarmTypeMenu(item, buttonView, itemView);
return true;
+ case R.id.setAlarmAction:
+ if (adapterListener != null) {
+ adapterListener.onRequestAction(item, 0); // TODO: action1
+ }
+ return true;
+
case R.id.setAlarmSound:
if (adapterListener != null) {
adapterListener.onRequestRingtone(item);
@@ -829,61 +996,11 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
};
}
- private static CharSequence getAlarmLabel(Context context, AlarmClockItem item)
- {
- String emptyLabel = ((item.type == AlarmClockItem.AlarmType.ALARM) ? context.getString(R.string.alarmMode_alarm) : context.getString(R.string.alarmMode_notification));
- return (item.label == null || item.label.isEmpty()) ? emptyLabel : item.label;
- }
-
- private static CharSequence getAlarmEvent(Context context, AlarmClockItem item)
- {
- if (item.event != null)
- {
- return item.event.getLongDisplayString();
-
- } else if (item.timezone != null) {
- Calendar adjustedTime = Calendar.getInstance(AlarmClockItem.AlarmTimeZone.getTimeZone(item.timezone, item.location));
- adjustedTime.set(Calendar.HOUR_OF_DAY, item.hour);
- adjustedTime.set(Calendar.MINUTE, item.minute);
- return utils.calendarTimeShortDisplayString(context, adjustedTime) + "\n" + AlarmClockItem.AlarmTimeZone.displayString(item.timezone);
-
- } else {
- return context.getString(R.string.alarmOption_solarevent_none);
- }
- }
-
- private static CharSequence getAlarmTime(Context context, AlarmClockItem item)
+ protected void onRequestDialog(final AlarmClockItem item)
{
- Calendar alarmTime = Calendar.getInstance();
- alarmTime.setTimeInMillis(item.timestamp);
-
- CharSequence alarmDesc;
- SuntimesUtils.TimeDisplayText timeText = utils.calendarTimeShortDisplayString(context, alarmTime, false);
- if (SuntimesUtils.is24()) {
- alarmDesc = timeText.getValue();
-
- } else {
- String timeString = timeText.getValue() + " " + timeText.getSuffix();
- alarmDesc = SuntimesUtils.createRelativeSpan(null, timeString, " " + timeText.getSuffix(), 0.40f);
+ if (adapterListener != null) {
+ adapterListener.onRequestDialog(item);
}
- return alarmDesc;
- }
-
- private static CharSequence getAlarmDate(Context context, AlarmClockItem item)
- {
- Calendar alarmTime = Calendar.getInstance();
- alarmTime.setTimeInMillis(item.timestamp);
-
- CharSequence alarmDesc;
- SuntimesUtils.TimeDisplayText timeText = utils.calendarDateDisplayString(context, alarmTime, true);
- if (SuntimesUtils.is24()) {
- alarmDesc = timeText.getValue();
-
- } else {
- String timeString = timeText.getValue() + " " + timeText.getSuffix();
- alarmDesc = SuntimesUtils.createRelativeSpan(null, timeString, " " + timeText.getSuffix(), 0.40f);
- }
- return alarmDesc;
}
/**
@@ -892,7 +1009,7 @@ private static CharSequence getAlarmDate(Context context, AlarmClockItem item)
*/
protected void confirmDeleteAlarm(final AlarmClockItem item, final View itemView)
{
- String message = context.getString(R.string.deletealarm_dialog_message, getAlarmLabel(context, item), getAlarmTime(context, item), getAlarmEvent(context, item));
+ String message = context.getString(R.string.deletealarm_dialog_message, AlarmEditViewHolder.displayAlarmLabel(context, item), AlarmEditViewHolder.displayAlarmTime(context, item), AlarmEditViewHolder.displayEvent(context, item));
AlertDialog.Builder confirm = new AlertDialog.Builder(context)
.setTitle(context.getString(R.string.deletealarm_dialog_title))
.setMessage(message)
@@ -930,7 +1047,7 @@ public void onAnimationRepeat(Animation animation) {
public void onAnimationEnd(Animation animation) {
items.remove(item);
notifyDataSetChanged();
- CharSequence message = context.getString(R.string.deletealarm_toast_success, getAlarmLabel(context, item), getAlarmTime(context, item), getAlarmEvent(context, item));
+ CharSequence message = context.getString(R.string.deletealarm_toast_success, AlarmEditViewHolder.displayAlarmLabel(context, item), AlarmEditViewHolder.displayAlarmTime(context, item), AlarmEditViewHolder.displayEvent(context, item));
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
});
@@ -938,26 +1055,12 @@ public void onAnimationEnd(Animation animation) {
}
}
- protected AlarmClockAdapterListener adapterListener;
- public void setAdapterListener(AlarmClockAdapterListener l)
+ protected AlarmItemAdapterListener adapterListener;
+ public void setAdapterListener(AlarmItemAdapterListener l)
{
adapterListener = l;
}
- /**
- * AlarmClockAdapterListener
- */
- public static abstract class AlarmClockAdapterListener
- {
- public void onRequestLabel(AlarmClockItem forItem) {}
- public void onRequestRingtone(AlarmClockItem forItem) {}
- public void onRequestSolarEvent(AlarmClockItem forItem) {}
- public void onRequestLocation(AlarmClockItem forItem) {}
- public void onRequestTime(AlarmClockItem forItem) {}
- public void onRequestOffset(AlarmClockItem forItem) {}
- public void onRequestRepetition(AlarmClockItem forItem) {}
- }
-
/**
* AlarmClockItemView
*/
@@ -966,15 +1069,17 @@ private static class AlarmClockItemView
public View card;
public View cardBackdrop;
public ImageButton typeButton;
- public TextView text;
- public TextView text2;
+ public TextView text_label;
+ public TextView text_event;
public TextView text_date;
public TextView text_datetime;
public TextView text_location;
public TextView text_ringtone;
+ public TextView text_action0;
+ public TextView text_action1;
public CheckBox check_vibrate;
- public TextView option_repeat;
- public TextView option_offset;
+ public TextView text_repeat;
+ public TextView text_offset;
public ImageButton overflow;
public SwitchCompat switch_enabled;
@@ -985,15 +1090,17 @@ public AlarmClockItemView(View view)
card = view.findViewById(R.id.layout_alarmcard);
cardBackdrop = view.findViewById(R.id.layout_alarmcard0);
typeButton = (ImageButton) view.findViewById(R.id.type_menu);
- text = (TextView) view.findViewById(android.R.id.text1);
- text2 = (TextView) view.findViewById(android.R.id.text2);
+ text_label = (TextView) view.findViewById(android.R.id.text1);
+ text_event = (TextView) view.findViewById(R.id.text_event);
text_date = (TextView) view.findViewById(R.id.text_date);
text_datetime = (TextView) view.findViewById(R.id.text_datetime);
- text_location = (TextView) view.findViewById(R.id.text_location_label);
+ text_location = (TextView) view.findViewById(R.id.text_location);
text_ringtone = (TextView) view.findViewById(R.id.text_ringtone);
+ text_action0 = (TextView) view.findViewById(R.id.text_action0);
+ text_action1 = (TextView) view.findViewById(R.id.text_action1);
check_vibrate = (CheckBox) view.findViewById(R.id.check_vibrate);
- option_repeat = (TextView) view.findViewById(R.id.option_repeat);
- option_offset = (TextView) view.findViewById(R.id.option_offset);
+ text_repeat = (TextView) view.findViewById(R.id.text_repeat);
+ text_offset = (TextView) view.findViewById(R.id.text_datetime_offset);
overflow = (ImageButton) view.findViewById(R.id.overflow_menu);
if (Build.VERSION.SDK_INT >= 14) {
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmListDialog.java b/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmListDialog.java
new file mode 100644
index 000000000..39011b64f
--- /dev/null
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmListDialog.java
@@ -0,0 +1,1438 @@
+/**
+ Copyright (C) 2020 Forrest Guice
+ This file is part of SuntimesWidget.
+
+ SuntimesWidget 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.
+
+ SuntimesWidget 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 SuntimesWidget. If not, see .
+*/
+package com.forrestguice.suntimeswidget.alarmclock.ui;
+
+import android.annotation.SuppressLint;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.ColorUtils;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.widget.ImageViewCompat;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.PopupMenu;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.SwitchCompat;
+import android.text.style.ImageSpan;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.ImageButton;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.forrestguice.suntimeswidget.R;
+import com.forrestguice.suntimeswidget.SuntimesUtils;
+import com.forrestguice.suntimeswidget.alarmclock.AlarmClockItem;
+import com.forrestguice.suntimeswidget.alarmclock.AlarmDatabaseAdapter;
+import com.forrestguice.suntimeswidget.alarmclock.AlarmNotifications;
+import com.forrestguice.suntimeswidget.alarmclock.AlarmSettings;
+import com.forrestguice.suntimeswidget.alarmclock.AlarmState;
+import com.forrestguice.suntimeswidget.calculator.core.Location;
+import com.forrestguice.suntimeswidget.settings.AppSettings;
+import com.forrestguice.suntimeswidget.settings.SolarEventIcons;
+import com.forrestguice.suntimeswidget.settings.SolarEvents;
+import com.forrestguice.suntimeswidget.settings.WidgetSettings;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+@SuppressWarnings("Convert2Diamond")
+public class AlarmListDialog extends DialogFragment
+{
+ public static final String EXTRA_SELECTED_ROWID = "selectedRowID";
+
+ protected View emptyView;
+ protected RecyclerView list;
+ protected AlarmListDialogAdapter adapter;
+ protected ProgressBar progress;
+
+ @Override
+ public void onCreate(Bundle savedState)
+ {
+ super.onCreate(savedState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle savedState)
+ {
+ ContextThemeWrapper contextWrapper = new ContextThemeWrapper(getActivity(), AppSettings.loadTheme(getContext()));
+ View content = inflater.cloneInContext(contextWrapper).inflate(R.layout.layout_dialog_alarmlist, parent, false);
+
+ progress = (ProgressBar) content.findViewById(R.id.progress);
+ progress.setVisibility(View.GONE);
+
+ emptyView = content.findViewById(android.R.id.empty);
+ emptyView.setOnClickListener(onEmptyViewClick);
+ emptyView.setVisibility(View.GONE);
+
+ adapter = new AlarmListDialogAdapter(getActivity());
+ adapter.setAdapterListener(adapterListener);
+
+ list = (RecyclerView) content.findViewById(R.id.recyclerview);
+ list.setLayoutManager(new LinearLayoutManager(getActivity()));
+ list.addItemDecoration(itemDecoration);
+ list.setAdapter(adapter);
+
+ if (savedState != null) {
+ loadSettings(savedState);
+ }
+
+ reloadAdapter();
+ return content;
+ }
+
+ @Override
+ public void onSaveInstanceState( Bundle outState )
+ {
+ saveSettings(outState);
+ super.onSaveInstanceState(outState);
+ }
+
+ protected void loadSettings(Bundle bundle)
+ {
+ if (adapter != null) {
+ adapter.setSelectedRowID(bundle.getLong(EXTRA_SELECTED_ROWID, -1));
+ }
+ }
+
+ protected void saveSettings(Bundle bundle)
+ {
+ if (adapter != null) {
+ bundle.putLong(EXTRA_SELECTED_ROWID, adapter.getSelectedRowID());
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ inflater.inflate(R.menu.alarmlist, menu);
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu)
+ {
+ int sortValue = AlarmSettings.loadPrefAlarmSort(getActivity());
+ MenuItem sort_alarmtime = menu.findItem(R.id.sortByAlarmTime);
+ MenuItem sort_creation = menu.findItem(R.id.sortByCreation);
+ sort_alarmtime.setChecked(sortValue == AlarmSettings.SORT_BY_ALARMTIME);
+ sort_creation.setChecked(sortValue == AlarmSettings.SORT_BY_CREATION);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ switch (item.getItemId())
+ {
+ case R.id.sortByAlarmTime:
+ AlarmSettings.savePrefAlarmSort(getActivity(), AlarmSettings.SORT_BY_ALARMTIME);
+ if (Build.VERSION.SDK_INT >= 11) {
+ getActivity().invalidateOptionsMenu();
+ } // else { TODO }
+ adapter.sortItems();
+ return true;
+
+ case R.id.sortByCreation:
+ AlarmSettings.savePrefAlarmSort(getActivity(), AlarmSettings.SORT_BY_CREATION);
+ if (Build.VERSION.SDK_INT >= 11) {
+ getActivity().invalidateOptionsMenu();
+ } // else { TODO }
+ adapter.sortItems();
+ return true;
+
+ case R.id.action_clear:
+ confirmClearAlarms(getActivity());
+ return true;
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ protected View.OnClickListener onEmptyViewClick = null;
+ protected void setOnEmptyViewClick( View.OnClickListener listener )
+ {
+ onEmptyViewClick = listener;
+ if (emptyView != null) {
+ emptyView.setOnClickListener(onEmptyViewClick);
+ }
+ }
+
+ public RecyclerView getList() {
+ return list;
+ }
+
+ public AlarmListDialogAdapter getAdapter() {
+ return adapter;
+ }
+
+ public void setSelectedRowID(long value) {
+ adapter.setSelectedRowID(value);
+ }
+
+ public long getSelectedRowID() {
+ return ((adapter != null) ? adapter.getSelectedRowID() : -1);
+ }
+
+ public void scrollToSelectedItem()
+ {
+ int position = adapter.getSelectedIndex();
+ if (position != -1) {
+ list.scrollToPosition(position);
+ }
+ }
+
+ public void clearSelection() {
+ adapter.clearSelection();
+ }
+
+ public void notifyAlarmUpdated(long rowID) {
+ reloadAdapter(rowID);
+ }
+
+ public void notifyAlarmDeleted(long rowID)
+ {
+ if (listener != null) {
+ listener.onAlarmDeleted(rowID);
+ }
+ offerUndoDeleteAlarm(getActivity(), adapter.getItem(rowID));
+ adapter.removeItem(rowID);
+ updateViews();
+ }
+
+ public void notifyAlarmsCleared()
+ {
+ if (listener != null) {
+ listener.onAlarmsCleared();
+ }
+ offerUndoClearAlarms(getActivity(), adapter.getItems()); // pass the (now stale) items to undo
+ reloadAdapter(); // and reload adapter (now cleared)
+ }
+
+ protected static DialogInterface.OnClickListener onDeleteConfirmed(final Context context, final AlarmClockItem item) {
+ return new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ context.sendBroadcast(AlarmNotifications.getAlarmIntent(context, AlarmNotifications.ACTION_DELETE, item.getUri()));
+ }
+ };
+ }
+ protected static DialogInterface.OnClickListener onClearAlarmsConfirmed(final Context context) {
+ return new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ context.sendBroadcast(AlarmNotifications.getAlarmIntent(context, AlarmNotifications.ACTION_DELETE, null));
+ }
+ };
+ }
+
+ public static void confirmClearAlarms(final Context context)
+ {
+ int[] attrs = { R.attr.icActionDelete };
+ TypedArray a = context.obtainStyledAttributes(attrs);
+ int iconResID = a.getResourceId(0, R.drawable.ic_action_discard);
+ a.recycle();
+
+ AlertDialog.Builder confirm = new AlertDialog.Builder(context)
+ .setTitle(context.getString(R.string.clearalarms_dialog_title))
+ .setMessage(context.getString(R.string.clearalarms_dialog_message))
+ .setIcon(iconResID)
+ .setPositiveButton(context.getString(R.string.clearalarms_dialog_ok), onClearAlarmsConfirmed(context))
+ .setNegativeButton(context.getString(R.string.clearalarms_dialog_cancel), null);
+ confirm.show();
+ }
+
+ public void offerUndoClearAlarms(Context context, final List items)
+ {
+ View view = getView();
+ if (context != null && view != null)
+ {
+ Snackbar snackbar = Snackbar.make(view, context.getString(R.string.clearalarms_toast_success), Snackbar.LENGTH_INDEFINITE);
+ snackbar.setAction(context.getString(R.string.configAction_undo), new View.OnClickListener() {
+ @Override
+ public void onClick(View v)
+ {
+ Context context = getActivity();
+ if (context != null) {
+ for (AlarmClockItem item : items) {
+ if (item != null) {
+ addAlarm(context, item);
+ }
+ }
+ }
+ }
+ });
+ SuntimesUtils.themeSnackbar(context, snackbar, null);
+ snackbar.setDuration(UNDO_DELETE_MILLIS);
+ snackbar.show();
+ }
+ }
+
+ public void offerUndoDeleteAlarm(Context context, final AlarmClockItem deletedItem)
+ {
+ View view = getView();
+ if (context != null && view != null && deletedItem != null)
+ {
+ Snackbar snackbar = Snackbar.make(view, context.getString(R.string.deletealarm_toast_success1, deletedItem.type.getDisplayString()), Snackbar.LENGTH_INDEFINITE);
+ snackbar.setAction(context.getString(R.string.configAction_undo), new View.OnClickListener() {
+ @Override
+ public void onClick(View v)
+ {
+ Context context = getActivity();
+ if (context != null && deletedItem != null) {
+ addAlarm(getActivity(), deletedItem);
+ }
+ }
+ });
+ SuntimesUtils.themeSnackbar(context, snackbar, null);
+ snackbar.setDuration(UNDO_DELETE_MILLIS);
+ snackbar.show();
+ }
+ }
+ public static final int UNDO_DELETE_MILLIS = 8000;
+
+ public AlarmClockItem createAlarm(final Context context, AlarmClockItem.AlarmType type, String label, SolarEvents event, Location location, int hour, int minute, String timezone, boolean vibrate, Uri ringtoneUri, ArrayList repetitionDays, boolean addToDatabase)
+ {
+ final AlarmClockItem alarm = createAlarm(context, type, label, event, location, hour, minute, timezone, vibrate, ringtoneUri, repetitionDays);
+ if (addToDatabase) {
+ addAlarm(context, alarm);
+ }
+ return alarm;
+ }
+
+ public static AlarmClockItem createAlarm(final Context context, AlarmClockItem.AlarmType type, String label, @NonNull SolarEvents event, @NonNull Location location) {
+ return createAlarm(context, type, label, event, location, -1, -1, null, AlarmSettings.loadPrefVibrateDefault(context), AlarmSettings.getDefaultRingtoneUri(context, type), AlarmRepeatDialog.PREF_DEF_ALARM_REPEATDAYS);
+ }
+
+ public static AlarmClockItem createAlarm(final Context context, AlarmClockItem.AlarmType type, String label, SolarEvents event, Location location, int hour, int minute, String timezone, boolean vibrate, Uri ringtoneUri, ArrayList repetitionDays)
+ {
+ final AlarmClockItem alarm = new AlarmClockItem();
+ alarm.enabled = AlarmSettings.loadPrefAlarmAutoEnable(context);
+ alarm.type = type;
+ alarm.label = label;
+ alarm.hour = hour;
+ alarm.minute = minute;
+ alarm.timezone = timezone;
+ alarm.event = event;
+ alarm.location = (location != null ? location : WidgetSettings.loadLocationPref(context, 0));
+ alarm.repeating = false;
+ alarm.vibrate = vibrate;
+
+ alarm.ringtoneURI = (ringtoneUri != null ? ringtoneUri.toString() : null);
+ if (alarm.ringtoneURI != null)
+ {
+ Ringtone ringtone = RingtoneManager.getRingtone(context, ringtoneUri);
+ alarm.ringtoneName = ringtone.getTitle(context);
+ ringtone.stop();
+ }
+
+ alarm.setState(alarm.enabled ? AlarmState.STATE_NONE : AlarmState.STATE_DISABLED);
+ alarm.modified = true;
+ return alarm;
+ }
+
+
+ public AlarmClockItem addAlarm(final Context context, AlarmClockItem alarm)
+ {
+ AlarmDatabaseAdapter.AlarmUpdateTask task = new AlarmDatabaseAdapter.AlarmUpdateTask(context, true, true);
+ task.setTaskListener(new AlarmDatabaseAdapter.AlarmItemTaskListener()
+ {
+ @Override
+ public void onFinished(Boolean result, AlarmClockItem item)
+ {
+ if (result)
+ {
+ if (listener != null) {
+ listener.onAlarmAdded(item);
+ }
+
+ setSelectedRowID(item.rowID);
+ reloadAdapter();
+
+ if (item.enabled) {
+ context.sendBroadcast( AlarmNotifications.getAlarmIntent(context, AlarmNotifications.ACTION_SCHEDULE, item.getUri()) );
+ }
+ }
+ }
+ });
+ task.execute(alarm);
+ return alarm;
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public void reloadAdapter() {
+ reloadAdapter(null, onListLoaded);
+ }
+ public void reloadAdapter(Long rowId) {
+ reloadAdapter(rowId, onItemChanged);
+ }
+ public void reloadAdapter(Long rowId, AlarmListTask.AlarmListTaskListener taskListener)
+ {
+ AlarmListTask listTask = new AlarmListTask(getActivity());
+ listTask.setTaskListener(taskListener);
+ listTask.execute(rowId);
+ Log.d("DEBUG", "reloadAdapter");
+ }
+
+ protected AlarmListTask.AlarmListTaskListener onListLoaded = new AlarmListTask.AlarmListTaskListener() {
+ @Override
+ public void onLoadFinished(List data)
+ {
+ Log.d("DEBUG", "onListLoaded: " + data.size());
+ adapter.setItems(data);
+ updateViews();
+ scrollToSelectedItem();
+ }
+ };
+
+ protected AlarmListTask.AlarmListTaskListener onItemChanged = new AlarmListTask.AlarmListTaskListener() {
+ @Override
+ public void onLoadFinished(List data)
+ {
+ Log.d("DEBUG", "onItemChanged: " + data.size());
+ adapter.setItem(data.get(0));
+ updateViews();
+ scrollToSelectedItem();
+ }
+ };
+
+ protected void updateViews()
+ {
+ emptyView.setVisibility(adapter.getItemCount() > 0 ? View.GONE : View.VISIBLE);
+ list.setVisibility(adapter.getItemCount() > 0 ? View.VISIBLE : View.GONE);
+ }
+
+ /**
+ * AlarmClockListTask
+ */
+ public static class AlarmListTask extends AsyncTask>
+ {
+ private AlarmDatabaseAdapter db;
+ private WeakReference contextRef;
+
+ public AlarmListTask(Context context)
+ {
+ contextRef = new WeakReference<>(context);
+ db = new AlarmDatabaseAdapter(context.getApplicationContext());
+ }
+
+ @Override
+ protected void onPreExecute() {}
+
+ @Override
+ protected List doInBackground(Long... rowIds)
+ {
+ ArrayList items = new ArrayList<>();
+ db.open();
+ Cursor cursor = (rowIds == null || rowIds.length <= 0 || rowIds[0] == null)
+ ? db.getAllAlarms(0, true) : db.getAlarm(rowIds[0]);
+ while (!cursor.isAfterLast())
+ {
+ ContentValues entryValues = new ContentValues();
+ DatabaseUtils.cursorRowToContentValues(cursor, entryValues);
+
+ AlarmClockItem item = new AlarmClockItem(contextRef.get(), entryValues);
+ if (!item.enabled) {
+ AlarmNotifications.updateAlarmTime(contextRef.get(), item);
+ }
+ items.add(item);
+ publishProgress(item);
+
+ cursor.moveToNext();
+ }
+ db.close();
+ return items;
+ }
+
+ @Override
+ protected void onProgressUpdate(AlarmClockItem... item) {}
+
+ @Override
+ protected void onPostExecute(List result)
+ {
+ if (result != null)
+ {
+ if (taskListener != null) {
+ taskListener.onLoadFinished(result);
+ }
+ }
+ }
+
+ protected AlarmListTaskListener taskListener;
+ public void setTaskListener( AlarmListTaskListener l )
+ {
+ taskListener = l;
+ }
+
+ public static abstract class AlarmListTaskListener
+ {
+ public void onLoadFinished(List result) {};
+ }
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * RecyclerView.Adapter
+ */
+ public static class AlarmListDialogAdapter extends RecyclerView.Adapter
+ {
+ protected long selectedRowID = -1;
+ protected ArrayList items = new ArrayList<>();
+ protected WeakReference contextRef;
+
+ public AlarmListDialogAdapter(Context context) {
+ super();
+ contextRef = new WeakReference<>(context);
+ setHasStableIds(true);
+ }
+
+ public void setSelectedRowID(long rowID) {
+ Log.d("setSelectedRowID", ""+ rowID);
+ selectedRowID = rowID;
+ notifyDataSetChanged();
+ }
+ public long getSelectedRowID() {
+ return selectedRowID;
+ }
+
+ public void setSelectedIndex(int index) {
+ AlarmClockItem item = items.get(index);
+ setSelectedRowID(item.rowID);
+ }
+ public int getSelectedIndex() {
+ return getIndex(selectedRowID);
+ }
+ public int getIndex( long rowID )
+ {
+ for (int i=0; i values)
+ {
+ items.clear();
+ items.addAll(sortItems(values));
+ notifyDataSetChanged();
+ }
+
+ public void setItem(AlarmClockItem item)
+ {
+ int position = getIndex(item.rowID);
+ if (position >= 0 && position < items.size())
+ {
+ items.add(position, item);
+ AlarmClockItem previous = items.remove(position + 1);
+
+ if (item.timestamp != previous.timestamp) {
+ sortItems();
+ } else {
+ notifyItemChanged(position);
+ }
+
+ } else {
+ items.add(item);
+ notifyDataSetChanged();
+ }
+ }
+
+ public void clearItems() {
+ items.clear();
+ notifyDataSetChanged();
+ }
+
+ public void removeItem(long alarmID)
+ {
+ int position = getIndex(alarmID);
+ if (position >= 0 && position < items.size()) {
+ items.remove(position);
+ notifyItemRemoved(position);
+ }
+ }
+
+ public AlarmClockItem getItem(long rowID)
+ {
+ for (int i=0; i getItems() {
+ return new ArrayList<>(items);
+ }
+
+ public void sortItems()
+ {
+ sortItems(items);
+ notifyDataSetChanged();
+ }
+
+ protected List sortItems(List items)
+ {
+ final long now = Calendar.getInstance().getTimeInMillis();
+ final int sortMode = AlarmSettings.loadPrefAlarmSort(contextRef.get());
+ Collections.sort(items, new Comparator()
+ {
+ @Override
+ public int compare(AlarmClockItem o1, AlarmClockItem o2)
+ {
+ switch (sortMode)
+ {
+ case AlarmSettings.SORT_BY_ALARMTIME: // nearest alarm time first
+ return compareLong((o1.timestamp + o1.offset) - now, (o2.timestamp + o2.offset) - now);
+
+ case AlarmSettings.SORT_BY_CREATION:
+ default: return compareLong(o2.rowID, o1.rowID); // newest items first
+ }
+ }
+ });
+ return items;
+ }
+
+ static int compareLong(long x, long y) {
+ return (x < y) ? -1 : ((x == y) ? 0 : 1); // copied from Long.compare to support api < 19
+ }
+
+ @Override
+ public long getItemId( int position ) {
+ return (position >= 0 && position < items.size()) ? items.get(position).rowID : 0;
+ }
+
+ @Override
+ public AlarmListDialogItem onCreateViewHolder(ViewGroup parent, int viewType)
+ {
+ LayoutInflater layout = LayoutInflater.from(parent.getContext());
+ View view = layout.inflate(R.layout.layout_listitem_alarmclock2, parent, false);
+ return new AlarmListDialogItem(view);
+ }
+
+ @Override
+ public void onBindViewHolder(AlarmListDialogItem holder, int position)
+ {
+ AlarmClockItem item = items.get(position);
+ holder.isSelected = (item.rowID == selectedRowID);
+ holder.preview_offset = !holder.isSelected;
+ ViewCompat.setTransitionName(holder.text_datetime, "transition_" + item.rowID);
+
+ detachClickListeners(holder);
+ holder.bindData(contextRef.get(), items.get(position));
+ attachClickListeners(holder, position);
+ }
+
+ @Override
+ public void onViewRecycled(AlarmListDialogItem holder)
+ {
+ detachClickListeners(holder);
+ holder.isSelected = false;
+ }
+
+ private void attachClickListeners(@NonNull final AlarmListDialogItem holder, final int position)
+ {
+ if (holder.card != null) {
+ holder.card.setOnClickListener(itemClickListener(position, holder));
+ //holder.card.setOnLongClickListener(itemLongClickListener(position));
+ }
+ if (holder.overflow != null) {
+ holder.overflow.setOnClickListener(overflowMenuListener(position));
+ }
+ if (holder.typeButton != null) {
+ holder.typeButton.setOnClickListener(typeMenuListener(position, holder.typeButton));
+ }
+ if (holder.button_delete != null) {
+ holder.button_delete.setOnClickListener(deleteButtonListener(position));
+ }
+ if (holder.text_note != null) {
+ holder.text_note.setOnClickListener(noteListener(position, holder));
+ }
+
+ if (Build.VERSION.SDK_INT >= 14) {
+ if (holder.switch_enabled != null) {
+ holder.switch_enabled.setOnCheckedChangeListener(alarmEnabledListener(position));
+ }
+ } else {
+ if (holder.check_enabled != null) {
+ holder.check_enabled.setOnCheckedChangeListener(alarmEnabledListener(position));
+ }
+ }
+ }
+
+ private void detachClickListeners(@NonNull AlarmListDialogItem holder)
+ {
+ if (holder.card != null) {
+ holder.card.setOnClickListener(null);
+ holder.card.setOnLongClickListener(null);
+ }
+ if (holder.overflow != null) {
+ holder.overflow.setOnClickListener(null);
+ }
+ if (holder.typeButton != null) {
+ holder.typeButton.setOnClickListener(null);
+ }
+ if (holder.button_delete != null) {
+ holder.button_delete.setOnClickListener(null);
+ }
+ if (holder.text_note != null) {
+ holder.text_note.setOnClickListener(null);
+ }
+
+ if (Build.VERSION.SDK_INT >= 14) {
+ if (holder.switch_enabled != null) {
+ holder.switch_enabled.setOnCheckedChangeListener(null);
+ }
+ } else {
+ if (holder.check_enabled != null) {
+ holder.check_enabled.setOnCheckedChangeListener(null);
+ }
+ }
+ }
+
+ private View.OnClickListener itemClickListener(final int position, final AlarmListDialogItem holder)
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v)
+ {
+ if (listener != null) {
+ listener.onItemClicked(items.get(position), holder);
+ }
+ setSelectedIndex(position);
+ }
+ };
+ }
+
+ private View.OnLongClickListener itemLongClickListener(final int position)
+ {
+ return new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v)
+ {
+ setSelectedIndex(position);
+ if (listener != null) {
+ return listener.onItemLongClicked(items.get(position));
+ } else return true;
+ }
+ };
+ }
+
+ private View.OnClickListener overflowMenuListener(final int position)
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setSelectedIndex(position);
+ showOverflowMenu(contextRef.get(), position, v);
+ }
+ };
+ }
+
+ private View.OnClickListener deleteButtonListener(final int position)
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ AlarmEditDialog.confirmDeleteAlarm(contextRef.get(), items.get(position), onDeleteConfirmed(contextRef.get(), items.get(position)));
+ }
+ };
+ }
+
+ private View.OnClickListener noteListener(final int position, final AlarmListDialogItem view)
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (listener != null) {
+ listener.onItemNoteClicked(items.get(position), view);
+ }
+ }
+ };
+ }
+
+ private View.OnClickListener editButtonListener(final int position, final AlarmListDialogItem holder)
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setSelectedIndex(position);
+ if (listener != null) {
+ listener.onItemClicked(items.get(position), holder);
+ }
+ }
+ };
+ }
+
+ private View.OnClickListener typeMenuListener(final int position, View v)
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setSelectedIndex(position);
+ showAlarmTypeMenu(contextRef.get(), position, v);
+ }
+ };
+ }
+
+ private CompoundButton.OnCheckedChangeListener alarmEnabledListener(final int position)
+ {
+ return new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ setSelectedIndex(position);
+ enableAlarm(contextRef.get(), position, isChecked);
+ }
+ };
+ }
+
+ @Override
+ public int getItemCount() {
+ return items.size();
+ }
+
+ protected AdapterListener listener;
+ public void setAdapterListener( AdapterListener listener ) {
+ this.listener = listener;
+ }
+
+ ///
+
+ protected void showOverflowMenu(Context context, final int position, final View buttonView)
+ {
+ PopupMenu menu = new PopupMenu(context, buttonView);
+ MenuInflater inflater = menu.getMenuInflater();
+ inflater.inflate(R.menu.alarmcontext1, menu.getMenu());
+ menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener()
+ {
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem)
+ {
+ switch (menuItem.getItemId())
+ {
+ case R.id.action_delete:
+ AlarmEditDialog.confirmDeleteAlarm(contextRef.get(), items.get(position), onDeleteConfirmed(contextRef.get(), items.get(position)));
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ });
+
+ SuntimesUtils.forceActionBarIcons(menu.getMenu());
+ menu.show();
+ }
+
+ protected void showAlarmTypeMenu(final Context context, final int position, final View buttonView)
+ {
+ PopupMenu menu = new PopupMenu(context, buttonView);
+ MenuInflater inflater = menu.getMenuInflater();
+ inflater.inflate(R.menu.alarmtype, menu.getMenu());
+
+ menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener()
+ {
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem)
+ {
+ switch (menuItem.getItemId())
+ {
+ case R.id.alarmTypeNotification:
+ return changeAlarmType(context, position, AlarmClockItem.AlarmType.NOTIFICATION);
+
+ case R.id.alarmTypeAlarm:
+ default:
+ return changeAlarmType(context, position, AlarmClockItem.AlarmType.ALARM);
+ }
+ }
+ });
+
+ SuntimesUtils.forceActionBarIcons(menu.getMenu());
+ menu.show();
+ }
+
+ protected boolean changeAlarmType(Context context, final int position, AlarmClockItem.AlarmType type)
+ {
+ AlarmClockItem item = items.get(position);
+ if (item.type != type)
+ {
+ Log.d("AlarmList", "alarmTypeMenu: alarm type is changed: " + type);
+ if (item.enabled)
+ {
+ Log.d("AlarmList", "alarmTypeMenu: alarm is enabled (reschedule required?)");
+ // item is enabled; disable it or reschedule/reenable
+ return false;
+
+ } else {
+ Log.d("AlarmList", "alarmTypeMenu: alarm is disabled, changing its type..");
+ item.type = type;
+ item.setState(AlarmState.STATE_NONE);
+
+ AlarmDatabaseAdapter.AlarmUpdateTask task = new AlarmDatabaseAdapter.AlarmUpdateTask(context, false, true);
+ task.setTaskListener(changeAlarmTypeTaskListener(position));
+ task.execute(item);
+ return true;
+ }
+ }
+ Log.w("AlarmList", "alarmTypeMenu: alarm type is unchanged");
+ return false;
+ }
+ private AlarmDatabaseAdapter.AlarmItemTaskListener changeAlarmTypeTaskListener(final int position)
+ {
+ return new AlarmDatabaseAdapter.AlarmItemTaskListener() {
+ @Override
+ public void onFinished(Boolean result, AlarmClockItem item) {
+ notifyItemChanged(position);
+ }
+ };
+ }
+
+ public void enableAlarm(final Context context, final int position, final boolean enabled)
+ {
+ AlarmClockItem item = items.get(position);
+ item.alarmtime = 0;
+ item.enabled = enabled;
+ item.modified = true;
+
+ AlarmDatabaseAdapter.AlarmUpdateTask enableTask = new AlarmDatabaseAdapter.AlarmUpdateTask(context, false, false);
+ enableTask.setTaskListener(new AlarmDatabaseAdapter.AlarmItemTaskListener()
+ {
+ @Override
+ public void onFinished(Boolean result, AlarmClockItem item)
+ {
+ if (result) {
+ context.sendBroadcast( enabled ? AlarmNotifications.getAlarmIntent(context, AlarmNotifications.ACTION_SCHEDULE, item.getUri())
+ : AlarmNotifications.getAlarmIntent(context, AlarmNotifications.ACTION_DISABLE, item.getUri()) );
+ if (!enabled) {
+ AlarmNotifications.updateAlarmTime(context, item);
+ }
+ notifyItemChanged(position);
+
+ if (listener != null) {
+ listener.onAlarmToggled(items.get(position), enabled);
+ }
+
+ } else Log.e("AlarmClockActivity", "enableAlarm: failed to save state!");
+ }
+ });
+ enableTask.execute(item);
+ }
+
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * RecyclerView.ViewHolder
+ */
+ public static class AlarmListDialogItem extends RecyclerView.ViewHolder
+ {
+ public static SuntimesUtils utils = new SuntimesUtils();
+
+ public boolean isSelected = false;
+ public boolean preview_offset = true;
+ public boolean preview_offset_transition = false;
+
+ public View card;
+ public View cardTray;
+ public View cardBackdrop;
+ public ImageButton typeButton;
+ public TextView text_label;
+ public TextView text_event;
+ public TextView text_note;
+ public TextView text_date;
+ public TextView text_datetime;
+ public TextView text_location;
+ public TextView text_ringtone;
+ public TextView text_action0;
+ public TextView text_action1;
+ public TextView text_vibrate;
+ public CheckBox check_vibrate;
+ public TextView text_repeat;
+ public TextView text_offset;
+ public ImageButton overflow;
+ public ImageButton button_delete;
+ public SwitchCompat switch_enabled;
+ public CheckBox check_enabled;
+
+ public int res_iconAlarm = R.drawable.ic_action_alarms;
+ public int res_iconNotification = R.drawable.ic_action_notification;
+ public int res_iconSoundOn = R.drawable.ic_action_soundenabled;
+ public int res_iconSoundOff = R.drawable.ic_action_sounddisabled;
+ public int res_iconVibrate = R.drawable.ic_action_vibration;
+ public int res_iconAction = R.drawable.ic_action_extension;
+ public int res_backgroundOn = R.drawable.card_alarmitem_enabled_dark1;
+ public int res_backgroundOff = R.drawable.card_alarmitem_disabled_dark1;
+
+ public int color_on = Color.CYAN;
+ public int color_off = Color.GRAY, color_off1 = Color.WHITE;
+ public int color_press = Color.MAGENTA;
+ public int color_selected = Color.CYAN;
+ public int color_notselected = Color.TRANSPARENT;
+
+ public AlarmListDialogItem(View view)
+ {
+ super(view);
+
+ card = view.findViewById(R.id.layout_alarmcard);
+ cardTray = view.findViewById(R.id.layout_alarmcard_tray);
+ cardBackdrop = view.findViewById(R.id.layout_alarmcard0);
+ typeButton = (ImageButton) view.findViewById(R.id.type_menu);
+ text_label = (TextView) view.findViewById(android.R.id.text1);
+ text_event = (TextView) view.findViewById(R.id.text_event);
+ text_note = (TextView) view.findViewById(R.id.text_note);
+ text_date = (TextView) view.findViewById(R.id.text_date);
+ text_datetime = (TextView) view.findViewById(R.id.text_datetime);
+ text_location = (TextView) view.findViewById(R.id.text_location);
+ text_ringtone = (TextView) view.findViewById(R.id.text_ringtone);
+ text_action0 = (TextView) view.findViewById(R.id.text_action0);
+ text_action1 = (TextView) view.findViewById(R.id.text_action1);
+ text_vibrate = (TextView) view.findViewById(R.id.text_vibrate);
+ check_vibrate = (CheckBox) view.findViewById(R.id.check_vibrate);
+ text_repeat = (TextView) view.findViewById(R.id.text_repeat);
+ text_offset = (TextView) view.findViewById(R.id.text_datetime_offset);
+ overflow = (ImageButton) view.findViewById(R.id.overflow_menu);
+ button_delete = (ImageButton) view.findViewById(R.id.button_delete);
+
+ if (Build.VERSION.SDK_INT >= 14) {
+ switch_enabled = (SwitchCompat) view.findViewById(R.id.switch_enabled); // switch used by api >= 14 (otherwise null)
+ } else {
+ check_enabled = (CheckBox) view.findViewById(R.id.switch_enabled); // checkbox used by api < 14 (otherwise null)
+ }
+ }
+
+ public void triggerPreviewOffset(final Context context, final AlarmClockItem item)
+ {
+ if (preview_offset_transition || item.offset == 0) {
+ return;
+ }
+
+ preview_offset = true;
+ preview_offset_transition = true;
+ bindData(context, item);
+
+ cardTray.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ preview_offset_transition = false;
+ preview_offset = !isSelected;
+ bindData(context, item);
+ }
+ }, AlarmEditDialog.PREVIEW_OFFSET_DURATION_MILLIS);
+ }
+
+ @SuppressLint("ResourceType")
+ private void themeViews(Context context)
+ {
+ int[] attrs = { R.attr.icActionAlarm, R.attr.icActionNotification,
+ R.attr.icActionSoundEnabled, R.attr.icActionSoundDisabled,
+ R.attr.icActionExtension, R.attr.icActionVibrationEnabled, R.attr.gridItemSelected,
+ R.attr.alarmCardEnabled, R.attr.alarmCardDisabled,
+ R.attr.alarmColorEnabled, android.R.attr.textColorSecondary, android.R.attr.textColorPrimary,
+ R.attr.buttonPressColor };
+ TypedArray a = context.obtainStyledAttributes(attrs);
+ res_iconAlarm = a.getResourceId(0, R.drawable.ic_action_alarms);
+ res_iconNotification = a.getResourceId(1, R.drawable.ic_action_notification);
+ res_iconSoundOn = a.getResourceId(2, R.drawable.ic_action_soundenabled);
+ res_iconSoundOff = a.getResourceId(3, R.drawable.ic_action_sounddisabled);
+ res_iconAction = a.getResourceId(4, R.drawable.ic_action_extension);
+ res_iconVibrate = a.getResourceId(5, R.drawable.ic_action_extension);
+ color_selected = ContextCompat.getColor(context, a.getResourceId(6, R.color.grid_selected_dark));
+ res_backgroundOn = a.getResourceId(7, R.drawable.card_alarmitem_enabled_dark1);
+ res_backgroundOff = a.getResourceId(8, R.drawable.card_alarmitem_disabled_dark1);
+ color_on = ContextCompat.getColor(context, a.getResourceId(9, R.color.alarm_enabled_dark));
+ color_off = ContextCompat.getColor(context, a.getResourceId(10, android.R.color.secondary_text_dark));
+ color_off1 = ContextCompat.getColor(context, a.getResourceId(11, android.R.color.primary_text_dark));
+ color_press = ContextCompat.getColor(context, a.getResourceId(12, R.color.btn_tint_pressed_dark));
+ a.recycle();
+ }
+
+
+ public void bindData(Context context, @NonNull AlarmClockItem item)
+ {
+ themeViews(context);
+ updateView(context, this, item);
+ }
+
+ protected void updateView(Context context, AlarmListDialogItem view, @NonNull final AlarmClockItem item)
+ {
+ int eventType = item.event == null ? -1 : item.event.getType();
+ boolean isSchedulable = AlarmNotifications.updateAlarmTime(context, item, Calendar.getInstance(), false);
+
+ // spannable icons
+ int iconColor = (item.enabled ? color_on : color_off);
+ int[] attrs = { R.attr.icActionTimeReset };
+ TypedArray a = context.obtainStyledAttributes(attrs);
+ int offsetIconSize = (int)context.getResources().getDimension(R.dimen.offsetIcon_width);
+ int offsetIconResID = a.getResourceId(0, R.drawable.ic_action_timereset);
+ a.recycle();
+ Drawable offsetIcon = SuntimesUtils.createImageSpan(context, offsetIconResID, offsetIconSize, offsetIconSize, iconColor).getDrawable().mutate();
+
+ // background
+ view.cardBackdrop.setBackgroundColor( isSelected ? ColorUtils.setAlphaComponent(color_selected, 170) : color_notselected); // 66% alpha
+ if (Build.VERSION.SDK_INT >= 16) {
+ view.card.setBackground(item.enabled ? ContextCompat.getDrawable(context, res_backgroundOn) : ContextCompat.getDrawable(context, res_backgroundOff));
+ } else {
+ view.card.setBackgroundDrawable(item.enabled ? ContextCompat.getDrawable(context, res_backgroundOn) : ContextCompat.getDrawable(context, res_backgroundOff));
+ }
+
+ // enabled / disabled
+ if (Build.VERSION.SDK_INT >= 14) {
+ if (view.switch_enabled != null) {
+ view.switch_enabled.setChecked(item.enabled);
+ }
+ } else {
+ if (view.check_enabled != null) {
+ view.check_enabled.setChecked(item.enabled);
+ }
+ }
+
+ // type button
+ if (view.typeButton != null) {
+ view.typeButton.setImageDrawable(ContextCompat.getDrawable(context, (item.type == AlarmClockItem.AlarmType.ALARM ? res_iconAlarm : res_iconNotification)));
+ view.typeButton.setContentDescription(item.type.getDisplayString());
+
+ ImageViewCompat.setImageTintList(view.typeButton, SuntimesUtils.colorStateList(
+ (!isSelected && !item.enabled ? color_off : (item.enabled ? color_on : color_off)),
+ color_off,
+ (!isSelected && !item.enabled ? color_off : color_press)
+ ));
+ }
+
+ // label
+ if (view.text_label != null) {
+ view.text_label.setText(AlarmEditViewHolder.displayAlarmLabel(context, item));
+ view.text_label.setTextColor(item.enabled ? color_on : color_off);
+ }
+
+ // event
+ if (view.text_event != null)
+ {
+ view.text_event.setText(AlarmEditViewHolder.displayEvent(context, item));
+ view.text_event.setTextColor(item.enabled ? color_on : color_off);
+
+ float eventIconSize = context.getResources().getDimension(R.dimen.eventIcon_width);
+ if (item.event != null)
+ {
+ Drawable eventIcon = SolarEventIcons.getIconDrawable(context, item.event, (int)eventIconSize, (int)eventIconSize);
+ view.text_event.setCompoundDrawablePadding(SolarEventIcons.getIconDrawablePadding(context, item.event));
+ view.text_event.setCompoundDrawables(eventIcon, null, null, null);
+
+ } else {
+ Drawable eventIcon = SolarEventIcons.getIconDrawable(context, item.timezone, (int)eventIconSize, (int)eventIconSize);
+ if (item.timezone == null) {
+ SolarEventIcons.tintDrawable(eventIcon, item.enabled ? color_on : color_off);
+ }
+ text_event.setCompoundDrawablePadding(SolarEventIcons.getIconDrawablePadding(context, item.timezone));
+ text_event.setCompoundDrawables(eventIcon, null, null, null);
+ }
+ }
+
+ // time
+ if (view.text_datetime != null)
+ {
+ CharSequence timeDisplay = isSchedulable ? AlarmEditViewHolder.displayAlarmTime(context, item, preview_offset) : "";
+ view.text_datetime.setText(timeDisplay);
+ view.text_datetime.setTextColor(item.enabled ? color_on : (isSelected ? color_off1 : color_off));
+
+ /*if (item.offset != 0 && !isSelected) {
+ view.text_datetime.setCompoundDrawablePadding((int)context.getResources().getDimension(R.dimen.offsetIcon_margin));
+ view.text_datetime.setCompoundDrawables(offsetIcon, null, null, null);
+ } else {
+ text_event.setCompoundDrawablePadding(SolarEventIcons.getIconDrawablePadding(context, item.timezone));
+ view.text_datetime.setCompoundDrawablePadding(0);
+ view.text_datetime.setCompoundDrawables(null, null, null, null);
+ }*/
+ }
+
+ // date
+ if (view.text_date != null) {
+ view.text_date.setText(isSchedulable ? AlarmEditViewHolder.displayAlarmDate(context, item, preview_offset) : "");
+ view.text_date.setVisibility(isSchedulable && AlarmEditViewHolder.showAlarmDate(context, item) ? View.VISIBLE : View.GONE);
+ view.text_date.setTextColor(item.enabled ? color_on : (isSelected ? color_off1 : color_off));
+ }
+
+ // location
+ if (view.text_location != null) {
+ view.text_location.setVisibility((item.event == null && item.timezone == null) ? View.INVISIBLE : View.VISIBLE);
+ view.text_location.setText(item.location.getLabel());
+ view.text_location.setTextColor(item.enabled ? color_on : color_off);
+
+ Drawable[] d = SuntimesUtils.tintCompoundDrawables(view.text_location.getCompoundDrawables(), (item.enabled ? color_on : color_off));
+ view.text_location.setCompoundDrawables(d[0], d[1], d[2], d[3]);
+ }
+
+ // ringtone
+ if (view.text_ringtone != null) {
+ view.text_ringtone.setText( ringtoneDisplayChip(context, item, isSelected) );
+ view.text_ringtone.setTextColor(item.enabled ? color_on : color_off);
+ }
+
+ // action
+ if (view.text_action0 != null) {
+ view.text_action0.setText(actionDisplayChip(context, item, 0, isSelected));
+ view.text_action0.setVisibility( item.actionID0 != null ? View.VISIBLE : View.GONE );
+ view.text_action0.setTextColor(item.enabled ? color_on : color_off);
+ }
+
+ if (view.text_action1 != null) {
+ view.text_action1.setText(actionDisplayChip(context, item, 1, isSelected));
+ view.text_action1.setVisibility( item.actionID1 != null ? View.VISIBLE : View.GONE );
+ view.text_action1.setTextColor(item.enabled ? color_on : color_off);
+ }
+
+ // vibrate
+ if (view.check_vibrate != null) {
+ view.check_vibrate.setChecked(item.vibrate);
+ view.check_vibrate.setTextColor(item.enabled ? color_on : color_off);
+ }
+ if (view.text_vibrate != null) {
+ view.text_vibrate.setText(vibrateDisplayChip(context, item, isSelected));
+ view.text_vibrate.setVisibility(item.vibrate ? View.VISIBLE : View.GONE);
+ view.text_vibrate.setTextColor(item.enabled ? color_on : color_off);
+ }
+
+ // repeating
+ if (view.text_repeat != null)
+ {
+ boolean noRepeat = item.repeatingDays == null || item.repeatingDays.isEmpty();
+ String repeatText = AlarmClockItem.repeatsEveryDay(item.repeatingDays)
+ ? context.getString(R.string.alarmOption_repeat_all)
+ : noRepeat
+ ? context.getString(R.string.alarmOption_repeat_none)
+ : AlarmRepeatDialog.getDisplayString(context, item.repeatingDays);
+
+ if (item.repeating && (eventType == SolarEvents.TYPE_MOONPHASE || eventType == SolarEvents.TYPE_SEASON)) {
+ repeatText = context.getString(R.string.alarmOption_repeat);
+ }
+
+ view.text_repeat.setText(repeatText);
+ view.text_repeat.setTextColor(item.enabled ? color_on : color_off);
+ view.text_repeat.setVisibility((noRepeat) ? View.GONE : View.VISIBLE);
+ }
+
+ // offset (before / after)
+ if (view.text_offset != null)
+ {
+ CharSequence offsetDisplay = (preview_offset ? "" : AlarmEditViewHolder.displayOffset(context, item));
+ view.text_offset.setText((isSchedulable && isSelected) ? offsetDisplay : "");
+
+ if (preview_offset && item.offset != 0) {
+ view.text_offset.setText(SuntimesUtils.createSpan(context, "i", "i", new ImageSpan(offsetIcon), ImageSpan.ALIGN_BASELINE));
+ }
+
+ view.text_offset.setTextColor(item.enabled ? color_on : color_off);
+ }
+
+ // extended tray
+ if (view.cardTray != null) {
+ view.cardTray.setVisibility(isSelected ? View.VISIBLE : View.GONE);
+ }
+ if (view.text_note != null) {
+ view.text_note.setText(isSelected ? AlarmEditViewHolder.displayAlarmNote(context, item, isSchedulable) : "");
+ }
+ }
+
+ private CharSequence ringtoneDisplayChip(Context context, AlarmClockItem item, boolean isSelected)
+ {
+ int iconDimen = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,20, context.getResources().getDisplayMetrics());
+ int iconID = item.ringtoneName != null ? res_iconSoundOn : res_iconSoundOff;
+ ImageSpan icon = isSelected || item.enabled
+ ? SuntimesUtils.createImageSpan(context, iconID, iconDimen, iconDimen, item.enabled ? color_on : 0)
+ : SuntimesUtils.createImageSpan(context, iconID, iconDimen, iconDimen, color_off, PorterDuff.Mode.MULTIPLY);
+ return SuntimesUtils.createSpan(context, "[icon]", "[icon]", icon);
+ }
+
+ private CharSequence vibrateDisplayChip(Context context, AlarmClockItem item, boolean isSelected)
+ {
+ if (item.vibrate)
+ {
+ int iconID = res_iconVibrate;
+ int iconDimen = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,20, context.getResources().getDisplayMetrics());
+ ImageSpan ringtonIcon = isSelected || item.enabled
+ ? SuntimesUtils.createImageSpan(context, iconID, iconDimen, iconDimen, item.enabled ? color_on : 0)
+ : SuntimesUtils.createImageSpan(context, iconID, iconDimen, iconDimen, color_off, PorterDuff.Mode.MULTIPLY);
+ return SuntimesUtils.createSpan(context, "[icon]", "[icon]", ringtonIcon);
+ } else {
+ return "";
+ }
+ }
+
+ private CharSequence actionDisplayChip(Context context, AlarmClockItem item, int actionNum, boolean isSelected)
+ {
+ int iconDimen = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,20, context.getResources().getDisplayMetrics());
+ ImageSpan icon = (isSelected || item.enabled)
+ ? SuntimesUtils.createImageSpan(context, res_iconAction, iconDimen, iconDimen, item.enabled ? color_on : 0)
+ : SuntimesUtils.createImageSpan(context, res_iconAction, iconDimen, iconDimen, color_off, PorterDuff.Mode.MULTIPLY);
+ return SuntimesUtils.createSpan(context, "[icon]", "[icon]", icon);
+ }
+ }
+
+ private RecyclerView.ItemDecoration itemDecoration = new RecyclerView.ItemDecoration()
+ {
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
+ {
+ int position = parent.getChildAdapterPosition(view);
+ if (position == adapter.getItemCount() - 1) { // add bottom margin on last item to avoid blocking FAB
+ outRect.bottom = 400;
+ } else {
+ super.getItemOffsets(outRect, view, parent, state);
+ }
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ protected AdapterListener listener;
+ protected AdapterListener adapterListener = new AdapterListener()
+ {
+ @Override
+ public void onItemClicked(AlarmClockItem item, AlarmListDialogItem holder)
+ {
+ if (listener != null) {
+ listener.onItemClicked(item, holder);
+ }
+ }
+
+ @Override
+ public boolean onItemLongClicked(AlarmClockItem item) {
+ if (listener != null) {
+ return listener.onItemLongClicked(item);
+ } else return false;
+ }
+
+ @Override
+ public void onItemNoteClicked(AlarmClockItem item, AlarmListDialogItem view) {
+ if (listener != null) {
+ listener.onItemNoteClicked(item, view);
+ }
+ }
+
+ @Override
+ public void onAlarmToggled(AlarmClockItem item, boolean enabled) {
+ if (listener != null) {
+ listener.onAlarmToggled(item, enabled);
+ }
+ }
+
+ @Override
+ public void onAlarmAdded(AlarmClockItem item) {
+ if (listener != null) {
+ listener.onAlarmAdded(item);
+ }
+ }
+
+ @Override
+ public void onAlarmDeleted(long rowID) {
+ if (listener != null) {
+ listener.onAlarmDeleted(rowID);
+ }
+ }
+
+ @Override
+ public void onAlarmsCleared() {
+ if (listener != null) {
+ listener.onAlarmsCleared();
+ }
+ }
+ };
+
+ public void setAdapterListener(AdapterListener listener) {
+ this.listener = listener;
+ }
+
+ public interface AdapterListener
+ {
+ void onItemClicked(AlarmClockItem item, AlarmListDialogItem view);
+ boolean onItemLongClicked(AlarmClockItem item);
+ void onItemNoteClicked(AlarmClockItem item, AlarmListDialogItem view);
+ void onAlarmToggled(AlarmClockItem item, boolean enabled);
+ void onAlarmAdded(AlarmClockItem item);
+ void onAlarmDeleted(long rowID);
+ void onAlarmsCleared();
+ }
+
+}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmTimeDialog.java b/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmTimeDialog.java
index d67d8d715..d1ce82a08 100644
--- a/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmTimeDialog.java
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/alarmclock/ui/AlarmTimeDialog.java
@@ -18,26 +18,33 @@
package com.forrestguice.suntimeswidget.alarmclock.ui;
import android.annotation.SuppressLint;
-import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
-import android.content.DialogInterface;
-import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Typeface;
import android.os.Bundle;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
-import android.support.v7.app.AlertDialog;
-import android.util.Log;
-import android.util.TypedValue;
+import android.support.v4.content.ContextCompat;
+import android.text.SpannableString;
+import android.text.style.CharacterStyle;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import android.widget.ImageView;
import android.widget.Spinner;
+import android.widget.TextView;
import android.widget.TimePicker;
+import android.widget.Toast;
import com.forrestguice.suntimeswidget.R;
+import com.forrestguice.suntimeswidget.SuntimesUtils;
import com.forrestguice.suntimeswidget.alarmclock.AlarmClockItem;
+import com.forrestguice.suntimeswidget.calculator.core.Location;
+import com.forrestguice.suntimeswidget.settings.WidgetSettings;
public class AlarmTimeDialog extends DialogFragment
{
@@ -53,93 +60,78 @@ public class AlarmTimeDialog extends DialogFragment
public static final String PREF_KEY_ALARM_TIME_MODE = "alarmtimezonemode";
public static final AlarmClockItem.AlarmTimeZone PREF_DEF_ALARM_TIME_MODE = AlarmClockItem.AlarmTimeZone.SYSTEM_TIME;
- private TimePicker timePicker;
- private boolean is24 = PREF_DEF_ALARM_TIME_24HR;
- private int hour = PREF_DEF_ALARM_TIME_HOUR;
- private int minute = PREF_DEF_ALARM_TIME_MINUTE;
+ public static final String PREF_KEY_ALARM_LOCATION = "alarmlocation";
+ private TimePicker timePicker;
private Spinner modePicker;
- ArrayAdapter modeAdapter;
- private AlarmClockItem.AlarmTimeZone mode = AlarmClockItem.AlarmTimeZone.SYSTEM_TIME;
+ private TextView locationPicker;
- /**
- * @param savedInstanceState a Bundle containing dialog state
- * @return an Dialog ready to be shown
- */
- @SuppressWarnings({"deprecation","RestrictedApi"})
- @NonNull @Override
- public Dialog onCreateDialog(Bundle savedInstanceState)
+ public AlarmTimeDialog()
{
- super.onCreate(savedInstanceState);
-
- final Activity myParent = getActivity();
- LayoutInflater inflater = myParent.getLayoutInflater();
- @SuppressLint("InflateParams")
- View dialogContent = inflater.inflate(R.layout.layout_dialog_alarmtime, null);
+ super();
+
+ Bundle defaultArgs = new Bundle();
+ defaultArgs.putInt(PREF_KEY_ALARM_TIME_HOUR, PREF_DEF_ALARM_TIME_HOUR);
+ defaultArgs.putInt(PREF_KEY_ALARM_TIME_MINUTE, PREF_DEF_ALARM_TIME_MINUTE);
+ defaultArgs.putBoolean(PREF_KEY_ALARM_TIME_24HR, PREF_DEF_ALARM_TIME_24HR);
+ defaultArgs.putString(PREF_KEY_ALARM_TIME_MODE, PREF_DEF_ALARM_TIME_MODE.timeZoneID());
+ setArguments(defaultArgs);
+ }
- Resources r = getResources();
- int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, r.getDisplayMetrics());
-
- AlertDialog.Builder builder = new AlertDialog.Builder(myParent);
- builder.setView(dialogContent, 0, padding, 0, 0);
- builder.setTitle(myParent.getString(R.string.alarmtime_dialog_title));
-
- AlertDialog dialog = builder.create();
- dialog.setCanceledOnTouchOutside(false);
-
- dialog.setButton(AlertDialog.BUTTON_NEGATIVE, myParent.getString(R.string.alarmtime_dialog_cancel),
- new DialogInterface.OnClickListener()
- {
- @Override
- public void onClick(DialogInterface dialog, int which)
- {
- dialog.dismiss();
- if (onCanceled != null) {
- onCanceled.onClick(dialog, which);
- }
- }
- }
- );
-
- dialog.setButton(AlertDialog.BUTTON_POSITIVE, myParent.getString(R.string.alarmtime_dialog_ok),
- new DialogInterface.OnClickListener()
- {
- @Override
- public void onClick(DialogInterface dialog, int which)
- {
- dialog.dismiss();
- if (onAccepted != null) {
- onAccepted.onClick(dialog, which);
- }
- }
- }
- );
-
- initViews(myParent, dialogContent);
- if (savedInstanceState != null) {
- loadSettings(savedInstanceState);
+ @Override
+ public void onCreate(Bundle savedState)
+ {
+ Bundle args = getArguments();
+ if (getLocation() == null) {
+ args.putParcelable(PREF_KEY_ALARM_LOCATION, WidgetSettings.loadLocationPref(getActivity(), 0));
}
- updateViews(getContext());
- return dialog;
+ super.onCreate(savedState);
+ }
+
+ public void setTime(int hour, int minute) {
+ getArguments().putInt(PREF_KEY_ALARM_TIME_HOUR, hour);
+ getArguments().putInt(PREF_KEY_ALARM_TIME_MINUTE, minute);
+ }
+ public void set24Hour(boolean value) {
+ getArguments().putBoolean(PREF_KEY_ALARM_TIME_24HR, value);
+ }
+ public void setTimeZone(String value) {
+ getArguments().putString(PREF_KEY_ALARM_TIME_MODE, value);
+ }
+ public void setLocation(Location location) {
+ getArguments().putParcelable(PREF_KEY_ALARM_LOCATION, location);
+ }
+
+ @SuppressWarnings({"deprecation","RestrictedApi"})
+ @NonNull @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return super.onCreateDialog(savedInstanceState);
}
+ @SuppressLint("InflateParams")
@Override
- public void onSaveInstanceState( Bundle outState )
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle savedState)
{
- saveSettings(outState);
- super.onSaveInstanceState(outState);
+ super.onCreate(savedState);
+ View dialogContent = inflater.inflate(R.layout.layout_dialog_alarmtime, null);
+ initViews(getActivity(), dialogContent);
+ updateViews(getContext());
+ return dialogContent;
}
protected void initViews( final Context context, View dialogContent )
{
AlarmClockItem.AlarmTimeZone.initDisplayStrings(context);
- modeAdapter = new ArrayAdapter<>(context, R.layout.layout_listitem_oneline, AlarmClockItem.AlarmTimeZone.values());
- modeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ AlarmTimeModeAdapter modeAdapter = new AlarmTimeModeAdapter(context, R.layout.layout_listitem_alarmtz, AlarmClockItem.AlarmTimeZone.values());
+ //modeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
modePicker = (Spinner)dialogContent.findViewById(R.id.modepicker);
modePicker.setAdapter(modeAdapter);
- modePicker.setSelection(modeAdapter.getPosition(this.mode));
+
+ AlarmClockItem.AlarmTimeZone mode = AlarmClockItem.AlarmTimeZone.valueOfID(getArguments().getString(PREF_KEY_ALARM_TIME_MODE));
+ modePicker.setSelection(modeAdapter.getPosition(mode));
timePicker = (TimePicker)dialogContent.findViewById(R.id.timepicker);
+ locationPicker = (TextView) dialogContent.findViewById(R.id.locationPicker);
setTimeChangedListener();
}
@@ -151,6 +143,9 @@ private void setTimeChangedListener()
if (modePicker != null) {
modePicker.setOnItemSelectedListener(onModeChanged);
}
+ if (locationPicker != null) {
+ locationPicker.setOnClickListener(onLocationClicked);
+ }
}
private void clearTimeChangedListener()
{
@@ -160,6 +155,9 @@ private void clearTimeChangedListener()
if (modePicker != null) {
modePicker.setOnItemSelectedListener(null);
}
+ if (locationPicker != null) {
+ locationPicker.setOnClickListener(null);
+ }
}
private TimePicker.OnTimeChangedListener onTimeChanged = new TimePicker.OnTimeChangedListener()
@@ -167,100 +165,150 @@ private void clearTimeChangedListener()
@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute)
{
- AlarmTimeDialog.this.hour = hourOfDay;
- AlarmTimeDialog.this.minute = minute;
+ getArguments().putInt(PREF_KEY_ALARM_TIME_HOUR, hourOfDay);
+ getArguments().putInt(PREF_KEY_ALARM_TIME_MINUTE, minute);
+ if (listener != null) {
+ listener.onChanged(AlarmTimeDialog.this);
+ }
}
};
- private AdapterView.OnItemSelectedListener onModeChanged = new AdapterView.OnItemSelectedListener() {
+ private AdapterView.OnItemSelectedListener onModeChanged = new AdapterView.OnItemSelectedListener()
+ {
@Override
- public void onItemSelected(AdapterView> parent, View view, int position, long id) {
- AlarmTimeDialog.this.mode = (AlarmClockItem.AlarmTimeZone) parent.getItemAtPosition(position);
+ public void onItemSelected(AdapterView> parent, View view, int position, long id)
+ {
+ String timezone = ((AlarmClockItem.AlarmTimeZone) parent.getItemAtPosition(position)).timeZoneID();
+ getArguments().putString(PREF_KEY_ALARM_TIME_MODE, timezone);
+ updateViews(getActivity());
+ if (listener != null) {
+ listener.onChanged(AlarmTimeDialog.this);
+ }
}
-
@Override
public void onNothingSelected(AdapterView> parent) {}
};
- private void updateViews(Context context)
+ private View.OnClickListener onLocationClicked = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (listener != null) {
+ listener.onLocationClick(AlarmTimeDialog.this);
+ }
+ }
+ };
+
+ protected void updateViews(Context context)
{
if (timePicker != null)
{
clearTimeChangedListener();
- timePicker.setIs24HourView(is24);
- timePicker.setCurrentHour(hour);
- timePicker.setCurrentMinute(minute);
+
+ timePicker.setIs24HourView(getArguments().getBoolean(PREF_KEY_ALARM_TIME_24HR));
+ timePicker.setCurrentHour(getArguments().getInt(PREF_KEY_ALARM_TIME_HOUR));
+ timePicker.setCurrentMinute(getArguments().getInt(PREF_KEY_ALARM_TIME_MINUTE));
+
+ locationPicker.setText(displayLocation(getActivity(), getLocation()));
+ locationPicker.setVisibility(getArguments().getString(PREF_KEY_ALARM_TIME_MODE) == null ? View.GONE : View.VISIBLE);
+
setTimeChangedListener();
}
}
- public void setTime(int hour, int minute)
+ public static CharSequence displayLocation(Context context, Location location)
{
- this.hour = hour;
- this.minute = minute;
- }
-
- public void set24Hour(boolean value)
- {
- this.is24 = value;
+ if (location == null) {
+ return "";
+ }
+ String coordString = context.getString(R.string.location_format_latlon, location.getLatitude(), location.getLongitude());
+ String labelString = location.getLabel();
+ String displayString = labelString + "\n" + coordString;
+ SpannableString displayText = SuntimesUtils.createBoldSpan(null, displayString, labelString);
+ return SuntimesUtils.createRelativeSpan(displayText, displayString, coordString, 0.75f);
}
- public int getHour()
- {
- return hour;
+ public int getHour() {
+ return getArguments().getInt(PREF_KEY_ALARM_TIME_HOUR);
}
- public int getMinute()
- {
- return minute;
+ public int getMinute() {
+ return getArguments().getInt(PREF_KEY_ALARM_TIME_MINUTE);
}
public String getTimeZone() {
- return mode.timeZoneID();
- }
- public void setTimeZone( String tzID )
- {
- AlarmClockItem.AlarmTimeZone m = AlarmClockItem.AlarmTimeZone.valueOfID(tzID);
- this.mode = (m != null ? m : PREF_DEF_ALARM_TIME_MODE);
- if (modePicker != null) {
- modePicker.setSelection(modeAdapter.getPosition(this.mode));
- }
+ return getArguments().getString(PREF_KEY_ALARM_TIME_MODE);
}
- protected void loadSettings(Bundle bundle)
- {
- this.is24 = bundle.getBoolean(PREF_KEY_ALARM_TIME_24HR, PREF_DEF_ALARM_TIME_24HR);
- this.hour = bundle.getInt(PREF_KEY_ALARM_TIME_HOUR, PREF_DEF_ALARM_TIME_HOUR);
- this.minute = bundle.getInt(PREF_KEY_ALARM_TIME_MINUTE, PREF_DEF_ALARM_TIME_MINUTE);
-
- String modeString = bundle.getString(PREF_KEY_ALARM_TIME_MODE);
- this.mode = ((modeString != null) ? AlarmClockItem.AlarmTimeZone.valueOf(modeString) : PREF_DEF_ALARM_TIME_MODE);
+ public Location getLocation() {
+ return (Location)getArguments().getParcelable(PREF_KEY_ALARM_LOCATION);
}
- protected void saveSettings(Bundle bundle)
+ public interface DialogListener
{
- bundle.putBoolean(PREF_KEY_ALARM_TIME_24HR, is24);
- bundle.putInt(PREF_KEY_ALARM_TIME_HOUR, hour);
- bundle.putInt(PREF_KEY_ALARM_TIME_MINUTE, minute);
- bundle.putString(PREF_KEY_ALARM_TIME_MODE, mode.name());
+ void onAccepted(AlarmTimeDialog dialog);
+ void onCanceled(AlarmTimeDialog dialog);
+ void onChanged(AlarmTimeDialog dialog);
+ void onLocationClick(AlarmTimeDialog dialog);
}
- /**
- * Dialog accepted listener.
- */
- private DialogInterface.OnClickListener onAccepted = null;
- public void setOnAcceptedListener( DialogInterface.OnClickListener listener )
- {
- onAccepted = listener;
+ private DialogListener listener = null;
+ public void setDialogListener( DialogListener listener ) {
+ this.listener = listener;
}
/**
- * Dialog cancelled listener.
+ * AlarmTimeModeAdapter
*/
- private DialogInterface.OnClickListener onCanceled = null;
- public void setOnCanceledListener( DialogInterface.OnClickListener listener )
+ public static class AlarmTimeModeAdapter extends ArrayAdapter
{
- onCanceled = listener;
+ private int layout;
+ public AlarmTimeModeAdapter(@NonNull Context context, int resource, @NonNull AlarmClockItem.AlarmTimeZone[] objects)
+ {
+ super(context, resource, objects);
+ layout = resource;
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, @NonNull ViewGroup parent) {
+ return createView(position, convertView, parent);
+ }
+ @NonNull @Override
+ public View getView(int position, View convertView, @NonNull ViewGroup parent) {
+ return createView(position, convertView, parent);
+ }
+
+ @SuppressLint("ResourceType")
+ private View createView(int position, View convertView, ViewGroup parent)
+ {
+ View view = convertView;
+ if (view == null) {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ view = inflater.inflate(layout, parent, false);
+ }
+
+ int[] iconAttr = { R.attr.icActionTime };
+ TypedArray typedArray = getContext().obtainStyledAttributes(iconAttr);
+ int res_icon0 = typedArray.getResourceId(0, R.drawable.ic_action_time);
+ typedArray.recycle();
+
+ ImageView icon = (ImageView) view.findViewById(android.R.id.icon1);
+ TextView text = (TextView) view.findViewById(android.R.id.text1);
+
+ AlarmClockItem.AlarmTimeZone item = getItem(position);
+
+ if (text != null) {
+ text.setText(item != null ? item.displayString() : "");
+ }
+
+ if (icon != null)
+ {
+ int resID = (item != null && item.timeZoneID() != null ? R.drawable.ic_sun : res_icon0);
+ icon.setImageDrawable(null);
+ icon.setBackgroundResource(item != null ? resID : 0);
+ }
+
+ return view;
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/calculator/SuntimesMoonData.java b/app/src/main/java/com/forrestguice/suntimeswidget/calculator/SuntimesMoonData.java
index 2159778a6..5787800fd 100644
--- a/app/src/main/java/com/forrestguice/suntimeswidget/calculator/SuntimesMoonData.java
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/calculator/SuntimesMoonData.java
@@ -115,10 +115,20 @@ public double getMoonIlluminationNow()
* result: moon transit time
*/
private Calendar noonToday, noonTomorrow;
- public Calendar getLunarNoonToday()
- {
+ public Calendar getLunarNoonToday() {
return noonToday;
}
+ public Calendar getLunarNoonTomorrow() {
+ return noonTomorrow;
+ }
+
+ private Calendar midnightToday, midnightTomorrow;
+ public Calendar getLunarMidnightToday() {
+ return midnightToday;
+ }
+ public Calendar getLunarMidnightTomorrow() {
+ return midnightTomorrow;
+ }
/**
* result: phase today
@@ -221,23 +231,42 @@ public void calculate()
riseSet[1] = calculator.getMoonTimesForDate(todaysCalendar);
riseSet[2] = calculator.getMoonTimesForDate(otherCalendar);
+ ArrayList midnights = findMidnight();
+ if (midnights.size() >= 1)
+ {
+ midnightToday = midnights.get(midnights.size() - 1);
+ for (Calendar midnight : midnights)
+ {
+ if (midnight.get(Calendar.DAY_OF_YEAR) == todaysCalendar.get(Calendar.DAY_OF_YEAR)) {
+ midnightToday = midnight;
+ }
+ if (midnight.get(Calendar.DAY_OF_YEAR) == otherCalendar.get(Calendar.DAY_OF_YEAR)) {
+ midnightTomorrow = midnight;
+ }
+ }
+ }
+ if (midnightTomorrow == null && midnightToday != null)
+ {
+ midnightTomorrow = (Calendar)midnightToday.clone();
+ midnightTomorrow.add(Calendar.DAY_OF_MONTH, 1);
+ midnightTomorrow.add(Calendar.MINUTE, 50); // approximate noon tomorrow
+ //Log.d("DEBUG", "using approximate lunar noon tomorrow");
+ }
+
ArrayList noons = findNoon();
if (noons.size() >= 1)
{
noonToday = noons.get(noons.size() - 1);
for (Calendar noon : noons)
{
- if (noon.get(Calendar.DAY_OF_YEAR) == todaysCalendar.get(Calendar.DAY_OF_YEAR))
- {
+ if (noon.get(Calendar.DAY_OF_YEAR) == todaysCalendar.get(Calendar.DAY_OF_YEAR)) {
noonToday = noon;
}
- if (noon.get(Calendar.DAY_OF_YEAR) == otherCalendar.get(Calendar.DAY_OF_YEAR))
- {
+ if (noon.get(Calendar.DAY_OF_YEAR) == otherCalendar.get(Calendar.DAY_OF_YEAR)) {
noonTomorrow = noon;
}
}
}
-
if (noonTomorrow == null && noonToday != null)
{
noonTomorrow = (Calendar)noonToday.clone();
@@ -305,6 +334,35 @@ private ArrayList findNoon()
return noon;
}
+ private ArrayList findMidnight()
+ {
+ ArrayList events = new ArrayList<>();
+ for (int i=0; i.
+*/
+
+package com.forrestguice.suntimeswidget.getfix;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.forrestguice.suntimeswidget.calculator.core.Location;
+
+public class PlaceItem implements Parcelable
+{
+ public static final String TAG_DEFAULT = "[default]";
+
+ public long rowID = -1;
+ public Location location = null;
+ public boolean isDefault = false;
+
+ public PlaceItem() {}
+
+ public PlaceItem(long rowID, Location location )
+ {
+ this.rowID = rowID;
+ this.location = location;
+ }
+
+ public PlaceItem( Parcel in )
+ {
+ this.rowID = in.readLong();
+ this.location = in.readParcelable(getClass().getClassLoader());
+ this.isDefault = (in.readInt() == 1);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(rowID);
+ dest.writeParcelable(location, 0);
+ dest.writeInt(isDefault ? 1 : 0);
+ }
+
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator()
+ {
+ public PlaceItem createFromParcel(Parcel in) {
+ return new PlaceItem(in);
+ }
+ public PlaceItem[] newArray(int size)
+ {
+ return new PlaceItem[size];
+ }
+ };
+}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/getfix/PlacesActivity.java b/app/src/main/java/com/forrestguice/suntimeswidget/getfix/PlacesActivity.java
new file mode 100644
index 000000000..9c051448d
--- /dev/null
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/getfix/PlacesActivity.java
@@ -0,0 +1,181 @@
+/**
+ Copyright (C) 2020 Forrest Guice
+ This file is part of SuntimesWidget.
+
+ SuntimesWidget 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.
+
+ SuntimesWidget 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 SuntimesWidget. If not, see .
+*/
+
+package com.forrestguice.suntimeswidget.getfix;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.forrestguice.suntimeswidget.AboutActivity;
+import com.forrestguice.suntimeswidget.R;
+import com.forrestguice.suntimeswidget.SuntimesUtils;
+import com.forrestguice.suntimeswidget.settings.AppSettings;
+import com.forrestguice.suntimeswidget.settings.WidgetSettings;
+
+public class PlacesActivity extends AppCompatActivity
+{
+ public static final String EXTRA_ADAPTER_MODIFIED = "isModified";
+ public static final String EXTRA_ALLOW_PICK = "allowPick";
+ public static final String EXTRA_SELECTED = "selectedRowID";
+ public static final String EXTRA_LOCATION = "selectedLocation";
+
+ protected PlacesListFragment list;
+
+ @Override
+ protected void attachBaseContext(Context newBase)
+ {
+ Context context = AppSettings.initLocale(newBase);
+ super.attachBaseContext(context);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ setTheme(AppSettings.loadTheme(this));
+ super.onCreate(savedInstanceState);
+ setResult(RESULT_CANCELED);
+
+ initLocale();
+ setContentView(R.layout.layout_activity_places);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.app_menubar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null)
+ {
+ actionBar.setHomeButtonEnabled(true);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
+ FragmentManager fragments = getSupportFragmentManager();
+ list = (PlacesListFragment) fragments.findFragmentById(R.id.placesListFragment);
+ list.setFragmentListener(listFragmentListener);
+
+ Intent intent = getIntent();
+ list.setAllowPick(intent.getBooleanExtra(EXTRA_ALLOW_PICK, false));
+ list.setSelectedRowID(intent.getLongExtra(EXTRA_SELECTED, -1));
+ }
+
+ protected void initLocale()
+ {
+ WidgetSettings.initDefaults(this);
+ WidgetSettings.initDisplayStrings(this);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu)
+ {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.placesactivity, menu);
+ return true;
+ }
+
+ @SuppressWarnings("RestrictedApi")
+ @Override
+ protected boolean onPrepareOptionsPanel(View view, Menu menu)
+ {
+ SuntimesUtils.forceActionBarIcons(menu);
+ return super.onPrepareOptionsPanel(view, menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ switch (item.getItemId())
+ {
+ case R.id.action_about:
+ showAbout();
+ return true;
+
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private PlacesListFragment.FragmentListener listFragmentListener = new PlacesListFragment.FragmentListener()
+ {
+ @Override
+ public boolean onItemEdit(PlaceItem item) {
+ //editPlace(item);
+ return false;
+ }
+
+ @Override
+ public void onItemPicked(PlaceItem item) {
+ pickPlace(item);
+ }
+
+ @Override
+ public void onActionModeFinished() {}
+
+ @Override
+ public void onItemClicked(PlaceItem item, int position) { /* EMPTY */ }
+
+ @Override
+ public boolean onItemLongClicked(PlaceItem item, int position) {
+ return false;
+ }
+
+ @Override
+ public void onFilterChanged(String filterText, Long[] filterExceptions) {}
+ };
+
+ protected void pickPlace(PlaceItem item)
+ {
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_SELECTED, item.rowID);
+ intent.putExtra(EXTRA_LOCATION, item.location);
+ intent.putExtra(EXTRA_ADAPTER_MODIFIED, list.isModified());
+ setResult(Activity.RESULT_OK, intent);
+ finish();
+ overridePendingTransition(R.anim.transition_ok_in, R.anim.transition_ok_out);
+ }
+
+ @Override
+ public void onBackPressed()
+ {
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_ADAPTER_MODIFIED, list.isModified());
+ setResult(list.isModified() ? Activity.RESULT_OK : Activity.RESULT_CANCELED, intent);
+ finish();
+ overridePendingTransition(R.anim.transition_cancel_in, R.anim.transition_cancel_out);
+ }
+
+ protected void showAbout()
+ {
+ Intent about = new Intent(this, AboutActivity.class);
+ startActivity(about);
+ overridePendingTransition(R.anim.transition_next_in, R.anim.transition_next_out);
+ }
+}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/getfix/PlacesEditFragment.java b/app/src/main/java/com/forrestguice/suntimeswidget/getfix/PlacesEditFragment.java
new file mode 100644
index 000000000..51c7758f1
--- /dev/null
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/getfix/PlacesEditFragment.java
@@ -0,0 +1,618 @@
+/**
+ Copyright (C) 2020 Forrest Guice
+ This file is part of SuntimesWidget.
+
+ SuntimesWidget 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.
+
+ SuntimesWidget 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 SuntimesWidget. If not, see .
+*/
+package com.forrestguice.suntimeswidget.getfix;
+
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.BottomSheetBehavior;
+import android.support.design.widget.BottomSheetDialog;
+import android.support.design.widget.BottomSheetDialogFragment;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.view.ActionMode;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.forrestguice.suntimeswidget.R;
+import com.forrestguice.suntimeswidget.calculator.core.Location;
+import com.forrestguice.suntimeswidget.settings.AppSettings;
+import com.forrestguice.suntimeswidget.settings.WidgetSettings;
+
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.util.regex.Pattern;
+
+public class PlacesEditFragment extends BottomSheetDialogFragment
+{
+ public static final String KEY_LOCATION = "location";
+ public static final String KEY_LOCATION_LATITUDE = "locationLatitude";
+ public static final String KEY_LOCATION_LONGITUDE = "locationLongitude";
+ public static final String KEY_LOCATION_ALTITUDE = "locationAltitude";
+ public static final String KEY_LOCATION_LABEL = "locationLabel";
+
+ private EditText text_locationAlt;
+ private TextView text_locationAltUnits;
+ private EditText text_locationLat;
+ private EditText text_locationLon;
+ private EditText text_locationName;
+
+ private ImageButton button_getfix;
+ private ProgressBar progress_getfix;
+
+ protected ActionMode actionMode = null;
+ protected PlacesEditActionCompat actions = new PlacesEditActionCompat();
+
+ public PlacesEditFragment()
+ {
+ super();
+ setArguments(new Bundle());
+ }
+
+ private GetFixHelper getFixHelper;
+ private GetFixUI getFixUI_editMode = new GetFixUI()
+ {
+ @Override
+ public void enableUI(boolean value)
+ {
+ text_locationName.requestFocus();
+ text_locationLat.setEnabled(value);
+ text_locationLon.setEnabled(value);
+ text_locationAlt.setEnabled(value);
+ text_locationName.setEnabled(value);
+ }
+
+ @Override
+ public void updateUI(android.location.Location... locations)
+ {
+ DecimalFormat formatter = com.forrestguice.suntimeswidget.calculator.core.Location.decimalDegreesFormatter();
+ text_locationLat.setText( formatter.format(locations[0].getLatitude()) );
+ text_locationLon.setText( formatter.format(locations[0].getLongitude()) );
+ text_locationAlt.setText( altitudeDisplayString(locations[0], formatter, WidgetSettings.loadLengthUnitsPref(getContext(), 0)) );
+ }
+
+ @Override
+ public void showProgress(boolean showProgress) {
+ progress_getfix.setVisibility((showProgress ? View.VISIBLE : View.GONE));
+ }
+
+ @Override
+ public void onStart() {
+ button_getfix.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onResult(android.location.Location result, boolean wasCancelled)
+ {
+ button_getfix.setImageResource((result == null) ? ICON_GPS_SEARCHING : ICON_GPS_FOUND);
+ button_getfix.setVisibility(View.VISIBLE);
+ button_getfix.setEnabled(true);
+ }
+ };
+
+ protected FragmentListener listener;
+ public void setFragmentListener( FragmentListener value ) {
+ listener = value;
+ }
+
+ public interface FragmentListener
+ {
+ void onCanceled(PlaceItem place);
+ void onAccepted(PlaceItem place);
+ }
+
+ private PlaceItem item = null;
+ public void setPlace(PlaceItem item)
+ {
+ this.item = item;
+ updateViews(item.location);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults)
+ {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ getFixHelper.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+
+ @Override
+ public void onStop()
+ {
+ super.onStop();
+ cancelGetFix();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle savedInstanceState)
+ {
+ ContextThemeWrapper contextWrapper = new ContextThemeWrapper(getActivity(), AppSettings.loadTheme(getContext())); // hack: contextWrapper required because base theme is not properly applied
+ View view = inflater.cloneInContext(contextWrapper).inflate(R.layout.layout_dialog_place, parent, false);
+ initViews(getActivity(), view);
+
+ if (savedInstanceState != null) {
+ loadSettings(savedInstanceState);
+
+ } else if (item != null) {
+ setPlace(item);
+
+ } else {
+ updateViews(null);
+ }
+
+ //triggerActionMode(item);
+ return view;
+ }
+
+ protected void initViews(Context context, View content)
+ {
+ WidgetSettings.initDisplayStrings(context);
+
+ text_locationName = (EditText) content.findViewById(R.id.appwidget_location_name);
+ text_locationLat = (EditText) content.findViewById(R.id.appwidget_location_lat);
+ text_locationLon = (EditText) content.findViewById(R.id.appwidget_location_lon);
+ text_locationAlt = (EditText) content.findViewById(R.id.appwidget_location_alt);
+ text_locationAltUnits = (TextView)content.findViewById(R.id.appwidget_location_alt_units);
+
+ ImageButton button_save = (ImageButton) content.findViewById(R.id.save_button);
+ if (button_save != null) {
+ button_save.setOnClickListener(onSaveButtonClicked);
+ }
+
+ ImageButton button_cancel = (ImageButton) content.findViewById(R.id.cancel_button);
+ if (button_cancel != null) {
+ button_cancel.setOnClickListener(onCancelButtonClicked);
+ }
+
+ progress_getfix = (ProgressBar) content.findViewById(R.id.appwidget_location_getfixprogress);
+ progress_getfix.setVisibility(View.GONE);
+
+ button_getfix = (ImageButton) content.findViewById(R.id.appwidget_location_getfix);
+ button_getfix.setOnClickListener(new View.OnClickListener()
+ {
+ @Override
+ public void onClick(View v)
+ {
+ getFixHelper.getFix(0);
+ }
+ });
+
+ getFixHelper = new GetFixHelper(getActivity(), getFixUI_editMode); // 0; getFixUI_editMode
+ updateGPSButtonIcons();
+ }
+
+ /**
+ * @param savedInstanceState a Bundle containing previously saved dialog state
+ * @return an AlertDialog ready for display
+ */
+ @SuppressWarnings({"deprecation","RestrictedApi"})
+ @NonNull @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState)
+ {
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.setOnShowListener(onDialogShow);
+ return dialog;
+ }
+
+ @Override
+ public void onSaveInstanceState( Bundle bundle )
+ {
+ bundle.putParcelable(KEY_LOCATION, item);
+ bundle.putString(KEY_LOCATION_LATITUDE, text_locationLat.getText().toString());
+ bundle.putString(KEY_LOCATION_LONGITUDE, text_locationLon.getText().toString());
+ bundle.putString(KEY_LOCATION_ALTITUDE, text_locationAlt.getText().toString());
+ bundle.putString(KEY_LOCATION_LABEL, text_locationName.getText().toString());
+ getFixHelper.saveSettings(bundle);
+ super.onSaveInstanceState(bundle);
+ }
+
+ protected void loadSettings(Bundle bundle)
+ {
+ item = bundle.getParcelable(KEY_LOCATION);
+ String label = bundle.getString(KEY_LOCATION_LABEL);
+ String longitude = bundle.getString(KEY_LOCATION_LONGITUDE);
+ String latitude = bundle.getString(KEY_LOCATION_LATITUDE);
+ String altitude = bundle.getString(KEY_LOCATION_ALTITUDE);
+
+ if (longitude != null && latitude != null)
+ {
+ com.forrestguice.suntimeswidget.calculator.core.Location location;
+ if (altitude != null)
+ location = new com.forrestguice.suntimeswidget.calculator.core.Location(label, latitude, longitude, altitude);
+ else location = new com.forrestguice.suntimeswidget.calculator.core.Location(label, latitude, longitude);
+ updateViews(location);
+ }
+ getFixHelper.loadSettings(bundle);
+ }
+
+ private DialogInterface.OnShowListener onDialogShow = new DialogInterface.OnShowListener() {
+ @Override
+ public void onShow(DialogInterface dialogInterface) {
+ expandSheet(dialogInterface);
+ disableTouchOutsideBehavior();
+ }
+ };
+
+ @Override
+ public void onCancel(DialogInterface dialog)
+ {
+ cancelGetFix();
+ dismiss();
+ if (actionMode != null) {
+ actionMode.finish();
+ }
+ if (listener != null) {
+ listener.onCanceled(item);
+ }
+ }
+
+ private void expandSheet(DialogInterface dialog)
+ {
+ if (dialog == null) {
+ return;
+ }
+
+ BottomSheetDialog bottomSheet = (BottomSheetDialog) dialog;
+ FrameLayout layout = (FrameLayout) bottomSheet.findViewById(android.support.design.R.id.design_bottom_sheet); // for AndroidX, resource is renamed to com.google.android.material.R.id.design_bottom_sheet
+ if (layout != null)
+ {
+ BottomSheetBehavior behavior = BottomSheetBehavior.from(layout);
+ behavior.setHideable(false);
+ behavior.setSkipCollapsed(true);
+ behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
+ }
+ }
+
+ public void cancelGetFix() {
+ getFixHelper.cancelGetFix();
+ }
+
+ public void updateGPSButtonIcons()
+ {
+ int icon = GetFixUI.ICON_GPS_SEARCHING;
+ if (!getFixHelper.isLocationEnabled(getContext())) {
+ icon = GetFixUI.ICON_GPS_DISABLED;
+
+ } else if (getFixHelper.gotFix) {
+ icon = GetFixUI.ICON_GPS_FOUND;
+ }
+ button_getfix.setImageResource(icon);
+ }
+
+ public static Bundle bundleData( Uri data, String label )
+ {
+ String lat = "";
+ String lon = "";
+ String alt = "";
+
+ if (data != null && "geo".equals(data.getScheme()))
+ {
+ String dataString = data.getSchemeSpecificPart();
+ String[] dataParts = dataString.split(Pattern.quote("?"));
+ if (dataParts.length > 0)
+ {
+ String geoPath = dataParts[0];
+ String[] geoParts = geoPath.split(Pattern.quote(","));
+ if (geoParts.length >= 2)
+ {
+ lat = geoParts[0];
+ lon = geoParts[1];
+
+ if (geoParts.length >= 3)
+ {
+ alt = geoParts[2];
+ }
+ }
+ }
+ }
+
+ Bundle bundle = new Bundle();
+ bundle.putString(KEY_LOCATION_LATITUDE, lat);
+ bundle.putString(KEY_LOCATION_LONGITUDE, lon);
+ bundle.putString(KEY_LOCATION_ALTITUDE, alt);
+ bundle.putString(KEY_LOCATION_LABEL, label);
+ return bundle;
+ }
+
+ @SuppressLint("SetTextI18n")
+ private void updateViews(com.forrestguice.suntimeswidget.calculator.core.Location location)
+ {
+ if (text_locationName == null || text_locationLat == null || text_locationLon == null || text_locationAlt == null) {
+ return;
+ }
+ if (item == null || item.location == null)
+ {
+ text_locationLat.setText("");
+ text_locationLon.setText("");
+ text_locationName.setText("");
+ text_locationAlt.setText("");
+
+ } else {
+ text_locationLat.setText(location.getLatitude());
+ text_locationLon.setText(location.getLongitude());
+ text_locationName.setText(location.getLabel());
+ updateAltitudeField(getActivity(), location);
+ }
+ updateAltitudeLabel(getActivity());
+ }
+
+ private void updateAltitudeField(Context context, Location location)
+ {
+ if (context != null && text_locationAlt != null)
+ {
+ WidgetSettings.LengthUnit units = WidgetSettings.loadLengthUnitsPref(getContext(), 0);
+ switch (units)
+ {
+ case IMPERIAL:
+ case USC: text_locationAlt.setText( Double.toString(WidgetSettings.LengthUnit.metersToFeet(location.getAltitudeAsDouble())) );
+ break;
+
+ case METRIC:
+ default: text_locationAlt.setText(location.getAltitude());
+ break;
+ }
+ }
+ }
+
+ private void updateAltitudeLabel(Context context)
+ {
+ if (context != null && text_locationAltUnits != null)
+ {
+ WidgetSettings.LengthUnit units = WidgetSettings.loadLengthUnitsPref(getContext(), 0);
+ switch (units)
+ {
+ case IMPERIAL:
+ case USC: text_locationAltUnits.setText(context.getString(R.string.units_feet_short));
+ break;
+
+ case METRIC:
+ default: text_locationAltUnits.setText(context.getString(R.string.units_meters));
+ break;
+ }
+ }
+ }
+
+ protected PlaceItem createPlaceItem(PlaceItem item0)
+ {
+ PlaceItem item = new PlaceItem();
+ if (item0 != null)
+ {
+ item.rowID = item0.rowID;
+ item.location = new Location(text_locationName.getText().toString(), text_locationLat.getText().toString(), text_locationLon.getText().toString(), text_locationAlt.getText().toString(),
+ WidgetSettings.loadLengthUnitsPref(getActivity(), 0) == WidgetSettings.LengthUnit.METRIC);
+ item.isDefault = item0.isDefault;
+
+ } else {
+ item.rowID = -1;
+ item.location = new Location(text_locationName.getText().toString(), text_locationLat.getText().toString(), text_locationLon.getText().toString(), text_locationAlt.getText().toString(),
+ WidgetSettings.loadLengthUnitsPref(getActivity(), 0) == WidgetSettings.LengthUnit.METRIC);
+ }
+ return item;
+ }
+
+ private View.OnClickListener onCancelButtonClicked = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ onCancel(getDialog());
+ }
+ };
+
+ private View.OnClickListener onSaveButtonClicked = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ savePlace();
+ }
+ };
+
+ protected void savePlace()
+ {
+ final PlaceItem returnValue = createPlaceItem(item);
+ final boolean validInput = validateInput();
+ if (validInput)
+ {
+ if (listener != null) {
+ listener.onAccepted(returnValue);
+ }
+ }
+
+ final GetFixTask.GetFixTaskListener cancelGetFixListener = new GetFixTask.GetFixTaskListener()
+ {
+ @Override
+ public void onCancelled()
+ {
+ if (validInput)
+ {
+ if (listener != null) {
+ listener.onAccepted(returnValue);
+ }
+ }
+ }
+ };
+ getFixHelper.removeGetFixTaskListener(cancelGetFixListener);
+ getFixHelper.addGetFixTaskListener(cancelGetFixListener);
+ getFixHelper.cancelGetFix();
+ }
+
+ public boolean validateInput()
+ {
+ Context myParent = getActivity();
+ boolean isValid = true;
+
+ String name = text_locationName.getText().toString();
+ if (name.trim().isEmpty())
+ {
+ isValid = false;
+ text_locationName.setError(myParent.getString(R.string.location_dialog_error_name));
+ }
+
+ String latitude = text_locationLat.getText().toString();
+ try {
+ BigDecimal lat = new BigDecimal(latitude);
+ if (lat.doubleValue() < -90d || lat.doubleValue() > 90d)
+ {
+ isValid = false;
+ text_locationLat.setError(myParent.getString(R.string.location_dialog_error_lat));
+ }
+
+ } catch (NumberFormatException e1) {
+ isValid = false;
+ text_locationLat.setError(myParent.getString(R.string.location_dialog_error_lat));
+ }
+
+ String longitude = text_locationLon.getText().toString();
+ try {
+ BigDecimal lon = new BigDecimal(longitude);
+ if (lon.doubleValue() < -180d || lon.doubleValue() > 180d)
+ {
+ isValid = false;
+ text_locationLon.setError(myParent.getString(R.string.location_dialog_error_lon));
+ }
+
+ } catch (NumberFormatException e2) {
+ isValid = false;
+ text_locationLon.setError(myParent.getString(R.string.location_dialog_error_lon));
+ }
+
+ String altitude = text_locationAlt.getText().toString();
+ if (!altitude.trim().isEmpty())
+ {
+ try {
+ BigDecimal alt = new BigDecimal(altitude);
+
+ } catch (NumberFormatException e3) {
+ isValid = false;
+ text_locationAlt.setError(myParent.getString(R.string.location_dialog_error_alt));
+ }
+ }
+
+ return isValid;
+ }
+
+ public static CharSequence altitudeDisplayString(android.location.Location location, DecimalFormat formatter, WidgetSettings.LengthUnit units)
+ {
+ switch (units)
+ {
+ case IMPERIAL:
+ case USC:
+ return formatter.format(WidgetSettings.LengthUnit.metersToFeet(location.getAltitude()));
+
+ case METRIC:
+ default:
+ return formatter.format(location.getAltitude());
+ }
+ }
+
+ private void disableTouchOutsideBehavior()
+ {
+ if (getShowsDialog())
+ {
+ Window window = getDialog().getWindow();
+ if (window != null) {
+ View decorView = window.getDecorView().findViewById(android.support.design.R.id.touch_outside);
+ decorView.setOnClickListener(null);
+ }
+ }
+ }
+
+ /**
+ * triggerActionMode
+ */
+ protected void triggerActionMode(PlaceItem item)
+ {
+ if (actionMode == null)
+ {
+ if (item != null)
+ {
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ actionMode = activity.startSupportActionMode(actions);
+ if (actionMode != null) {
+ updateActionMode(getActivity(), item);
+ }
+ }
+ } else {
+ updateActionMode(getActivity(), item);
+ }
+ }
+
+ protected void updateActionMode(Context context, PlaceItem item)
+ {
+ if (actionMode != null) {
+ actionMode.setTitle(item.location != null ? item.location.getLabel() : "");
+ actionMode.setSubtitle("");
+ } else {
+ triggerActionMode(item);
+ }
+ }
+
+ /**
+ * PlacesEditActionCompat
+ */
+ private class PlacesEditActionCompat implements android.support.v7.view.ActionMode.Callback
+ {
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu)
+ {
+ MenuInflater inflater = mode.getMenuInflater();
+ inflater.inflate(R.menu.placesedit, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem)
+ {
+ switch (menuItem.getItemId())
+ {
+ case R.id.savePlace:
+ savePlace();
+ break;
+ }
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ actionMode = null;
+ }
+ }
+
+}
+
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/getfix/PlacesListFragment.java b/app/src/main/java/com/forrestguice/suntimeswidget/getfix/PlacesListFragment.java
new file mode 100644
index 000000000..6e709787c
--- /dev/null
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/getfix/PlacesListFragment.java
@@ -0,0 +1,1544 @@
+/**
+ Copyright (C) 2014-2020 Forrest Guice
+ This file is part of SuntimesWidget.
+
+ SuntimesWidget 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.
+
+ SuntimesWidget 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 SuntimesWidget. If not, see .
+*/
+
+package com.forrestguice.suntimeswidget.getfix;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.content.FileProvider;
+import android.support.v4.view.MenuItemCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.view.ActionMode;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.SearchView;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Filter;
+import android.widget.Filterable;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.forrestguice.suntimeswidget.R;
+import com.forrestguice.suntimeswidget.SuntimesUtils;
+import com.forrestguice.suntimeswidget.calculator.core.Location;
+import com.forrestguice.suntimeswidget.settings.WidgetSettings;
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
+
+public class PlacesListFragment extends Fragment
+{
+ public static final String KEY_SELECTED_ROWID = "selectedRowID";
+ public static final String KEY_FILTER_TEXT = "filterText";
+ public static final String KEY_FILTER_EXCEPTIONS = "filterExceptions";
+ public static final String KEY_ALLOW_PICK = "allowPick";
+ public static final String KEY_MODIFIED = "isModified";
+
+ public static final String DIALOG_EDITPLACE = "placedialog";
+
+ protected FragmentListener listener;
+ protected PlacesListAdapter adapter;
+ protected RecyclerView listView;
+ protected View emptyView;
+ protected View progressView;
+ protected ActionMode actionMode = null;
+ protected PlacesListActionCompat actions = new PlacesListActionCompat();
+
+ public PlacesListFragment()
+ {
+ super();
+ setArguments(new Bundle());
+ setAllowPick(false);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onResume()
+ {
+ super.onResume();
+
+ FragmentManager fragments = getChildFragmentManager();
+ PlacesEditFragment editDialog = (PlacesEditFragment) fragments.findFragmentByTag(DIALOG_EDITPLACE);
+ if (editDialog != null) {
+ editDialog.setFragmentListener(onEditPlace);
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle savedState)
+ {
+ View dialogContent = inflater.inflate(R.layout.layout_dialog_placeslist, parent, false);
+
+ adapter = new PlacesListAdapter(getActivity());
+ adapter.setFilterText(getFilterText());
+ adapter.setAdapterListener(listAdapterListener);
+
+ listView = (RecyclerView) dialogContent.findViewById(R.id.placesList);
+ listView.setLayoutManager(new LinearLayoutManager(getActivity()));
+ listView.setAdapter(adapter);
+
+ emptyView = dialogContent.findViewById(android.R.id.empty);
+ if (emptyView != null) {
+ emptyView.setVisibility(View.GONE);
+ }
+
+ progressView = dialogContent.findViewById(R.id.progressLayout);
+ if (progressView != null) {
+ progressView.setVisibility(View.GONE);
+ }
+
+ if (savedState != null) {
+ reloadAdapter(listTaskListener(savedState.getLongArray(KEY_SELECTED_ROWID)));
+ } else reloadAdapter();
+
+ return dialogContent;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle state)
+ {
+ state.putLongArray(KEY_SELECTED_ROWID, adapter.getSelectedRowID());
+ super.onSaveInstanceState(state);
+ }
+
+ @Override
+ public void onCreateOptionsMenu (Menu menu, MenuInflater inflater)
+ {
+ inflater.inflate(R.menu.placeslist, menu);
+
+ final MenuItem worldPlacesItem = menu.findItem(R.id.addWorldPlaces);
+ if (worldPlacesItem != null)
+ {
+ if (Build.VERSION.SDK_INT >= 17) {
+ worldPlacesItem.setVisible(true);
+ worldPlacesItem.setEnabled(true);
+ } else {
+ worldPlacesItem.setEnabled(false);
+ worldPlacesItem.setVisible(false); // TODO: legacy support
+ }
+ }
+
+ final MenuItem searchItem = menu.findItem(R.id.searchPlaces);
+ if (searchItem != null)
+ {
+ if (Build.VERSION.SDK_INT >= 11)
+ {
+ MenuItemCompat.setOnActionExpandListener(searchItem, onItemSearchExpand);
+ SearchView searchView = (SearchView) searchItem.getActionView();
+ if (searchView != null)
+ {
+ if (!TextUtils.isEmpty(adapter.getFilterText()))
+ {
+ if (Build.VERSION.SDK_INT >= 14) {
+ searchItem.expandActionView();
+ }
+ searchView.setQuery(adapter.getFilterText(), true);
+ searchView.clearFocus();
+ }
+ searchView.setOnQueryTextListener(onItemSearch);
+ }
+
+ } else {
+ searchItem.setVisible(false); // TODO: legacy support
+ }
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ switch (item.getItemId())
+ {
+ case R.id.addPlace:
+ addPlace(getActivity());
+ return true;
+
+ case R.id.clearPlaces:
+ clearPlaces(getActivity());
+ return true;
+
+ case R.id.exportPlaces:
+ exportPlaces(getActivity());
+ return true;
+
+ case R.id.addWorldPlaces:
+ addWorldPlaces(getActivity());
+ return true;
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ protected void triggerActionMode(PlaceItem... items)
+ {
+ if (actionMode == null)
+ {
+ if (items[0] != null)
+ {
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ actionMode = activity.startSupportActionMode(actions);
+ if (actionMode != null) {
+ updateActionMode(getActivity(), items);
+ }
+ }
+
+ } else {
+ updateActionMode(getActivity(), items);
+ }
+ }
+
+ protected void updateActionMode(Context context, PlaceItem... items)
+ {
+ if (items == null || items.length == 0 ||
+ items[0] == null || items[0].location == null) {
+ return;
+ }
+
+ if (actionMode != null)
+ {
+ long[] rowID = new long[items.length];
+ for (int i=0; i 1)
+ {
+ actionMode.setTitle( context.getString(R.string.configLabel_places_multiSelect, Integer.toString(rowID.length)));
+ actionMode.setSubtitle("");
+
+ } else {
+ actionMode.setTitle(items[0].location.getLabel());
+ actionMode.setSubtitle(locationDisplayString(context, items[0].location, true));
+ }
+ actions.setItems(items);
+ actionMode.invalidate();
+
+ } else {
+ triggerActionMode(items);
+ }
+ }
+
+ protected void finishActionMode()
+ {
+ actionMode.finish();
+ if (listener != null) {
+ listener.onActionModeFinished();
+ }
+ }
+
+ private class PlacesListActionCompat implements android.support.v7.view.ActionMode.Callback
+ {
+ private PlaceItem[] items = null;
+ public void setItems(PlaceItem[] values) {
+ this.items = values;
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu)
+ {
+ MenuInflater inflater = mode.getMenuInflater();
+ inflater.inflate(R.menu.placescontext, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu)
+ {
+ SuntimesUtils.forceActionBarIcons(menu);
+
+ int[] singleSelectItems = new int[] { R.id.pickPlace, R.id.sharePlace, R.id.editPlace, R.id.copyPlace };
+ for (int resID : singleSelectItems)
+ {
+ MenuItem menuItem = menu.findItem(resID);
+ if (menuItem != null) {
+ menuItem.setVisible(items == null || items.length == 1);
+ }
+ }
+
+ MenuItem pickPlace = menu.findItem(R.id.pickPlace);
+ if (pickPlace != null) {
+ pickPlace.setVisible(pickPlace.isVisible() && allowPick());
+ Log.d("DEBUG", "onPrepareActionMode: allowPick: " + allowPick());
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem)
+ {
+ switch (menuItem.getItemId())
+ {
+ case R.id.pickPlace:
+ pickPlace(items[0]);
+ finishActionMode();
+ return true;
+
+ case R.id.editPlace:
+ editPlace(items[0]);
+ return true;
+
+ case R.id.copyPlace:
+ copyPlace(items[0]);
+ return true;
+
+ case R.id.deletePlace:
+ deletePlace(getActivity(), items);
+ return true;
+
+ case R.id.sharePlace:
+ sharePlace(items[0]);
+ return true;
+
+ case android.R.id.home:
+ finishActionMode();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode)
+ {
+ actionMode = null;
+ adapter.setSelectedRowID(-1);
+
+ if (listener != null) {
+ listener.onActionModeFinished();
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public void reloadAdapter() {
+ reloadAdapter(listTaskListener(-1));
+ }
+
+ public void reloadAdapter( PlacesListTask.TaskListener taskListener )
+ {
+ Context context = getActivity();
+ if (context != null)
+ {
+ PlacesListTask listTask = new PlacesListTask(context);
+ listTask.setTaskListener(taskListener);
+ listTask.execute();
+ }
+ }
+
+ protected PlacesListTask.TaskListener listTaskListener(final long... selectedRowID)
+ {
+ return new PlacesListTask.TaskListener() {
+ @Override
+ public void onStarted() {
+ emptyView.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onFinished(List results)
+ {
+ if (emptyView != null) {
+ emptyView.setVisibility(results.isEmpty() ? View.VISIBLE : View.GONE);
+ }
+ listView.setVisibility(results.isEmpty() ? View.GONE : View.VISIBLE);
+
+ adapter.setSelectedRowID(selectedRowID);
+ adapter.setValues(results);
+ adapter.setFilterExceptions(getFilterExceptions());
+ adapter.applyFilter(getFilterText(), false);
+
+ if (selectedRowID != null && selectedRowID.length > 0 && selectedRowID[0] != -1)
+ {
+ listView.scrollToPosition(adapter.indexOf(selectedRowID[0]));
+ triggerActionMode(adapter.getItems(selectedRowID));
+ }
+ }
+ };
+ }
+
+ protected AdapterListener listAdapterListener = new AdapterListener()
+ {
+ @Override
+ public void onItemClicked(PlaceItem item, int position)
+ {
+ triggerActionMode(item);
+ if (listener != null) {
+ listener.onItemClicked(item, position);
+ }
+ }
+
+ @Override
+ public boolean onItemLongClicked(PlaceItem item, int position)
+ {
+ long[] valuesArray = adapter.getSelectedRowID();
+ boolean emptySelection = (valuesArray.length == 1 && valuesArray[0] == -1);
+
+ ArrayList values = new ArrayList<>();
+ for (int i=0; i info = context.getPackageManager().queryIntentActivities(intent, 0);
+ List geoIntents = new ArrayList();
+
+ if (!info.isEmpty())
+ {
+ for (ResolveInfo resolveInfo : info)
+ {
+ if (!TextUtils.equals(resolveInfo.activityInfo.packageName, "com.forrestguice.suntimeswidget"))
+ {
+ Intent geoIntent = new Intent(Intent.ACTION_VIEW);
+ geoIntent.setPackage(resolveInfo.activityInfo.packageName);
+ geoIntent.setData(item.location.getUri());
+ geoIntents.add(geoIntent);
+ }
+ }
+ }
+
+ if (geoIntents.size() > 0)
+ {
+ Intent chooserIntent = Intent.createChooser(geoIntents.remove(0), getString(R.string.configAction_mapLocation_chooser));
+ chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, geoIntents.toArray(new Parcelable[0]));
+ startActivity(chooserIntent);
+
+ } else {
+ Toast.makeText(context, context.getString(R.string.configAction_mapLocation_noapp), Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+
+ protected void addPlace(Context context)
+ {
+ PlacesEditFragment dialog = new PlacesEditFragment();
+ dialog.setFragmentListener(onEditPlace);
+ dialog.show(getChildFragmentManager(), DIALOG_EDITPLACE);
+ }
+
+ protected void copyPlace(@Nullable PlaceItem item)
+ {
+ if (item != null && item.location != null)
+ {
+ Location location = new Location("", item.location.getLatitude(), item.location.getLongitude(), item.location.getAltitude());
+ PlaceItem place = new PlaceItem(-1, location);
+
+ PlacesEditFragment dialog = new PlacesEditFragment();
+ dialog.setFragmentListener(onEditPlace);
+ dialog.setPlace(place);
+ dialog.show(getChildFragmentManager(), DIALOG_EDITPLACE);
+ }
+ }
+
+ protected void editPlace(@Nullable PlaceItem item)
+ {
+ boolean editHandled = false;
+ if (listener != null) {
+ editHandled = listener.onItemEdit(item);
+ }
+
+ if (!editHandled)
+ {
+ Context context = getActivity();
+ if (item != null && item.location != null && context != null)
+ {
+ PlacesEditFragment dialog = new PlacesEditFragment();
+ dialog.setFragmentListener(onEditPlace);
+ dialog.setPlace(item);
+ dialog.show(getChildFragmentManager(), DIALOG_EDITPLACE);
+ }
+ }
+ }
+
+ protected void addOrUpdatePlace(PlaceItem... item)
+ {
+ addOrUpdatePlace(new PlacesListTask.TaskListener()
+ {
+ @Override
+ public void onStarted() {}
+
+ @Override
+ public void onFinished(List results)
+ {
+ if (results.size() > 0)
+ {
+ updateActionMode(getActivity(), results.toArray(new PlaceItem[0]));
+
+ if (adapter.getItemCount() == 0) {
+ reloadAdapter(listTaskListener(results.get(0).rowID));
+
+ } else {
+ adapter.updateValues(results);
+ scrollToSelection();
+ }
+ }
+ dismissEditPlaceDialog();
+ }
+ }, item);
+ }
+
+ protected void addOrUpdatePlace(PlacesListTask.TaskListener listener, PlaceItem... item)
+ {
+ setModified(true);
+ PlacesEditTask task = new PlacesEditTask(getActivity());
+ task.setTaskListener(listener);
+ task.execute(item);
+ }
+
+ protected void scrollToSelection()
+ {
+ LinearLayoutManager layout = (LinearLayoutManager) listView.getLayoutManager();
+ int selected = adapter.getSelectedPosition();
+ int start = layout.findFirstVisibleItemPosition();
+ int end = layout.findLastVisibleItemPosition();
+ if (selected != -1 && (selected <= start || selected >= end)) {
+ listView.smoothScrollToPosition(selected);
+ }
+ }
+
+ private PlacesEditFragment.FragmentListener onEditPlace = new PlacesEditFragment.FragmentListener()
+ {
+ @Override
+ public void onCanceled(PlaceItem item) {
+ }
+
+ @Override
+ public void onAccepted(PlaceItem item) {
+ addOrUpdatePlace(item);
+ }
+ };
+
+ protected void dismissEditPlaceDialog()
+ {
+ FragmentManager fragments = getChildFragmentManager();
+ PlacesEditFragment dialog = (PlacesEditFragment) fragments.findFragmentByTag(DIALOG_EDITPLACE);
+ if (dialog != null) {
+ dialog.dismiss();
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ private SearchView.OnQueryTextListener onItemSearch = new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ return false;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String text) {
+ adapter.applyFilter(text, true);
+ return true;
+ }
+ };
+
+ private MenuItemCompat.OnActionExpandListener onItemSearchExpand = new MenuItemCompat.OnActionExpandListener()
+ {
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ item.setVisible(false);
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem item) {
+ item.setVisible(true);
+ if (Build.VERSION.SDK_INT >= 11) {
+ getActivity().invalidateOptionsMenu();
+ }
+ return true;
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ protected void deletePlace(final Context context, @Nullable final PlaceItem... items)
+ {
+ if (items != null && items.length > 0
+ && items[0] != null && items[0].location != null)
+ {
+ final Long[] rowIDs = new Long[items.length];
+ for (int i=0; i 1);
+ String title = context.getString(multiDelete ? R.string.locationdelete_dialog_title1 : R.string.locationdelete_dialog_title);
+ String desc = (multiDelete ? context.getString(R.string.configLabel_places_multiSelect, Integer.toString(items.length)) : items[0].location.getLabel());
+ String message = context.getString(R.string.locationdelete_dialog_message, desc);
+
+ AlertDialog.Builder confirm = new AlertDialog.Builder(context)
+ .setTitle(title).setMessage(message).setIcon(android.R.drawable.ic_dialog_alert)
+ .setPositiveButton(context.getString(R.string.locationdelete_dialog_ok), onConfirmDeletePlace(context, rowIDs))
+ .setNegativeButton(context.getString(R.string.locationdelete_dialog_cancel), null);
+ confirm.show();
+ }
+ }
+
+ private DialogInterface.OnClickListener onConfirmDeletePlace(final Context context, final Long[] rowIDs)
+ {
+ return new DialogInterface.OnClickListener()
+ {
+ public void onClick(DialogInterface dialog, int whichButton)
+ {
+ DeletePlaceTask task = new DeletePlaceTask(context);
+ task.setTaskListener(new DeletePlaceTask.TaskListener()
+ {
+ @Override
+ public void onFinished(boolean result, Long... rowIDs)
+ {
+ List deletedItems = new ArrayList<>();
+ for (long rowID : rowIDs)
+ {
+ PlaceItem item = adapter.getItem(rowID);
+ if (item != null) {
+ deletedItems.add(item);
+ }
+ adapter.removeItem(rowID);
+ }
+ setModified(true);
+ offerUndoDeletePlace(context, deletedItems.toArray(new PlaceItem[0]));
+ }
+ });
+
+ finishActionMode();
+ task.execute(rowIDs);
+ }
+ };
+ }
+
+ protected void offerUndoDeletePlace(Context context, final PlaceItem... deletedItems)
+ {
+ View view = getView();
+ if (context != null && view != null && deletedItems != null)
+ {
+ final boolean multiDelete = (deletedItems.length > 1);
+ Snackbar snackbar = Snackbar.make(view, multiDelete ? context.getString(R.string.locationdelete_dialog_success1, Integer.toString(deletedItems.length)) : context.getString(R.string.locationdelete_dialog_success), Snackbar.LENGTH_INDEFINITE);
+ snackbar.setAction(context.getString(R.string.configAction_undo), new View.OnClickListener() {
+ @Override
+ public void onClick(View v)
+ {
+ Context context = getActivity();
+ if (context != null) {
+ for (PlaceItem item : deletedItems) {
+ item.rowID = -1; // re-add item
+ }
+ addOrUpdatePlace(deletedItems);
+ }
+ }
+ });
+ SuntimesUtils.themeSnackbar(context, snackbar, null);
+ snackbar.setDuration(UNDO_DELETE_MILLIS);
+ snackbar.show();
+ }
+ }
+ public static final int UNDO_DELETE_MILLIS = 8000;
+
+ public static class DeletePlaceTask extends AsyncTask
+ {
+ private GetFixDatabaseAdapter database;
+ private Long[] rowIDs = new Long[] { -1L };
+
+ public DeletePlaceTask(Context context) {
+ database = new GetFixDatabaseAdapter(context.getApplicationContext());
+ }
+
+ @Override
+ protected Boolean doInBackground(Long... params)
+ {
+ if (params.length > 0) {
+ rowIDs = params;
+ }
+
+ boolean result = false;
+ database.open();
+ for (long rowID : rowIDs)
+ {
+ if (rowID != -1) {
+ result = database.removePlace(rowID);
+ }
+ }
+ database.close();
+ return result;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result)
+ {
+ if (taskListener != null)
+ taskListener.onFinished(result, rowIDs);
+ }
+
+ private TaskListener taskListener = null;
+ public void setTaskListener( TaskListener listener ) {
+ taskListener = listener;
+ }
+ public static abstract class TaskListener
+ {
+ public void onFinished( boolean result, Long... rowIDs ) {}
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public void clearPlaces(final Context context)
+ {
+ AlertDialog.Builder confirm = new AlertDialog.Builder(context)
+ .setTitle(context.getString(R.string.locationclear_dialog_title))
+ .setMessage(context.getString(R.string.locationclear_dialog_message))
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setPositiveButton(context.getString(R.string.locationclear_dialog_ok), new DialogInterface.OnClickListener()
+ {
+ public void onClick(DialogInterface dialog, int whichButton)
+ {
+ BuildPlacesTask task = new BuildPlacesTask(context);
+ task.setTaskListener(clearPlacesListener);
+ task.execute(true); // clearFlag set to true
+ }
+ })
+ .setNegativeButton(context.getString(R.string.locationclear_dialog_cancel), null);
+
+ confirm.show();
+ }
+ private BuildPlacesTask.TaskListener clearPlacesListener = new BuildPlacesTask.TaskListener()
+ {
+ @Override
+ public void onStarted()
+ {
+ setRetainInstance(true);
+ Context context = getActivity();
+ if (context != null) {
+ showProgress(context, context.getString(R.string.locationcleared_dialog_title), context.getString(R.string.locationcleared_dialog_message));
+ }
+ }
+
+ @Override
+ public void onFinished(Integer result)
+ {
+ setModified(true);
+ setRetainInstance(false);
+ dismissProgress();
+
+ Context context = getActivity();
+ if (context != null) {
+ offerUndoClearPlaces(context, adapter.getItems());
+ }
+ reloadAdapter();
+ }
+ };
+ protected void offerUndoClearPlaces(Context context, final PlaceItem... deletedItems)
+ {
+ View view = getView();
+ if (context != null && view != null && deletedItems != null)
+ {
+ Snackbar snackbar = Snackbar.make(view, context.getString(R.string.locationcleared_toast_success), Snackbar.LENGTH_INDEFINITE);
+ snackbar.setAction(context.getString(R.string.configAction_undo), new View.OnClickListener() {
+ @Override
+ public void onClick(View v)
+ {
+ Context context = getActivity();
+ if (context != null) {
+ for (PlaceItem item : deletedItems) {
+ item.rowID = -1; // re-add item
+ }
+ addOrUpdatePlace(new PlacesListTask.TaskListener()
+ {
+ @Override
+ public void onStarted() {}
+
+ @Override
+ public void onFinished(List results)
+ {
+ setSelectedRowID(-1);
+ reloadAdapter();
+ dismissEditPlaceDialog();
+ }
+ }, deletedItems);
+ setSelectedRowID(-1);
+ }
+ }
+ });
+ SuntimesUtils.themeSnackbar(context, snackbar, null);
+ snackbar.setDuration(UNDO_DELETE_MILLIS);
+ snackbar.show();
+ }
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public void exportPlaces(Context context)
+ {
+ ExportPlacesTask task = new ExportPlacesTask(context, "SuntimesPlaces", true, true); // export to external cache
+ task.setTaskListener(exportPlacesListener);
+ task.execute();
+ }
+ private ExportPlacesTask.TaskListener exportPlacesListener = new ExportPlacesTask.TaskListener()
+ {
+ @Override
+ public void onStarted()
+ {
+ setRetainInstance(true);
+ Context context = getActivity();
+ if (context != null) {
+ showProgress(context, context.getString(R.string.locationexport_dialog_title), context.getString(R.string.locationexport_dialog_message));
+ }
+ }
+
+ @Override
+ public void onFinished(ExportPlacesTask.ExportResult results)
+ {
+ setRetainInstance(false);
+ dismissProgress();
+
+ Context context = getActivity();
+ if (context != null)
+ {
+ if (results.getResult())
+ {
+ Intent shareIntent = new Intent();
+ shareIntent.setAction(Intent.ACTION_SEND);
+ shareIntent.setType(results.getMimeType());
+ shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ try {
+ //Uri shareURI = Uri.fromFile(results.getExportFile()); // this URI works until api26 (throws FileUriExposedException)
+ Uri shareURI = FileProvider.getUriForFile(context, "com.forrestguice.suntimeswidget.fileprovider", results.getExportFile());
+ shareIntent.putExtra(Intent.EXTRA_STREAM, shareURI);
+
+ String successMessage = context.getString(R.string.msg_export_success, results.getExportFile().getAbsolutePath());
+ Toast.makeText(context, successMessage, Toast.LENGTH_LONG).show();
+
+ context.startActivity(Intent.createChooser(shareIntent, context.getResources().getText(R.string.msg_export_to)));
+ return; // successful export ends here...
+
+ } catch (Exception e) {
+ Log.e("ExportPlaces", "Failed to share file URI! " + e);
+ }
+
+ }
+
+ File file = results.getExportFile(); // export failed
+ String path = ((file != null) ? file.getAbsolutePath() : "");
+ String failureMessage = context.getString(R.string.msg_export_failure, path);
+ Toast.makeText(context, failureMessage, Toast.LENGTH_LONG).show();
+ }
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public void addWorldPlaces(Context context)
+ {
+ BuildPlacesTask task = new BuildPlacesTask(context);
+ task.setTaskListener(buildPlacesListener);
+ task.execute();
+ }
+ private BuildPlacesTask.TaskListener buildPlacesListener = new BuildPlacesTask.TaskListener()
+ {
+ @Override
+ public void onStarted()
+ {
+ setRetainInstance(true);
+ Context context = getActivity();
+ if (context != null) {
+ showProgress(context, context.getString(R.string.locationbuild_dialog_title), context.getString(R.string.locationbuild_dialog_message));
+ }
+ }
+
+ @Override
+ public void onFinished(Integer result)
+ {
+ setRetainInstance(false);
+ dismissProgress();
+ if (result > 0)
+ {
+ reloadAdapter();
+ Context context = getActivity();
+ if (context != null) {
+ Toast.makeText(context, context.getString(R.string.locationbuild_toast_success, result.toString()), Toast.LENGTH_LONG).show();
+ }
+ } // else // TODO: fail msg
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * PlacesListTask
+ */
+ public static class PlacesListTask extends AsyncTask>
+ {
+ protected GetFixDatabaseAdapter database;
+
+ public PlacesListTask(@NonNull Context context) {
+ database = new GetFixDatabaseAdapter(context.getApplicationContext());
+ }
+
+ @Override
+ protected List doInBackground(PlaceItem... items)
+ {
+ ArrayList result = new ArrayList<>();
+
+ database.open();
+ Cursor cursor = database.getAllPlaces(0, true);
+ if (cursor != null)
+ {
+ cursor.moveToFirst();
+ while (!cursor.isAfterLast())
+ {
+ String name = cursor.getString(cursor.getColumnIndex(GetFixDatabaseAdapter.KEY_PLACE_NAME));
+ String lat = cursor.getString(cursor.getColumnIndex(GetFixDatabaseAdapter.KEY_PLACE_LATITUDE));
+ String lon = cursor.getString(cursor.getColumnIndex(GetFixDatabaseAdapter.KEY_PLACE_LONGITUDE));
+ String alt = cursor.getString(cursor.getColumnIndex(GetFixDatabaseAdapter.KEY_PLACE_ALTITUDE));
+ String comment = cursor.getString(cursor.getColumnIndex(GetFixDatabaseAdapter.KEY_PLACE_COMMENT));
+ Location location = new Location(name, lat, lon, alt);
+ location.setUseAltitude(true);
+
+ PlaceItem item = new PlaceItem(cursor.getLong(cursor.getColumnIndex(GetFixDatabaseAdapter.KEY_ROWID)), location);
+ item.isDefault = (comment != null && comment.contains(PlaceItem.TAG_DEFAULT));
+
+ result.add(item);
+ cursor.moveToNext();
+ }
+ }
+ database.close();
+ return result;
+ }
+
+ @Override
+ protected void onPostExecute(List result)
+ {
+ if (listener != null) {
+ listener.onFinished(result);
+ }
+ }
+
+ @Override
+ protected void onPreExecute()
+ {
+ if (listener != null) {
+ listener.onStarted();
+ }
+ }
+
+ protected TaskListener listener = null;
+ public void setTaskListener(TaskListener listener) {
+ this.listener = listener;
+ }
+
+ public interface TaskListener
+ {
+ void onStarted();
+ void onFinished(List results);
+ }
+ }
+
+ /**
+ * PlacesEditTask
+ */
+ public static class PlacesEditTask extends PlacesListTask
+ {
+ public PlacesEditTask(@NonNull Context context) {
+ super(context);
+ }
+
+ @Override
+ protected List doInBackground(PlaceItem... items)
+ {
+ ArrayList result = new ArrayList<>();
+ database.open();
+ for (PlaceItem item : items)
+ {
+ if (item != null)
+ {
+ if (item.rowID == -1) {
+ item.rowID = database.addPlace(item.location);
+ Log.i(getClass().getSimpleName(), "Added place " + item.rowID);
+
+ } else {
+ database.updatePlace(item.rowID, item.location);
+ Log.i(getClass().getSimpleName(), "Updated place " + item.rowID);
+ }
+ result.add(item);
+ }
+ }
+ database.close();
+ return result;
+ }
+
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public void setSelectedRowID( long... rowID ) {
+ adapter.setSelectedRowID(rowID);
+ }
+ public long[] selectedRowID() {
+ return adapter.getSelectedRowID();
+ }
+
+ public void setFilterText( String value ) {
+ getArguments().putString(KEY_FILTER_TEXT, value);
+ if (adapter != null) {
+ adapter.setFilterText(value);
+ }
+ }
+ public String getFilterText() {
+ String value = getArguments().getString(KEY_FILTER_TEXT);
+ return (value != null ? value : "");
+ }
+ public long[] getFilterExceptions() {
+ return getArguments().getLongArray(KEY_FILTER_EXCEPTIONS);
+ }
+
+ public void setAllowPick(boolean value) {
+ getArguments().putBoolean(KEY_ALLOW_PICK, value);
+ }
+ public boolean allowPick() {
+ return getArguments().getBoolean(KEY_ALLOW_PICK, false);
+ }
+
+ public boolean isModified() {
+ return getArguments().getBoolean(KEY_MODIFIED, false);
+ }
+ protected void setModified(boolean value) {
+ getArguments().putBoolean(KEY_MODIFIED, value);
+ }
+
+ public void setFragmentListener(FragmentListener value) {
+ listener = value;
+ }
+
+ public interface FragmentListener extends AdapterListener
+ {
+ boolean onItemEdit(PlaceItem item);
+ void onItemPicked(PlaceItem item);
+ void onActionModeFinished();
+ }
+
+ public interface AdapterListener {
+ void onItemClicked(PlaceItem item, int position);
+ boolean onItemLongClicked(PlaceItem item, int position);
+ void onFilterChanged(String filterText, Long[] filterExceptions);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public static class PlacesListAdapter extends RecyclerView.Adapter implements Filterable
+ {
+ protected WeakReference contextRef;
+ protected ArrayList items0, items;
+ protected String filterText = "";
+ protected ArrayList filterExceptions;
+
+ public PlacesListAdapter(Context context)
+ {
+ contextRef = new WeakReference<>(context);
+ items0 = new ArrayList<>();
+ items = new ArrayList<>();
+ filterExceptions = new ArrayList<>();
+ }
+
+ public void setValues(List values)
+ {
+ filterExceptions.clear();
+
+ items0.clear();
+ items0.addAll(sortItems(values));
+
+ items.clear();
+ items.addAll(items0);
+
+ notifyDataSetChanged();
+ }
+
+ public void updateValues(List values)
+ {
+ for (PlaceItem value : values)
+ {
+ int position = indexOf(value.rowID, items0);
+ if (position >= 0 && position < items0.size())
+ {
+ items0.set(position, value);
+ } else {
+ items0.add(value);
+ sortItems(items0);
+ }
+ filterExceptions.add(value.rowID);
+ }
+ applyFilter(getFilterText(), false);
+ }
+
+ public int indexOf(long rowID) {
+ return indexOf(rowID, items);
+ }
+ protected static int indexOf(long rowID, List items)
+ {
+ int position = -1;
+ for (int i=0; i= 0) {
+ return items0.get(position);
+ } else return null;
+ }
+
+ public PlaceItem[] getItems() {
+ return items0.toArray(new PlaceItem[0]);
+ }
+
+ public PlaceItem[] getItems(long[] rowID)
+ {
+ PlaceItem[] array = new PlaceItem[rowID.length];
+ for (int i=0; i rowID)
+ {
+ PlaceItem[] array = new PlaceItem[rowID.size()];
+ for (int i=0; i sortItems(List items)
+ {
+ Collections.sort(items, new Comparator() {
+ @Override
+ public int compare(PlaceItem o1, PlaceItem o2)
+ {
+ if ((o1 == null || o1.location == null) && (o2 == null || o2.location == null)) {
+ return 0;
+
+ } else if (o1 == null || o1.location == null) {
+ return -1;
+
+ } else if (o2 == null || o2.location == null) {
+ return 1;
+
+ } else {
+ return o1.location.getLabel().toLowerCase(Locale.ROOT).compareTo(o2.location.getLabel().toLowerCase(Locale.ROOT));
+ }
+ }
+ });
+ return items;
+ }
+
+ private long[] selectedRowID = new long[] { -1 };
+ public void setSelectedRowID( long... rowID )
+ {
+ if (rowID != null)
+ {
+ selectedRowID = new long[rowID.length];
+ System.arraycopy(rowID, 0, selectedRowID, 0, rowID.length);
+ notifyDataSetChanged();
+ }
+ }
+ public long[] getSelectedRowID() {
+ return selectedRowID;
+ }
+ public void clearSelection() {
+ setSelectedRowID(-1);
+ }
+
+ public boolean isSelected(long rowID)
+ {
+ for (long id : selectedRowID) {
+ if (id == rowID) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int getSelectedPosition() {
+ return indexOf(selectedRowID[0]);
+ }
+
+ @Override
+ public PlacesListViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
+ {
+ LayoutInflater layout = LayoutInflater.from(parent.getContext());
+ View view = layout.inflate(R.layout.layout_listitem_places, parent, false);
+ return new PlacesListViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(PlacesListViewHolder holder, int position)
+ {
+ PlaceItem item = items.get(position);
+ holder.selected = isSelected(item.rowID);
+ holder.bindViewHolder(contextRef.get(), item);
+ attachClickListeners(holder);
+ }
+
+ @Override
+ public void onViewRecycled(PlacesListViewHolder holder)
+ {
+ detachClickListeners(holder);
+ holder.unbindViewHolder();
+ }
+
+ @Override
+ public int getItemCount() {
+ return items.size();
+ }
+
+ protected AdapterListener listener = null;
+ public void setAdapterListener(AdapterListener listener) {
+ this.listener = listener;
+ }
+
+ protected void attachClickListeners(PlacesListViewHolder holder)
+ {
+ if (holder.itemView != null) {
+ holder.itemView.setOnClickListener(onItemClicked(holder));
+ holder.itemView.setOnLongClickListener(onItemLongClicked(holder));
+ }
+ }
+
+ protected View.OnClickListener onItemClicked(final PlacesListViewHolder holder)
+ {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ int position = holder.getAdapterPosition();
+ if (listener != null && position >= 0 && position < items.size()) {
+ listener.onItemClicked(items.get(position), position);
+ }
+ }
+ };
+ }
+
+ protected View.OnLongClickListener onItemLongClicked(final PlacesListViewHolder holder)
+ {
+ return new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ int position = holder.getAdapterPosition();
+ if (listener != null && position >= 0 && position < items.size()) {
+ return listener.onItemLongClicked(items.get(position), position);
+ }
+ return false;
+ }
+ };
+ }
+
+ protected void detachClickListeners(PlacesListViewHolder holder)
+ {
+ if (holder.itemView != null) {
+ holder.itemView.setOnClickListener(null);
+ holder.itemView.setOnLongClickListener(null);
+ }
+ }
+
+ public void applyFilter(@Nullable String text, boolean clearExceptions) {
+ filterText = (text != null ? text : "");
+ if (listener != null) {
+ listener.onFilterChanged(filterText, filterExceptions.toArray(new Long[0]));
+ }
+
+ if (clearExceptions) {
+ filterExceptions.clear();
+ }
+ getFilter().filter(filterText);
+ }
+
+ public void setFilterText( String value ) {
+ filterText = value;
+ }
+
+ public String getFilterText() {
+ return filterText;
+ }
+
+ public void setFilterExceptions(long... values)
+ {
+ filterExceptions.clear();
+ if (values != null) {
+ for (Long value : values) {
+ filterExceptions.add(value);
+ }
+ }
+ }
+ public List getFilterExceptions() {
+ return new ArrayList<>(filterExceptions);
+ }
+
+ @Override
+ public Filter getFilter() {
+ return new PlacesFilter();
+ }
+
+ /**
+ * PlacesFilter
+ */
+ private class PlacesFilter extends Filter
+ {
+ @Override
+ protected FilterResults performFiltering(CharSequence constraint)
+ {
+ FilterResults results = new FilterResults();
+ results.values = new ArrayList<>((constraint.length() > 0) ? getFilteredValues(constraint.toString().toLowerCase(Locale.ROOT)) : items0);
+ return results;
+ }
+
+ protected List getFilteredValues(String constraint)
+ {
+ List values0 = new ArrayList<>();
+ List values1 = new ArrayList<>();
+ for (PlaceItem item : items0)
+ {
+ String label = item.location.getLabel().toLowerCase(Locale.ROOT).trim();
+
+ if (label.equals(constraint) || filterExceptions.contains(item.rowID)) {
+ values0.add(0, item);
+
+ } else if (label.startsWith(constraint)) {
+ values0.add(item);
+
+ } else if (label.contains(constraint)) {
+ values1.add(item);
+ }
+ }
+ List values = new ArrayList<>(values0);
+ values.addAll(values1);
+ return values;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void publishResults(CharSequence constraint, FilterResults results)
+ {
+ items.clear();
+ items.addAll((List) results.values);
+ notifyDataSetChanged();
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public static class PlacesListViewHolder extends RecyclerView.ViewHolder
+ {
+ public TextView label;
+ public TextView summary;
+ public ImageView icon_default, icon_userdefined;
+ public boolean selected = false;
+
+ public PlacesListViewHolder(View itemView)
+ {
+ super(itemView);
+ label = (TextView) itemView.findViewById(android.R.id.text1);
+ summary = (TextView) itemView.findViewById(android.R.id.text2);
+ icon_userdefined = (ImageView) itemView.findViewById(R.id.icon1);
+ icon_default = (ImageView) itemView.findViewById(R.id.icon2);
+ }
+
+ public void bindViewHolder(@Nullable Context context, @Nullable PlaceItem item )
+ {
+ this.itemView.setSelected(selected);
+ if (label != null) {
+ label.setText(context == null || item == null || item.location == null ? ""
+ : item.location.getLabel());
+ }
+ if (summary != null) {
+ summary.setText(context == null || item == null || item.location == null ? ""
+ : locationDisplayString(context, item.location, true));
+ }
+
+ if (item != null)
+ {
+ if (icon_default != null) {
+ icon_default.setVisibility(item.isDefault ? View.VISIBLE : View.GONE);
+ }
+ if (icon_userdefined != null) {
+ icon_userdefined.setVisibility(item.isDefault ? View.GONE : View.VISIBLE);
+ }
+ }
+ }
+
+ public void unbindViewHolder() {
+ selected = false;
+ bindViewHolder(null, null);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * locationDisplayString .. "lat, lon [alt]"
+ */
+ public static CharSequence locationDisplayString(@NonNull Context context, @NonNull Location location, boolean showAltitude)
+ {
+ String locationString = context.getString(R.string.location_format_latlon, location.getLatitude(), location.getLongitude());
+ if (showAltitude)
+ {
+ WidgetSettings.LengthUnit units = WidgetSettings.loadLengthUnitsPref(context, 0);
+ SuntimesUtils.TimeDisplayText altitudeText = SuntimesUtils.formatAsHeight(context, location.getAltitudeAsDouble(), units, 0,true);
+ String altitudeString = context.getString(R.string.location_format_alt, altitudeText.getValue(), altitudeText.getUnits());
+ String altitudeTag = context.getString(R.string.location_format_alttag, altitudeString);
+ String displayString = context.getString(R.string.location_format_latlonalt, locationString, altitudeTag);
+ return SuntimesUtils.createRelativeSpan(null, displayString, altitudeTag, 0.75f);
+
+ } else {
+ return locationString;
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/notes/SuntimesNotes.java b/app/src/main/java/com/forrestguice/suntimeswidget/notes/SuntimesNotes.java
index 4ee4875f8..740229aca 100644
--- a/app/src/main/java/com/forrestguice/suntimeswidget/notes/SuntimesNotes.java
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/notes/SuntimesNotes.java
@@ -138,7 +138,7 @@ public void init(Context context, SuntimesRiseSetDataset sundata, SuntimesMoonDa
continue;
else if ((!hasGoldBlue || !enabledBlue) && (event.equals(SolarEvents.EVENING_BLUE8) || event.equals(SolarEvents.MORNING_BLUE8) || event.equals(SolarEvents.EVENING_BLUE4) || event.equals(SolarEvents.MORNING_BLUE4)))
continue;
- else if ((!hasMoon || !enabledMoon) && (event.equals(SolarEvents.MOONRISE) || event.equals(SolarEvents.MOONSET)))
+ else if ((!hasMoon || !enabledMoon) && (event.equals(SolarEvents.MOONRISE) || event.equals(SolarEvents.MOONSET) || event.equals(SolarEvents.MOONNOON) || event.equals(SolarEvents.MOONNIGHT)))
continue;
else if (!enabledNoon && (event.equals(SolarEvents.NOON)))
continue;
@@ -363,6 +363,19 @@ private NoteData createNote(SolarEvents event)
noteString = context.getString(R.string.until_moonset);
break;
+ case MOONNOON:
+ iconStroke = strokeWidthNoon;
+ noteColor = noteColor2 = colorMoonrise;
+ noteString = context.getString(R.string.until_moonnoon);
+ break;
+
+ case MOONNIGHT:
+ iconStroke = strokeWidthNoon;
+ noteColor = colorMoonset;
+ noteColor2 = colorMoonrise;
+ noteString = context.getString(R.string.until_moonnight);
+ break;
+
case MORNING_ASTRONOMICAL:
iconStroke = strokeWidthRising;
noteColor = colorSunrise;
@@ -474,6 +487,8 @@ private String prefixString(SolarEvents event, boolean useSince)
{
case MOONRISE:
case MOONSET:
+ case MOONNOON:
+ case MOONNIGHT:
case MORNING_ASTRONOMICAL: // until
case MORNING_NAUTICAL:
case MORNING_BLUE8:
@@ -524,6 +539,22 @@ private void updateNote(NoteData note, Calendar now)
date = moondata.moonsetCalendarToday();
dateOther = moondata.moonsetCalendarTomorrow();
break;
+
+ case MOONNOON:
+ if (moondata == null) {
+ return;
+ }
+ date = moondata.getLunarNoonToday();
+ dateOther = moondata.getLunarNoonTomorrow();
+ break;
+ case MOONNIGHT:
+ if (moondata == null) {
+ return;
+ }
+ date = moondata.getLunarMidnightToday();
+ dateOther = moondata.getLunarMidnightTomorrow();
+ break;
+
case MORNING_ASTRONOMICAL:
date = dataset.dataAstro.sunriseCalendarToday();
dateOther = dataset.dataAstro.sunriseCalendarOther();
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/settings/ThemePreference.java b/app/src/main/java/com/forrestguice/suntimeswidget/settings/ActionButtonPreference.java
similarity index 68%
rename from app/src/main/java/com/forrestguice/suntimeswidget/settings/ThemePreference.java
rename to app/src/main/java/com/forrestguice/suntimeswidget/settings/ActionButtonPreference.java
index 990d021dc..1dfd2c4e5 100644
--- a/app/src/main/java/com/forrestguice/suntimeswidget/settings/ThemePreference.java
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/settings/ActionButtonPreference.java
@@ -1,5 +1,5 @@
/**
- Copyright (C) 2018 Forrest Guice
+ Copyright (C) 2018-2019 Forrest Guice
This file is part of SuntimesWidget.
SuntimesWidget is free software: you can redistribute it and/or modify
@@ -27,26 +27,26 @@
import com.forrestguice.suntimeswidget.R;
-public class ThemePreference extends ListPreference
+public class ActionButtonPreference extends ListPreference
{
- public ThemePreference(Context context)
+ public ActionButtonPreference(Context context)
{
super(context);
}
- public ThemePreference(Context context, AttributeSet attrs)
+ public ActionButtonPreference(Context context, AttributeSet attrs)
{
super(context, attrs);
}
@TargetApi(21)
- public ThemePreference(Context context, AttributeSet attrs, int defStyleAttr)
+ public ActionButtonPreference(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
}
@TargetApi(21)
- public ThemePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
+ public ActionButtonPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
{
super(context, attrs, defStyleAttr, defStyleRes);
}
@@ -73,24 +73,24 @@ protected void onBindView(View view)
private View.OnClickListener onActionClicked = new View.OnClickListener() {
@Override
public void onClick(View v) {
- if (themePreferenceListener != null) {
- themePreferenceListener.onActionButtonClicked();
+ if (actionButtonPreferenceListener != null) {
+ actionButtonPreferenceListener.onActionButtonClicked();
}
}
};
- private ThemePreferenceListener themePreferenceListener = null;
- public static abstract class ThemePreferenceListener
+ private ActionButtonPreferenceListener actionButtonPreferenceListener = null;
+ public static abstract class ActionButtonPreferenceListener
{
public void onActionButtonClicked() {}
}
/**
- * setThemePreferenceListener
- * @param listener ThemePreferenceListener
+ * setActionButtonPreferenceListener
+ * @param listener ActionButtonPreferenceListener
*/
- public void setThemePreferenceListener( ThemePreferenceListener listener ) {
- themePreferenceListener = listener;
+ public void setActionButtonPreferenceListener(ActionButtonPreferenceListener listener ) {
+ actionButtonPreferenceListener = listener;
}
}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/settings/AppSettings.java b/app/src/main/java/com/forrestguice/suntimeswidget/settings/AppSettings.java
index dbc97544f..132bc0676 100644
--- a/app/src/main/java/com/forrestguice/suntimeswidget/settings/AppSettings.java
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/settings/AppSettings.java
@@ -67,16 +67,16 @@ public class AppSettings
public static final String PREF_DEF_LOCALE = "en";
public static final String PREF_KEY_UI_DATETAPACTION = "app_ui_datetapaction";
- public static final TapAction PREF_DEF_UI_DATETAPACTION = TapAction.SWAP_CARD;
+ public static final String PREF_DEF_UI_DATETAPACTION = WidgetActions.SuntimesAction.SWAP_CARD.name();
public static final String PREF_KEY_UI_DATETAPACTION1 = "app_ui_datetapaction1";
- public static final TapAction PREF_DEF_UI_DATETAPACTION1 = TapAction.SHOW_CALENDAR;
+ public static final String PREF_DEF_UI_DATETAPACTION1 = WidgetActions.SuntimesAction.SHOW_CALENDAR.name();
public static final String PREF_KEY_UI_CLOCKTAPACTION = "app_ui_clocktapaction";
- public static final TapAction PREF_DEF_UI_CLOCKTAPACTION = TapAction.RESET_NOTE;
+ public static final String PREF_DEF_UI_CLOCKTAPACTION = WidgetActions.SuntimesAction.RESET_NOTE.name();
public static final String PREF_KEY_UI_NOTETAPACTION = "app_ui_notetapaction";
- public static final TapAction PREF_DEF_UI_NOTETAPACTION = TapAction.NEXT_NOTE;
+ public static final String PREF_DEF_UI_NOTETAPACTION = WidgetActions.SuntimesAction.NEXT_NOTE.name();
public static final String PREF_KEY_UI_SHOWWARNINGS = "app_ui_showwarnings";
public static final boolean PREF_DEF_UI_SHOWWARNINGS = true;
@@ -292,55 +292,6 @@ public static boolean isLocaleRtl(Context context)
return context.getResources().getBoolean(R.bool.is_rtl);
}
- /**
- * Actions that can be performed when a UI element is clicked.
- */
- public static enum TapAction
- {
- NOTHING("Do Nothing"),
- SWAP_CARD("Swap Cards"),
- SHOW_CALENDAR("Show Calendar"),
- CONFIG_DATE("Set Date"),
- ALARM("Set Alarm"),
- TIMEZONE("Set Time Zone"),
- NEXT_NOTE("Show next note"),
- PREV_NOTE("Show previous note"),
- RESET_NOTE("Show upcoming event");
-
- private String displayString;
-
- private TapAction(String displayString)
- {
- this.displayString = displayString;
- }
-
- public String toString()
- {
- return displayString;
- }
-
- public String getDisplayString()
- {
- return displayString;
- }
-
- public void setDisplayString( String displayString )
- {
- this.displayString = displayString;
- }
-
- public static void initDisplayStrings( Context context )
- {
- TapAction[] actions = TapAction.values();
- String[] labels = context.getResources().getStringArray(R.array.clockTapActions_display);
- for (int i=0; i.
-*/
-
-package com.forrestguice.suntimeswidget.settings;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-
-import android.graphics.Color;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.DialogFragment;
-import android.support.v7.app.AlertDialog;
-
-import com.flask.colorpicker.ColorPickerView;
-import com.flask.colorpicker.OnColorSelectedListener;
-import com.flask.colorpicker.builder.ColorPickerClickListener;
-import com.flask.colorpicker.builder.ColorPickerDialogBuilder;
-import com.forrestguice.suntimeswidget.R;
-
-public class ColorDialog extends DialogFragment
-{
- public ColorDialog() {}
-
- private int color = Color.WHITE;
- public int getColor()
- {
- return color;
- }
- public void setColor( int color )
- {
- this.color = color;
- }
-
- private boolean showAlpha = false;
- public void setShowAlpha(boolean value)
- {
- this.showAlpha = value;
- }
-
- @NonNull
- @Override
- public Dialog onCreateDialog(Bundle savedState)
- {
- super.onCreate(savedState);
- if (savedState != null)
- {
- setColor(savedState.getInt("color", getColor()));
- showAlpha = savedState.getBoolean("showAlpha", showAlpha);
- }
-
- Context context = getContext();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
- {
- ColorPickerDialogBuilder builder = ColorPickerDialogBuilder.with(context)
- .setTitle(context.getString(R.string.color_dialog_msg))
- .initialColor(color)
- .wheelType(ColorPickerView.WHEEL_TYPE.FLOWER)
- .density(12)
- .setOnColorSelectedListener(new OnColorSelectedListener()
- {
- @Override
- public void onColorSelected(int selectedColor)
- {
- setColor(selectedColor);
- }
- })
- .setPositiveButton(context.getString(R.string.color_dialog_ok), new ColorPickerClickListener()
- {
- @Override
- public void onClick(DialogInterface dialog, int selectedColor, Integer[] allColors)
- {
- setColor(selectedColor);
- signalColorChange(getColor());
- }
- })
- .setNegativeButton(context.getString(R.string.color_dialog_cancel), new DialogInterface.OnClickListener()
- {
- @Override
- public void onClick(DialogInterface dialog, int which) {}
- });
-
- builder = (showAlpha ? builder.showLightnessSlider(true).showAlphaSlider(true)
- : builder.lightnessSliderOnly());
-
- return builder.build();
-
- } else {
- AlertDialog alertDialog = new AlertDialog.Builder(context).create();
- alertDialog.setTitle("STUB: TODO");
- alertDialog.setMessage("Not currently supported for api < 14");
- alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- }
- });
- return alertDialog;
- }
- }
-
- @Override
- public void onSaveInstanceState( Bundle outState )
- {
- super.onSaveInstanceState(outState);
- outState.putInt("color", getColor());
- outState.putBoolean("showAlpha", showAlpha);
- }
-
- /**
- * ColorChangeListener
- */
- public static abstract class ColorChangeListener
- {
- public void onColorChanged(int color) {}
- }
- public ColorChangeListener colorChangeListener = null;
- public void setColorChangeListener( ColorChangeListener listener )
- {
- this.colorChangeListener = listener;
- }
-
- private void signalColorChange(int color)
- {
- if (colorChangeListener != null)
- {
- colorChangeListener.onColorChanged(color);
- }
- }
-
-}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/settings/EditBottomSheetDialog.java b/app/src/main/java/com/forrestguice/suntimeswidget/settings/EditBottomSheetDialog.java
new file mode 100644
index 000000000..f00904b4f
--- /dev/null
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/settings/EditBottomSheetDialog.java
@@ -0,0 +1,164 @@
+/**
+ Copyright (C) 2019 Forrest Guice
+ This file is part of SuntimesWidget.
+
+ SuntimesWidget 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.
+
+ SuntimesWidget 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 SuntimesWidget. If not, see .
+*/
+package com.forrestguice.suntimeswidget.settings;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.BottomSheetBehavior;
+import android.support.design.widget.BottomSheetDialog;
+import android.support.design.widget.BottomSheetDialogFragment;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+import com.forrestguice.suntimeswidget.R;
+
+@SuppressWarnings("Convert2Diamond")
+public abstract class EditBottomSheetDialog extends BottomSheetDialogFragment
+{
+ protected abstract int getLayoutID();
+
+ protected Button btn_accept, btn_cancel;
+
+ protected void initViews(Context context, View dialogContent, @Nullable Bundle savedState)
+ {
+ btn_cancel = (Button) dialogContent.findViewById(R.id.dialog_button_cancel);
+ btn_cancel.setOnClickListener(onDialogCancelClick);
+
+ btn_accept = (Button) dialogContent.findViewById(R.id.dialog_button_accept);
+ btn_accept.setOnClickListener(onDialogAcceptClick);
+ }
+
+ protected void updateViews(Context context) {}
+
+ protected boolean validateInput() {
+ return true;
+ }
+ protected void checkInput() {
+ boolean validInput = validateInput();
+ if (btn_accept != null) {
+ btn_accept.setEnabled(validInput);
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle savedState)
+ {
+ ContextThemeWrapper contextWrapper = new ContextThemeWrapper(getActivity(), AppSettings.loadTheme(getContext())); // hack: contextWrapper required because base theme is not properly applied
+ View dialogContent = inflater.cloneInContext(contextWrapper).inflate(getLayoutID(), parent, false);
+ initViews(getContext(), dialogContent, savedState);
+ updateViews(getContext());
+ return dialogContent;
+ }
+
+ @SuppressWarnings({"deprecation","RestrictedApi"})
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState)
+ {
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.setOnShowListener(onDialogShow);
+ return dialog;
+ }
+
+ protected DialogInterface.OnShowListener onShow = null;
+ public void setOnShowListener( DialogInterface.OnShowListener listener ) {
+ onShow = listener;
+ }
+
+ protected DialogInterface.OnClickListener onAccepted = null;
+ public void setOnAcceptedListener( DialogInterface.OnClickListener listener ) {
+ onAccepted = listener;
+ }
+
+ protected DialogInterface.OnClickListener onCanceled = null;
+ public void setOnCanceledListener( DialogInterface.OnClickListener listener ) {
+ onCanceled = listener;
+ }
+
+ @Override
+ public void onResume()
+ {
+ super.onResume();
+ expandSheet(getDialog());
+ }
+
+ protected View.OnClickListener onDialogCancelClick = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ getDialog().cancel();
+ }
+ };
+
+ @Override
+ public void onCancel(DialogInterface dialog)
+ {
+ if (onCanceled != null) {
+ onCanceled.onClick(getDialog(), 0);
+ }
+ }
+
+ private View.OnClickListener onDialogAcceptClick = new View.OnClickListener()
+ {
+ @Override
+ public void onClick(View v)
+ {
+ if (validateInput())
+ {
+ dismiss();
+ if (onAccepted != null) {
+ onAccepted.onClick(getDialog(), 0);
+ }
+ }
+ }
+ };
+
+ protected DialogInterface.OnShowListener onDialogShow = new DialogInterface.OnShowListener()
+ {
+ @Override
+ public void onShow(DialogInterface dialog) {
+ if (onShow != null) {
+ onShow.onShow(dialog);
+ }
+ }
+ };
+
+ protected void expandSheet(DialogInterface dialog)
+ {
+ if (dialog == null) {
+ return;
+ }
+
+ BottomSheetDialog bottomSheet = (BottomSheetDialog) dialog;
+ FrameLayout layout = (FrameLayout) bottomSheet.findViewById(android.support.design.R.id.design_bottom_sheet); // for AndroidX, resource is renamed to com.google.android.material.R.id.design_bottom_sheet
+ if (layout != null)
+ {
+ BottomSheetBehavior behavior = BottomSheetBehavior.from(layout);
+ behavior.setHideable(false);
+ behavior.setSkipCollapsed(true);
+ behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
+ }
+ }
+}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/settings/SolarEventIcons.java b/app/src/main/java/com/forrestguice/suntimeswidget/settings/SolarEventIcons.java
new file mode 100644
index 000000000..d836557d1
--- /dev/null
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/settings/SolarEventIcons.java
@@ -0,0 +1,196 @@
+/**
+ Copyright (C) 2020 Forrest Guice
+ This file is part of SuntimesWidget.
+
+ SuntimesWidget 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.
+
+ SuntimesWidget 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 SuntimesWidget. If not, see .
+*/
+
+package com.forrestguice.suntimeswidget.settings;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.drawable.DrawableCompat;
+
+import com.forrestguice.suntimeswidget.R;
+
+@SuppressWarnings("Convert2Diamond")
+public class SolarEventIcons
+{
+ @SuppressLint("ResourceType")
+ public static int getIconResID(Context context, SolarEvents event)
+ {
+ switch (event)
+ {
+ case MORNING_ASTRONOMICAL: case MORNING_NAUTICAL: case MORNING_BLUE8: case MORNING_CIVIL:
+ case MORNING_BLUE4: case SUNRISE: case MORNING_GOLDEN: case MOONRISE:
+ return R.drawable.svg_sunrise;
+
+ case EVENING_GOLDEN: case SUNSET: case EVENING_BLUE4: case EVENING_CIVIL: case EVENING_BLUE8:
+ case EVENING_NAUTICAL: case EVENING_ASTRONOMICAL: case MOONSET:
+ return R.drawable.svg_sunset;
+
+ case FIRSTQUARTER: return R.drawable.svg_moon_q1;
+ case THIRDQUARTER: return R.drawable.svg_moon_q3;
+
+ case NOON: return getResID(context, R.attr.sunnoonIcon, R.drawable.ic_noon_large);
+ case NEWMOON: return getResID(context, R.attr.moonPhaseIcon0, R.drawable.ic_moon_new);
+ case FULLMOON: return getResID(context, R.attr.moonPhaseIcon2, R.drawable.ic_moon_full);
+
+ case EQUINOX_SPRING: case SOLSTICE_SUMMER: case EQUINOX_AUTUMNAL: case SOLSTICE_WINTER:
+ return R.drawable.svg_season;
+
+ default: return 0;
+ }
+ }
+ public static int getIconResID(Context context, String timezoneID)
+ {
+ if (timezoneID != null && (timezoneID.equals(WidgetTimezones.ApparentSolarTime.TIMEZONEID) || timezoneID.equals(WidgetTimezones.LocalMeanTime.TIMEZONEID))) {
+ return getResID(context, R.attr.sunnoonIcon, R.drawable.ic_noon_large);
+ } else {
+ return getResID(context, R.attr.icActionTime, R.drawable.ic_action_time);
+ }
+ }
+
+ public static float[] getIconScale(SolarEvents event) {
+ return new float[] {1f, 1f};
+ }
+ public static float[] getIconScale(String timezoneID) {
+ return new float[] {1f, 1f};
+ }
+
+ @SuppressLint("ResourceType")
+ public static Integer getIconTint(Context context, SolarEvents event)
+ {
+ switch (event)
+ {
+ case MORNING_ASTRONOMICAL: case MORNING_NAUTICAL:
+ case MORNING_BLUE8: case MORNING_CIVIL:
+ case MORNING_BLUE4: case SUNRISE: case MORNING_GOLDEN:
+ return getColor(context, R.attr.sunriseColor, R.color.sunIcon_color_rising_dark);
+
+ case EVENING_GOLDEN: case SUNSET:
+ case EVENING_BLUE4: case EVENING_CIVIL:
+ case EVENING_BLUE8: case EVENING_NAUTICAL:
+ case EVENING_ASTRONOMICAL:
+ return getColor(context, R.attr.sunsetColor, R.color.sunIcon_color_setting_dark);
+
+ case EQUINOX_SPRING: return getColor(context, R.attr.springColor, R.color.springColor_dark);
+ case SOLSTICE_SUMMER: return getColor(context, R.attr.summerColor, R.color.summerColor_dark);
+ case EQUINOX_AUTUMNAL: return getColor(context, R.attr.fallColor, R.color.fallColor_dark);
+ case SOLSTICE_WINTER: return getColor(context, R.attr.winterColor, R.color.winterColor_dark);
+
+ case MOONRISE: case FIRSTQUARTER: return getColor(context, R.attr.moonriseColor, R.color.moonIcon_color_rising_dark);
+ case MOONSET: case THIRDQUARTER: return getColor(context, R.attr.moonsetColor, R.color.moonIcon_color_setting_dark);
+
+ default: return null;
+ }
+ }
+ public static Integer getIconTint(Context context, String timezoneID) {
+ return null;
+ }
+
+ public static int getIconDrawablePadding(Context context, @NonNull SolarEvents event)
+ {
+ switch (event)
+ {
+ case FIRSTQUARTER: case THIRDQUARTER:
+ case FULLMOON: case NEWMOON: case NOON:
+ return (int)context.getResources().getDimension(R.dimen.eventIcon_margin1);
+ default:
+ return (int)context.getResources().getDimension(R.dimen.eventIcon_margin);
+ }
+ }
+ public static int getIconDrawablePadding(Context context, String timezoneID) {
+ return (int)context.getResources().getDimension(R.dimen.eventIcon_margin1);
+ }
+
+ public static int getIconDrawableInset(Context context, @NonNull SolarEvents event)
+ {
+ switch (event)
+ {
+ case FULLMOON: case NEWMOON: case NOON:
+ return (int)context.getResources().getDimension(R.dimen.eventIcon_margin1);
+ default:
+ return 0;
+ }
+ }
+ public static int getIconDrawableInset(Context context, String timezoneID)
+ {
+ if (timezoneID != null && (timezoneID.equals(WidgetTimezones.ApparentSolarTime.TIMEZONEID) || timezoneID.equals(WidgetTimezones.LocalMeanTime.TIMEZONEID))) {
+ return (int)context.getResources().getDimension(R.dimen.eventIcon_margin1);
+ } else {
+ return 0;
+ }
+ }
+
+ public static Drawable getIconDrawable(Context context, String timezoneID, int width, int height)
+ {
+ return getIconDrawable(context, SolarEventIcons.getIconResID(context, timezoneID), width, height, getIconScale(timezoneID), getIconDrawableInset(context, timezoneID), SolarEventIcons.getIconTint(context, timezoneID));
+ }
+ public static Drawable getIconDrawable(Context context, @NonNull SolarEvents event, int width, int height) {
+ return getIconDrawable(context, SolarEventIcons.getIconResID(context, event), width, height, getIconScale(event), getIconDrawableInset(context, event), SolarEventIcons.getIconTint(context, event));
+ }
+ public static Drawable getIconDrawable(Context context, int resID, int width, int height, float[] scale, int inset, Integer tint)
+ {
+ Drawable eventIcon = ContextCompat.getDrawable(context, resID).mutate();
+ if (tint != null) {
+ tintDrawable(eventIcon, tint);
+ }
+
+ if (inset > 0) {
+ eventIcon = new InsetDrawable(eventIcon, inset, inset, inset, inset);
+ }
+
+ if (width > 0 && height > 0 && scale[0] > 0 && scale[1] > 0) {
+ eventIcon.setBounds(0, 0, (int)(scale[0] * width), (int)(scale[1] * height));
+ }
+
+ return eventIcon;
+ }
+
+ public static int getResID(Context context, int attr, int defResID)
+ {
+ int[] attrs = {attr};
+ TypedArray a = context.obtainStyledAttributes(attrs);
+ int resID = a.getResourceId(0, defResID);
+ a.recycle();
+ return resID;
+ }
+
+ public static int getColor(Context context, int attr, int defColor)
+ {
+ int[] attrs = {attr};
+ TypedArray a = context.obtainStyledAttributes(attrs);
+ int color = ContextCompat.getColor(context, a.getResourceId(0, defColor));
+ a.recycle();
+ return color;
+ }
+
+ public static void tintDrawable(Drawable d, int color)
+ {
+ if (Build.VERSION.SDK_INT >= 21) {
+ DrawableCompat.setTint(d, color);
+ DrawableCompat.setTintMode(d, PorterDuff.Mode.SRC_IN);
+ } else {
+ d.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ }
+ }
+}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/settings/SolarEvents.java b/app/src/main/java/com/forrestguice/suntimeswidget/settings/SolarEvents.java
index 03076eacb..a8b93ffa3 100644
--- a/app/src/main/java/com/forrestguice/suntimeswidget/settings/SolarEvents.java
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/settings/SolarEvents.java
@@ -69,7 +69,10 @@ public enum SolarEvents
EQUINOX_SPRING("equinox", "spring equinox", R.attr.springColor, 3, true), // 21
SOLSTICE_SUMMER("solstice", "summer solstice", R.attr.summerColor, 3, false), // 22
EQUINOX_AUTUMNAL("equinox", "autumnal equinox", R.attr.fallColor, 3, false), // 23
- SOLSTICE_WINTER("solstice", "winter solstice", R.attr.winterColor, 3, true) // 24
+ SOLSTICE_WINTER("solstice", "winter solstice", R.attr.winterColor, 3, true), // 24
+
+ MOONNOON("lunar noon", "lunar noon", R.attr.moonriseIcon, 1, true), // 25
+ MOONNIGHT("lunar midnight", "lunar midnight", R.attr.moonsetIcon, 1, false), // 26
; // .. R.array.solarevents_short/_long req same length/order
private int iconResource;
@@ -78,7 +81,7 @@ public enum SolarEvents
public boolean rising;
public static final int TYPE_SUN = 0; // sunrise, sunset, twilight (converted using toTimeMode)
- public static final int TYPE_MOON = 1; // moonrise, moonset
+ public static final int TYPE_MOON = 1; // moonrise, moonset, lunar noon, lunar midnight
public static final int TYPE_MOONPHASE = 2; // major phases (converted using toMoonPhase)
public static final int TYPE_SEASON = 3; // solstices & equinoxes (converted using toSolsticeEquinoxMode)
@@ -168,8 +171,15 @@ public static SolarEvents valueOf(String value, SolarEvents defaultType)
public static SolarEventsAdapter createAdapter(Context context)
{
- ArrayList choices = new ArrayList();
- choices.addAll(Arrays.asList(SolarEvents.values()));
+ ArrayList choices = new ArrayList(Arrays.asList(
+ MORNING_ASTRONOMICAL, MORNING_NAUTICAL, MORNING_BLUE8, MORNING_CIVIL, MORNING_BLUE4,
+ SUNRISE, MORNING_GOLDEN,
+ NOON, EVENING_GOLDEN,
+ SUNSET, EVENING_BLUE4, EVENING_CIVIL, EVENING_BLUE8, EVENING_NAUTICAL, EVENING_ASTRONOMICAL,
+ MOONRISE, MOONNOON, MOONSET, MOONNIGHT,
+ NEWMOON, FIRSTQUARTER, FULLMOON, THIRDQUARTER,
+ EQUINOX_SPRING, SOLSTICE_SUMMER, EQUINOX_AUTUMNAL, SOLSTICE_WINTER
+ ));
return new SolarEventsAdapter(context, choices);
}
@@ -188,7 +198,7 @@ public SolarEventsAdapter(Context context, ArrayList choices)
this.choices = choices;
}
- static int[] getIconDimen(Resources resources, SolarEvents event)
+ public static int[] getIconDimen(Resources resources, SolarEvents event)
{
int width, height;
switch (event)
@@ -258,7 +268,11 @@ private View alarmItemView(int position, View convertView, @NonNull ViewGroup pa
return view;
}
- private void adjustIcon(int iconResource, ImageView icon, SolarEvents event)
+ public static void adjustIcon(int iconResource, ImageView icon, SolarEvents event)
+ {
+ adjustIcon(iconResource, icon, event, 8);
+ }
+ public static void adjustIcon(int iconResource, ImageView icon, SolarEvents event, int marginDp)
{
Resources resources = icon.getContext().getResources();
int defWidth = (int)resources.getDimension(R.dimen.sunIconLarge_width);
@@ -271,7 +285,7 @@ private void adjustIcon(int iconResource, ImageView icon, SolarEvents event)
if (iconParams instanceof ViewGroup.MarginLayoutParams)
{
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) iconParams;
- float vertMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, resources.getDisplayMetrics());
+ float vertMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, marginDp, resources.getDisplayMetrics());
float horizMargin = (vertMargin + (defWidth - dimen[0])) / 2f;
params.setMargins((int)horizMargin, (int)vertMargin, (int)horizMargin, (int)vertMargin);
}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/settings/WidgetActions.java b/app/src/main/java/com/forrestguice/suntimeswidget/settings/WidgetActions.java
new file mode 100644
index 000000000..d0292842e
--- /dev/null
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/settings/WidgetActions.java
@@ -0,0 +1,713 @@
+/**
+ Copyright (C) 2019 Forrest Guice
+ This file is part of SuntimesWidget.
+
+ SuntimesWidget 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.
+
+ SuntimesWidget 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 SuntimesWidget. If not, see .
+*/
+
+package com.forrestguice.suntimeswidget.settings;
+
+import android.appwidget.AppWidgetManager;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.forrestguice.suntimeswidget.R;
+import com.forrestguice.suntimeswidget.SuntimesActivity;
+import com.forrestguice.suntimeswidget.SuntimesUtils;
+import com.forrestguice.suntimeswidget.SuntimesWidgetListActivity;
+import com.forrestguice.suntimeswidget.actions.ActionListActivity;
+import com.forrestguice.suntimeswidget.alarmclock.ui.AlarmClockActivity;
+import com.forrestguice.suntimeswidget.calculator.SuntimesData;
+import com.forrestguice.suntimeswidget.themes.WidgetThemeListActivity;
+
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * WidgetActions
+ */
+public class WidgetActions
+{
+ public static final String TAG = "WidgetActions";
+
+ public static final String PREFS_WIDGETS = WidgetSettings.PREFS_WIDGET; // Widget related actions are stored with WidgetSettings (where the actionID is either null or 0),
+ public static final String PREFS_ACTIONS = "com.forrestguice.suntimeswidget.actions"; // while the action collection is stored in separate .actions file.
+
+ public static final String PREF_PREFIX_KEY = WidgetSettings.PREF_PREFIX_KEY;
+ public static final String PREF_PREFIX_KEY_ACTION = "_action_";
+
+ public static final String PREF_KEY_ACTION_LAUNCH_TITLE = "title";
+ public static String PREF_DEF_ACTION_LAUNCH_TITLE = "Suntimes";
+
+ public static final String PREF_KEY_ACTION_LAUNCH_DESC = "desc";
+ public static String PREF_DEF_ACTION_LAUNCH_DESC = "";
+
+ public static final String PREF_KEY_ACTION_LAUNCH_COLOR = "color";
+ public static int PREF_DEF_ACTION_LAUNCH_COLOR = Color.WHITE;
+
+ public static final String PREF_KEY_ACTION_LAUNCH_TAGS = "tags";
+ public static final String TAG_DEFAULT = "default";
+ public static final String TAG_SUNTIMES = "suntimes";
+ public static final String TAG_SUNTIMESALARMS = "suntimesalarms";
+
+ public static final String PREF_KEY_ACTION_LAUNCH = "launch";
+ public static final String PREF_DEF_ACTION_LAUNCH = "com.forrestguice.suntimeswidget.SuntimesActivity";
+
+ public static final String PREF_KEY_ACTION_LAUNCH_ACTION = "action";
+ public static final String PREF_DEF_ACTION_LAUNCH_ACTION = "";
+
+ public static final String PREF_KEY_ACTION_LAUNCH_EXTRAS = "extras";
+ public static final String PREF_DEF_ACTION_LAUNCH_EXTRAS = "";
+
+ public static final String PREF_KEY_ACTION_LAUNCH_DATA = "data";
+ public static final String PREF_DEF_ACTION_LAUNCH_DATA = "";
+
+ public static final String PREF_KEY_ACTION_LAUNCH_DATATYPE = "datatype";
+ public static final String PREF_DEF_ACTION_LAUNCH_DATATYPE = "";
+
+ public static final String PREF_KEY_ACTION_LAUNCH_TYPE = "type";
+ public static final LaunchType PREF_DEF_ACTION_LAUNCH_TYPE = LaunchType.ACTIVITY;
+
+ public static final String LAUNCH_TYPE_ACTIVITY = "ACTIVITY";
+ public static final String LAUNCH_TYPE_BROADCAST = "BROADCAST";
+ public static final String LAUNCH_TYPE_SERVICE = "SERVICE";
+
+ public static final String PREF_KEY_ACTION_LAUNCH_LIST = "list";
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * LaunchType
+ */
+ public static enum LaunchType
+ {
+ ACTIVITY("Activity"),
+ BROADCAST("Broadcast"),
+ SERVICE("Service");
+
+ private LaunchType(String displayString)
+ {
+ this.displayString = displayString;
+ }
+
+ private String displayString;
+ public String getDisplayString()
+ {
+ return displayString;
+ }
+ public void setDisplayString(String value)
+ {
+ displayString = value;
+ }
+ public static void initDisplayStrings(Context context)
+ {
+ ACTIVITY.setDisplayString(context.getString(R.string.launchType_activity));
+ BROADCAST.setDisplayString(context.getString(R.string.launchType_broadcast));
+ SERVICE.setDisplayString(context.getString(R.string.launchType_service));
+ }
+ public String toString()
+ {
+ return displayString;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ public static void saveActionLaunchPref(Context context, @Nullable String titleString, @Nullable String descString, @Nullable Integer color, @Nullable String[] tags, int appWidgetId, @Nullable String id, @Nullable String launchString, @Nullable String type, @Nullable String action, @Nullable String dataString, @Nullable String mimeType, @Nullable String extrasString) {
+ saveActionLaunchPref(context, titleString, descString, color, tags, appWidgetId, id, launchString, type, action, dataString, mimeType, extrasString, true);
+ }
+
+ public static void saveActionLaunchPref(Context context, @Nullable String titleString, @Nullable String descString, @Nullable Integer color, @Nullable String[] tags, int appWidgetId, @Nullable String id, @Nullable String launchString, @Nullable String type, @Nullable String action, @Nullable String dataString, @Nullable String mimeType, @Nullable String extrasString, boolean listed)
+ {
+ boolean hasID = true;
+ if (id == null) {
+ id = "0";
+ hasID = false;
+ }
+ if (action != null && action.trim().isEmpty()) {
+ action = null;
+ }
+ if (dataString != null && dataString.trim().isEmpty()) {
+ dataString = null;
+ }
+ if (mimeType != null && mimeType.trim().isEmpty()) {
+ mimeType = null;
+ }
+ if (extrasString != null && extrasString.trim().isEmpty()) {
+ extrasString = null;
+ }
+
+ SharedPreferences.Editor prefs = context.getSharedPreferences(getPrefsId(appWidgetId, id), 0).edit();
+ String prefs_prefix0 = PREF_PREFIX_KEY + appWidgetId + PREF_PREFIX_KEY_ACTION + PREF_KEY_ACTION_LAUNCH + "_" + id + "_";
+
+ prefs.putString(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH, (launchString != null ? launchString : ""));
+ prefs.putString(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_TYPE, (type != null ? type : LAUNCH_TYPE_ACTIVITY));
+ prefs.putString(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_ACTION, (action != null ? action : ""));
+ prefs.putString(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_DATA, (dataString != null ? dataString : ""));
+ prefs.putString(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_DATATYPE, (mimeType != null ? mimeType : ""));
+ prefs.putString(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_EXTRAS, (extrasString != null ? extrasString : ""));
+ prefs.putString(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_TITLE, (titleString != null ? titleString : ""));
+ prefs.putString(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_DESC, (descString != null ? descString : ""));
+ prefs.putInt(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_COLOR, (color != null ? color : PREF_DEF_ACTION_LAUNCH_COLOR));
+
+ Set tagSet = new TreeSet<>();
+ if (tags != null) {
+ for (String tag : tags) {
+ if (!tagSet.contains(tag)) {
+ tagSet.add(tag);
+ }
+ }
+ }
+ putStringSet(prefs, prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_TAGS, tagSet);
+
+ prefs.apply();
+
+ if (hasID && listed)
+ {
+ Set actionList = loadActionLaunchList(context, 0);
+ actionList.add(id);
+ putStringSet(prefs, PREF_PREFIX_KEY + appWidgetId + PREF_PREFIX_KEY_ACTION + PREF_KEY_ACTION_LAUNCH + "_" + PREF_KEY_ACTION_LAUNCH_LIST, actionList);
+ prefs.apply();
+ }
+ }
+
+ public static Set loadActionTags(Context context, int appWidgetId, @Nullable String id)
+ {
+ SharedPreferences prefs = context.getSharedPreferences(PREFS_ACTIONS, 0);
+ String prefs_prefix0 = PREF_PREFIX_KEY + appWidgetId + PREF_PREFIX_KEY_ACTION + PREF_KEY_ACTION_LAUNCH + "_" + id + "_";
+ Set tagList = getStringSet(prefs, prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_TAGS, null);
+ return (tagList != null) ? new TreeSet(tagList) : new TreeSet();
+ }
+ public static Set loadActionLaunchList(Context context, int appWidgetId)
+ {
+ SharedPreferences prefs = context.getSharedPreferences(PREFS_ACTIONS, 0);
+ String listKey = PREF_PREFIX_KEY + appWidgetId + PREF_PREFIX_KEY_ACTION + PREF_KEY_ACTION_LAUNCH + "_" + PREF_KEY_ACTION_LAUNCH_LIST;
+ Set actionList = getStringSet(prefs, listKey, null);
+ return (actionList != null) ? new TreeSet(actionList) : new TreeSet();
+ }
+ public static String loadActionLaunchPref(Context context, int appWidgetId, @Nullable String id, @Nullable String key)
+ {
+ if (id == null) {
+ id = "0";
+ }
+
+ SharedPreferences prefs = context.getSharedPreferences(getPrefsId(appWidgetId, id), 0);
+ String prefs_prefix = PREF_PREFIX_KEY + appWidgetId + PREF_PREFIX_KEY_ACTION + PREF_KEY_ACTION_LAUNCH + "_" + id + "_";
+
+ if (key == null || key.isEmpty())
+ {
+ return prefs.getString(prefs_prefix + PREF_KEY_ACTION_LAUNCH, PREF_DEF_ACTION_LAUNCH);
+
+ } else {
+ switch (key)
+ {
+ case PREF_KEY_ACTION_LAUNCH_TYPE:
+ return prefs.getString(prefs_prefix + PREF_KEY_ACTION_LAUNCH_TYPE, LAUNCH_TYPE_ACTIVITY);
+
+ case PREF_KEY_ACTION_LAUNCH_ACTION:
+ return prefs.getString(prefs_prefix + PREF_KEY_ACTION_LAUNCH_ACTION, "");
+
+ case PREF_KEY_ACTION_LAUNCH_DATA:
+ return prefs.getString(prefs_prefix + PREF_KEY_ACTION_LAUNCH_DATA, "");
+
+ case PREF_KEY_ACTION_LAUNCH_DATATYPE:
+ return prefs.getString(prefs_prefix + PREF_KEY_ACTION_LAUNCH_DATATYPE, "");
+
+ case PREF_KEY_ACTION_LAUNCH_EXTRAS:
+ return prefs.getString(prefs_prefix + PREF_KEY_ACTION_LAUNCH_EXTRAS, "");
+
+ case PREF_KEY_ACTION_LAUNCH_TITLE:
+ return prefs.getString(prefs_prefix + PREF_KEY_ACTION_LAUNCH_TITLE, PREF_DEF_ACTION_LAUNCH_TITLE);
+
+ case PREF_KEY_ACTION_LAUNCH_DESC:
+ return prefs.getString(prefs_prefix + PREF_KEY_ACTION_LAUNCH_DESC, PREF_DEF_ACTION_LAUNCH_DESC);
+
+ case PREF_KEY_ACTION_LAUNCH_COLOR:
+ return Integer.toString(prefs.getInt(prefs_prefix + PREF_KEY_ACTION_LAUNCH_COLOR, PREF_DEF_ACTION_LAUNCH_COLOR));
+ }
+ return null;
+ }
+ }
+ public static void deleteActionLaunchPref(Context context, int appWidgetId, @Nullable String id)
+ {
+ if (id == null) {
+ id = "0";
+ }
+
+ SharedPreferences.Editor prefs = context.getSharedPreferences(getPrefsId(appWidgetId, id), 0).edit();
+ String prefs_prefix0 = PREF_PREFIX_KEY + appWidgetId + PREF_PREFIX_KEY_ACTION + PREF_KEY_ACTION_LAUNCH + "_" + id + "_";
+ prefs.remove(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH );
+ prefs.remove(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_TYPE);
+ prefs.remove(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_ACTION);
+ prefs.remove(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_DATA);
+ prefs.remove(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_DATATYPE);
+ prefs.remove(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_EXTRAS);
+ prefs.remove(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_TITLE);
+ prefs.remove(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_DESC);
+ prefs.remove(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_COLOR);
+ prefs.remove(prefs_prefix0 + PREF_KEY_ACTION_LAUNCH_TAGS);
+ prefs.apply();
+
+ Set actionList = loadActionLaunchList(context, 0);
+ actionList.remove(id);
+ putStringSet(prefs, PREF_PREFIX_KEY + appWidgetId + PREF_PREFIX_KEY_ACTION + PREF_KEY_ACTION_LAUNCH + "_" + PREF_KEY_ACTION_LAUNCH_LIST, actionList);
+ prefs.commit();
+ }
+ public static boolean hasActionLaunchPref(Context context, int appWidgetId, @NonNull String id)
+ {
+ SharedPreferences prefs = context.getSharedPreferences(getPrefsId(appWidgetId, id), 0);
+ String prefs_prefix = PREF_PREFIX_KEY + appWidgetId + PREF_PREFIX_KEY_ACTION + PREF_KEY_ACTION_LAUNCH + "_" + id + "_";
+ return prefs.contains(prefs_prefix + PREF_KEY_ACTION_LAUNCH_TYPE);
+ }
+
+ public static String getPrefsId(int appWidgetId, String actionId)
+ {
+ return (((actionId == null) || actionId.equals("0")) ? PREFS_WIDGETS : PREFS_ACTIONS);
+ }
+
+ public static Set getStringSet(SharedPreferences prefs, String key, @Nullable Set defValues) // TODO: needs test
+ {
+ if (Build.VERSION.SDK_INT >= 11) {
+ return prefs.getStringSet(key, defValues);
+
+ } else {
+ String s = prefs.getString(key, null);
+ return (s != null) ? new TreeSet<>(Arrays.asList(s.split("\\|"))) : null;
+ }
+ }
+
+ public static void putStringSet(SharedPreferences.Editor prefs, String key, @Nullable Set values) // TODO: needs test
+ {
+ if (Build.VERSION.SDK_INT >= 11) {
+ prefs.putStringSet(key, values);
+ prefs.apply();
+
+ } else {
+ if (values != null)
+ {
+ StringBuilder s = new StringBuilder();
+ for (String v : values) {
+ s.append(v).append("|");
+ }
+ prefs.putString(key, s.toString());
+ } else {
+ prefs.putString(key, null);
+ }
+ prefs.apply();
+ }
+ }
+
+ /**
+ * startIntent
+ */
+ public static void startIntent(@NonNull Context context, int appWidgetId, String id, @Nullable SuntimesData data, @Nullable Class fallbackLaunchClass, @Nullable Integer flags)
+ {
+ Intent launchIntent = WidgetActions.createIntent(context, appWidgetId, id, data, fallbackLaunchClass);
+ if (launchIntent != null)
+ {
+ if (flags != null) {
+ launchIntent.setFlags(flags);
+ }
+ String launchType = WidgetActions.loadActionLaunchPref(context, appWidgetId, id, WidgetActions.PREF_KEY_ACTION_LAUNCH_TYPE);
+
+ try {
+ WidgetActions.startIntent(context, launchIntent, launchType);
+
+ } catch (Exception e) {
+ Log.e(TAG, "startIntent: unable to start + " + launchType + " :: " + e);
+ Toast.makeText(context, context.getString(R.string.startaction_failed_toast), Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+ public static void startIntent(@NonNull Context context, @NonNull Intent launchIntent, @Nullable String launchType) throws ActivityNotFoundException, SecurityException
+ {
+ if (launchType != null)
+ {
+ Log.i(TAG, "startIntent :: " + launchType + " :: " + launchIntent.toString());
+ switch (launchType)
+ {
+ case WidgetActions.LAUNCH_TYPE_BROADCAST:
+ context.sendBroadcast(launchIntent);
+ break;
+
+ case WidgetActions.LAUNCH_TYPE_SERVICE:
+ context.startService(launchIntent);
+ break;
+
+ case WidgetActions.LAUNCH_TYPE_ACTIVITY:
+ default:
+ context.startActivity(launchIntent);
+ break;
+ }
+
+ } else {
+ Log.i(TAG, "startIntent :: ACTIVITY :: " + launchIntent.toString());
+ context.startActivity(launchIntent);
+ }
+ }
+
+ /**
+ * createIntent
+ */
+ @Nullable
+ public static Intent createIntent(Context context, int appWidgetId, String id, @Nullable SuntimesData data, @Nullable Class fallbackLaunchClass)
+ {
+ Intent launchIntent;
+ String launchClassName = WidgetActions.loadActionLaunchPref(context, appWidgetId, id, null);
+ String actionString = WidgetActions.loadActionLaunchPref(context, appWidgetId, id, WidgetActions.PREF_KEY_ACTION_LAUNCH_ACTION);
+ String dataString = WidgetActions.loadActionLaunchPref(context, appWidgetId, id, WidgetActions.PREF_KEY_ACTION_LAUNCH_DATA);
+ String mimeType = WidgetActions.loadActionLaunchPref(context, appWidgetId, id, WidgetActions.PREF_KEY_ACTION_LAUNCH_DATATYPE);
+ String extraString = WidgetActions.loadActionLaunchPref(context, appWidgetId, id, WidgetActions.PREF_KEY_ACTION_LAUNCH_EXTRAS);
+
+ if (launchClassName != null && !launchClassName.trim().isEmpty())
+ {
+ Class> launchClass;
+ try {
+ launchClass = Class.forName(launchClassName);
+ launchIntent = new Intent(context, launchClass);
+
+ } catch (ClassNotFoundException e) {
+ Log.e(TAG, "createIntent :: " + launchClassName + " cannot be found! " + e.toString());
+ if (fallbackLaunchClass != null)
+ {
+ launchClass = fallbackLaunchClass;
+ launchIntent = new Intent(context, launchClass);
+ launchIntent.putExtra(WidgetSettings.ActionMode.ONTAP_LAUNCH_CONFIG.name(), true);
+
+ } else {
+ return null;
+ }
+ }
+ } else {
+ launchIntent = new Intent();
+ }
+
+ WidgetActions.applyAction(launchIntent, actionString);
+ WidgetActions.applyData(context, launchIntent, dataString, mimeType, data);
+ WidgetActions.applyExtras(context, launchIntent, extraString, data);
+ launchIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ return launchIntent;
+ }
+
+ /**
+ * applyAction
+ */
+ public static void applyAction(Intent intent, @Nullable String action)
+ {
+ if (intent == null || action == null || action.isEmpty()) {
+ return;
+ }
+ intent.setAction(action);
+ }
+
+ /**
+ * applyData
+ * @param context context
+ * @param intent Intent to apply data to
+ * @param dataString (optional) data string (may contain %substitutions)
+ * @param mimeType (optional) dataString mimeType (text/plain)
+ * @param data (optional) supporting SuntimesData to be applied to %substitutions in the dataString
+ */
+ public static void applyData(Context context, Intent intent, @Nullable String dataString, @Nullable String mimeType, @Nullable SuntimesData data)
+ {
+ //Log.d(TAG, "applyData: " + dataString + " (" + mimeType + ") [" + data + "] to " + intent);
+ if (intent != null && dataString != null && !dataString.trim().isEmpty())
+ {
+ SuntimesUtils utils = new SuntimesUtils();
+ Uri dataUri = Uri.parse(Uri.decode(utils.displayStringForTitlePattern(context, dataString, data)));
+ if (mimeType != null && !mimeType.trim().isEmpty()) {
+ intent.setDataAndType(dataUri, mimeType);
+ } else intent.setData(dataUri);
+ }
+ }
+
+ public static void applyExtras(Context context, Intent intent, @Nullable String extraString, @Nullable SuntimesData data)
+ {
+ if (intent == null || extraString == null || extraString.trim().isEmpty()) {
+ return;
+ }
+
+ SuntimesUtils utils = new SuntimesUtils();
+ String[] extras = extraString.split("&");
+ for (String extra : extras)
+ {
+ String[] pair = extra.split("=");
+ if (pair.length == 2)
+ {
+ String key = pair[0];
+ String value = pair[1];
+
+ char c = value.charAt(0);
+ boolean isNumeric = (c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7'|| c == '8' || c == '9');
+ if (isNumeric)
+ {
+ if (value.endsWith("L") || value.endsWith("l"))
+ {
+ try {
+ intent.putExtra(key, Long.parseLong(value)); // long
+ Log.i(TAG, "applyExtras: applied " + extra + " (long)");
+
+ } catch (NumberFormatException e) {
+ intent.putExtra(key, value); // string
+ Log.w(TAG, "applyExtras: fallback " + extra + " (long)");
+ }
+
+ } else if (value.endsWith("D") || value.endsWith("d")) {
+ try {
+ intent.putExtra(key, Double.parseDouble(value)); // double
+ Log.i(TAG, "applyExtras: applied " + extra + " (double)");
+
+ } catch (NumberFormatException e) {
+ intent.putExtra(key, value); // string
+ Log.w(TAG, "applyExtras: fallback " + extra + " (double)");
+ }
+
+ } else if (value.endsWith("F") || value.endsWith("f")) {
+ try {
+ intent.putExtra(key, Float.parseFloat(value)); // float
+ Log.i(TAG, "applyExtras: applied " + extra + " (float)");
+
+ } catch (NumberFormatException e) {
+ intent.putExtra(key, value); // string
+ Log.w(TAG, "applyExtras: fallback " + extra + " (float)");
+ }
+ }
+
+ } else {
+ String lowerCase = value.toLowerCase(Locale.getDefault());
+ if (lowerCase.equals("true") || lowerCase.equals("false")) {
+ intent.putExtra(key, lowerCase.equals("true")); // boolean
+ Log.i(TAG, "applyExtras: applied " + extra + " (boolean)");
+
+ } else {
+ intent.putExtra(key, utils.displayStringForTitlePattern(context, value, data)); // string (may contain % patterns)
+ Log.i(TAG, "applyExtras: applied " + extra + " (String)");
+ }
+ }
+
+ } else {
+ Log.w(TAG, "applyExtras: skipping " + extra);
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ public static void deletePrefs(Context context)
+ {
+ SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_ACTIONS, 0).edit();
+ prefs.clear();
+ prefs.apply();
+ }
+
+ public static void deletePrefs(Context context, int appWidgetId)
+ {
+ String[] actionList = loadActionLaunchList(context, 0).toArray(new String[0]);
+ for (String action : actionList) {
+ deleteActionLaunchPref(context, appWidgetId, action);
+ }
+ deleteActionLaunchPref(context, appWidgetId, null);
+ }
+
+ public static void initDefaults(Context context)
+ {
+ PREF_DEF_ACTION_LAUNCH_TITLE = context.getString(R.string.app_name);
+ PREF_DEF_ACTION_LAUNCH_DESC = context.getString(R.string.app_shortdesc);
+
+ if (!hasActionLaunchPref(context, 0, "SUNTIMES")) {
+ saveActionLaunchPref(context, WidgetActions.PREF_DEF_ACTION_LAUNCH_TITLE, WidgetActions.PREF_DEF_ACTION_LAUNCH_DESC, null, new String[] {TAG_DEFAULT, TAG_SUNTIMES}, 0, "def_suntimes", WidgetActions.PREF_DEF_ACTION_LAUNCH,
+ WidgetActions.PREF_DEF_ACTION_LAUNCH_TYPE.name(), WidgetActions.PREF_DEF_ACTION_LAUNCH_ACTION, WidgetActions.PREF_DEF_ACTION_LAUNCH_DATA, WidgetActions.PREF_DEF_ACTION_LAUNCH_DATATYPE, WidgetActions.PREF_DEF_ACTION_LAUNCH_EXTRAS);
+ }
+
+ SuntimesAction.initDisplayStrings(context);
+ SuntimesAction.initDefaults(context);
+ }
+
+ public static void initDisplayStrings( Context context ) {
+ LaunchType.initDisplayStrings(context);
+ }
+
+ /**
+ * Actions that can be performed when a UI element is clicked.
+ */
+ public static enum SuntimesAction
+ {
+ NOTHING("Nothing", "Do nothing", new String[] {TAG_DEFAULT}, false),
+
+ ALARM("Suntimes", "Set alarm", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, false),
+
+ CARD_NEXT("Suntimes", "Show next card", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, false),
+ CARD_PREV("Suntimes", "Show previous card", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, false),
+ SWAP_CARD("Suntimes", "Swap cards", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, false),
+
+ NEXT_NOTE("Suntimes", "Show next note", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, false),
+ PREV_NOTE("Suntimes", "Show previous note", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, false),
+ RESET_NOTE("Suntimes", "Show upcoming event", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, false),
+
+ CONFIG_DATE("Suntimes", "Set date", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, true),
+ CONFIG_LOCATION("Suntimes", "Set location", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, true),
+ TIMEZONE("Suntimes", "Set time zone", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, true),
+
+ SHOW_DIALOG_WORLDMAP("Suntimes", "World Map Dialog", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, true),
+ SHOW_DIALOG_SOLSTICE("Suntimes", "Solstices Dialog", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, true),
+ SHOW_DIALOG_MOON("Suntimes", "Moon Dialog", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, true),
+ SHOW_DIALOG_SUN("Suntimes", "Sun Dialog", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, true),
+
+ OPEN_ALARM_LIST("Suntimes Alarms", "Alarm List", new String[] {TAG_DEFAULT, TAG_SUNTIMESALARMS}, true),
+ OPEN_THEME_LIST("Suntimes", "Theme List", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, true),
+ OPEN_ACTION_LIST("Suntimes", "Action List", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, true),
+ OPEN_WIDGET_LIST("Suntimes", "Widget List", new String[] {TAG_DEFAULT, TAG_SUNTIMES}, true),
+
+ SHOW_CALENDAR("Calendar", "Show calendar", new String[] {TAG_DEFAULT}, true),
+ SHOW_MAP("Map", "Show map", new String[] {TAG_DEFAULT}, true);
+
+ private String title, desc;
+ private String[] tags;
+ private boolean listed;
+
+ private SuntimesAction(String title, String desc, String[] tags, boolean listed)
+ {
+ this.title = title;
+ this.desc = desc;
+ this.tags = tags;
+ this.listed = listed;
+ }
+
+ public String toString() {
+ return (desc != null && !desc.trim().isEmpty()) ? desc : title;
+ }
+
+ public String desc() {
+ return desc;
+ }
+ public void setDesc( String desc ) {
+ this.desc = desc;
+ }
+
+ public String title() {
+ return title;
+ }
+ public void setTitle( String title ) {
+ this.title = title;
+ }
+
+ public String[] getTags() {
+ return tags;
+ }
+
+ public boolean listed() {
+ return listed;
+ }
+
+ public static void initDisplayStrings( Context context )
+ {
+ SuntimesAction[] actions = SuntimesAction.values(); // TODO
+ String[] titles = context.getResources().getStringArray(R.array.tapActions_titles);
+ String[] desc = context.getResources().getStringArray(R.array.tapActions_display);
+ for (int i=0; i.
+*/
+
+package com.forrestguice.suntimeswidget.settings.colors;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.View;
+
+import com.forrestguice.suntimeswidget.R;
+import com.forrestguice.suntimeswidget.settings.AppSettings;
+
+/**
+ * This activity can be used to pick a color:
+ * ```
+ * public void showColorPicker()
+ * {
+ * ArrayList recentColors = new ArrayList<>();
+ * recentColors.add(Color.RED);
+ * int color = Color.GREEN;
+ *
+ * Intent intent = new Intent(Intent.ACTION_PICK);
+ * intent.setData(Uri.parse("color://" + String.format("#%08X", color))); // selected color as uri fragment; color://#hexColor
+ *
+ * //intent.putExtra("color", color); // selected color as an int (another way to do same as above)
+ * intent.putExtra("showAlpha", false); // show alpha slider
+ * intent.putExtra("recentColors", recentColors); // show "recent" palette of colors
+ *
+ * startActivityForResult(intent, REQUEST_CODE);
+ * }
+ * ```
+ * ```
+ * public void onActivityResult(int requestCode, int resultCode, Intent data)
+ * {
+ * if (resultCode == RESULT_OK && resultCode == REQUEST_CODE)
+ * {
+ * int color;
+ * Uri uri = data.getData();
+ * if (uri != null)
+ * {
+ * try {
+ * color = Color.parseColor("#" + uri.getFragment());
+ * onColorPicked(color); // do something with returned value
+ *
+ * } catch (IllegalArgumentException e) {
+ * Log.e("onActivityResult", "bad color uri; " + e);
+ * }
+ * }
+ * }
+ * }
+ * ```
+ */
+public class ColorActivity extends AppCompatActivity
+{
+ public static final String SCHEME_COLOR = "color";
+
+ public ColorActivity() {
+ super();
+ }
+
+ @Override
+ protected void attachBaseContext(Context newBase) {
+ super.attachBaseContext(AppSettings.initLocale(newBase));
+ }
+
+ @Override
+ public void onCreate(Bundle icicle)
+ {
+ setTheme(AppSettings.loadTheme(this));
+ super.onCreate(icicle);
+ setResult(RESULT_CANCELED);
+ setContentView(R.layout.layout_activity_colors);
+
+ ColorDialog colorDialog = (ColorDialog) getSupportFragmentManager().findFragmentByTag(ColorChooser.DIALOGTAG_COLOR);
+ if (colorDialog == null)
+ {
+ colorDialog = createColorDialog(getIntent());
+ colorDialog.show(getSupportFragmentManager(), ColorChooser.DIALOGTAG_COLOR);
+ }
+ }
+
+ protected void setBackgroundColor(int color)
+ {
+ View background = findViewById(R.id.layout_background);
+ if (background != null) {
+ background.setBackgroundColor(color);
+ }
+ }
+
+ protected ColorDialog createColorDialog(Intent intent)
+ {
+ ColorDialog colorDialog = new ColorDialog();
+ colorDialog.setRecentColors(intent.getIntegerArrayListExtra(ColorDialog.KEY_RECENT));
+ colorDialog.setShowAlpha(intent.getBooleanExtra(ColorDialog.KEY_SHOWALPHA, false));
+
+ int color = Color.WHITE;
+ Uri data = intent.getData();
+ if (data != null && SCHEME_COLOR.equals(data.getScheme()))
+ {
+ try {
+ color = Color.parseColor("#" + data.getFragment());
+
+ } catch (IllegalArgumentException e) {
+ color = Color.WHITE;
+ Log.e("ColorActivity", e.toString());
+ }
+
+ } else if (intent.hasExtra(ColorDialog.KEY_COLOR)) {
+ color = intent.getIntExtra(ColorDialog.KEY_COLOR, Color.WHITE);
+ }
+
+ colorDialog.setColor(color);
+ colorDialog.setColorDialogListener(dialogListener);
+ return colorDialog;
+ }
+
+ private ColorDialog.ColorDialogListener dialogListener = new ColorDialog.ColorDialogListener()
+ {
+ @Override
+ public void onAccepted(int color) {
+ selectColor(color);
+ }
+
+ @Override
+ public void onColorChanged(int color) {
+ setBackgroundColor(color);
+ }
+
+ @Override
+ public void onCanceled() {
+ onBackPressed();
+ }
+ };
+
+ protected void selectColor( int color )
+ {
+ Intent intent = new Intent();
+ intent.setData(Uri.parse(SCHEME_COLOR + "://" + String.format("#%08X", color)));
+ intent.putExtra(ColorDialog.KEY_COLOR, color);
+ setResult(Activity.RESULT_OK, intent);
+ finish();
+ }
+
+ @Override
+ public void onBackPressed()
+ {
+ setResult(Activity.RESULT_CANCELED, new Intent());
+ finish();
+ }
+
+ @Override
+ public void onResume()
+ {
+ super.onResume();
+ ColorDialog colorDialog = (ColorDialog) getSupportFragmentManager().findFragmentByTag(ColorChooser.DIALOGTAG_COLOR);
+ if (colorDialog != null)
+ {
+ colorDialog.setColorDialogListener(dialogListener);
+ setBackgroundColor(colorDialog.getColor());
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/settings/ColorChooser.java b/app/src/main/java/com/forrestguice/suntimeswidget/settings/colors/ColorChooser.java
similarity index 91%
rename from app/src/main/java/com/forrestguice/suntimeswidget/settings/ColorChooser.java
rename to app/src/main/java/com/forrestguice/suntimeswidget/settings/colors/ColorChooser.java
index 144dfdabf..80fba5c47 100644
--- a/app/src/main/java/com/forrestguice/suntimeswidget/settings/ColorChooser.java
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/settings/colors/ColorChooser.java
@@ -16,7 +16,7 @@
along with SuntimesWidget. If not, see .
*/
-package com.forrestguice.suntimeswidget.settings;
+package com.forrestguice.suntimeswidget.settings.colors;
import android.app.Dialog;
import android.content.Context;
@@ -42,7 +42,7 @@
@SuppressWarnings("Convert2Diamond")
public class ColorChooser implements TextWatcher, View.OnFocusChangeListener
{
- private static final String DIALOGTAG_COLOR = "colorchooser";
+ public static final String DIALOGTAG_COLOR = "colorchooser";
private String chooserID = "0";
final protected ImageButton button;
@@ -139,6 +139,11 @@ public void unlink(ColorChooser chooser)
}
}
+ private ArrayList recentColors;
+ public void setRecentColors(ArrayList colors) {
+ recentColors = colors;
+ }
+
/**
* @return a key that identifies this chooser's value
*/
@@ -327,10 +332,12 @@ public void afterTextChanged(Editable editable)
protected void onColorChanged( int newColor )
{
- for (ColorChooser chooser : getLinked())
- {
+ for (ColorChooser chooser : getLinked()) {
chooser.setColor(newColor);
}
+ if (colorChangeListener != null) {
+ colorChangeListener.onColorChanged(newColor);
+ }
}
protected void onFocusGained(View view)
{
@@ -397,9 +404,10 @@ public void setFragmentManager( FragmentManager manager )
private void showColorPicker(Context context)
{
ColorDialog colorDialog = new ColorDialog();
+ colorDialog.setRecentColors(recentColors);
colorDialog.setShowAlpha(showAlpha);
colorDialog.setColor(getColor());
- colorDialog.setColorChangeListener(colorDialogChangeListener);
+ colorDialog.setColorDialogListener(colorDialogListener);
if (fragmentManager != null)
{
colorDialog.show(fragmentManager, DIALOGTAG_COLOR + "_" + chooserID);
@@ -410,10 +418,10 @@ private void showColorPicker(Context context)
}
}
- private final ColorDialog.ColorChangeListener colorDialogChangeListener = new ColorDialog.ColorChangeListener()
+ private final ColorDialog.ColorDialogListener colorDialogListener = new ColorDialog.ColorDialogListener()
{
@Override
- public void onColorChanged(int color)
+ public void onAccepted(int color)
{
setColor(color);
ColorChooser.this.onColorChanged(getColor());
@@ -427,9 +435,14 @@ public void onResume()
ColorDialog colorDialog = (ColorDialog) fragmentManager.findFragmentByTag(DIALOGTAG_COLOR + "_" + chooserID);
if (colorDialog != null)
{
- colorDialog.setColorChangeListener(colorDialogChangeListener);
+ colorDialog.setColorDialogListener(colorDialogListener);
}
}
}
+ private ColorDialog.ColorChangeListener colorChangeListener = null;
+ public void setColorChangeListener(ColorDialog.ColorChangeListener listener) {
+ colorChangeListener = listener;
+ }
+
}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/settings/ColorChooserView.java b/app/src/main/java/com/forrestguice/suntimeswidget/settings/colors/ColorChooserView.java
similarity index 98%
rename from app/src/main/java/com/forrestguice/suntimeswidget/settings/ColorChooserView.java
rename to app/src/main/java/com/forrestguice/suntimeswidget/settings/colors/ColorChooserView.java
index 02da80c30..f66100b07 100644
--- a/app/src/main/java/com/forrestguice/suntimeswidget/settings/ColorChooserView.java
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/settings/colors/ColorChooserView.java
@@ -15,7 +15,7 @@
You should have received a copy of the GNU General Public License
along with SuntimesWidget. If not, see .
*/
-package com.forrestguice.suntimeswidget.settings;
+package com.forrestguice.suntimeswidget.settings.colors;
import android.content.Context;
import android.content.res.TypedArray;
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/settings/colors/ColorDialog.java b/app/src/main/java/com/forrestguice/suntimeswidget/settings/colors/ColorDialog.java
new file mode 100644
index 000000000..a84d0d4e6
--- /dev/null
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/settings/colors/ColorDialog.java
@@ -0,0 +1,570 @@
+/**
+ Copyright (C) 2017-2020 Forrest Guice
+ This file is part of SuntimesWidget.
+
+ SuntimesWidget 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.
+
+ SuntimesWidget 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 SuntimesWidget. If not, see .
+*/
+
+package com.forrestguice.suntimeswidget.settings.colors;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+
+import android.content.SharedPreferences;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.BottomSheetBehavior;
+import android.support.design.widget.BottomSheetDialog;
+import android.support.design.widget.BottomSheetDialogFragment;
+import android.support.design.widget.TabLayout;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v7.widget.LinearSnapHelper;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.SnapHelper;
+import android.view.ContextThemeWrapper;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+
+import com.flask.colorpicker.ColorPickerView;
+import com.flask.colorpicker.OnColorChangedListener;
+import com.flask.colorpicker.slider.AlphaSlider;
+import com.forrestguice.suntimeswidget.R;
+import com.forrestguice.suntimeswidget.settings.AppSettings;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ColorDialog extends BottomSheetDialogFragment
+{
+ public static final String PREFS_COLORDIALOG = "ColorDialog";
+ public static final String KEY_COLORPICKER = "colorPicker";
+ public static final String KEY_SHOWALPHA = "showAlpha";
+ public static final String KEY_COLOR = "color";
+ public static final String KEY_RECENT = "recentColors";
+
+ public ColorDialog() {}
+
+ private ViewPager colorPager;
+ private TabLayout colorPagerTabs;
+ private ColorPickerPagerAdapter colorPagerAdapter;
+ protected Bundle colorPagerArgs = new Bundle();
+
+ private RecyclerView recentColors;
+ private ColorsAdapter recentColors_adapter;
+
+ public int getColor() {
+ return colorPagerArgs.getInt(KEY_COLOR);
+ }
+ public void setColor( int color ) {
+ colorPagerArgs.putInt(KEY_COLOR, color);
+ if (colorPagerAdapter != null)
+ {
+ colorPagerAdapter.setColor(color);
+ colorPagerAdapter.updateViews(getContext());
+ }
+ }
+
+ public boolean showAlpha() {
+ return colorPagerArgs.getBoolean(KEY_SHOWALPHA, false);
+ }
+ public void setShowAlpha(boolean value) {
+ colorPagerArgs.putBoolean(KEY_SHOWALPHA, value);
+ filterRecentColors();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle savedState)
+ {
+ Context context = getContext();
+ ContextThemeWrapper contextWrapper = new ContextThemeWrapper(getActivity(), AppSettings.loadTheme(context)); // hack: contextWrapper required because base theme is not properly applied
+ View dialogContent = inflater.cloneInContext(contextWrapper).inflate(R.layout.layout_dialog_colors, parent, false);
+
+ if (savedState != null)
+ {
+ setColor(savedState.getInt(KEY_COLOR, getColor()));
+ setShowAlpha(savedState.getBoolean(KEY_SHOWALPHA, showAlpha()));
+ setRecentColors(savedState.getIntegerArrayList(KEY_RECENT));
+ }
+ initViews(getActivity(), dialogContent);
+
+ SharedPreferences prefs = context.getSharedPreferences(PREFS_COLORDIALOG, Context.MODE_PRIVATE);
+ colorPager.setCurrentItem(prefs.getInt(KEY_COLORPICKER, 0));
+
+ return dialogContent;
+ }
+
+ @NonNull @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState)
+ {
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.setCancelable(true);
+ dialog.setCanceledOnTouchOutside(false);
+ dialog.setOnKeyListener(onKeyListener);
+
+ Window window = dialog.getWindow();
+ if (window != null) {
+ window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ }
+
+ return dialog;
+ }
+
+ private DialogInterface.OnKeyListener onKeyListener = new DialogInterface.OnKeyListener()
+ {
+ @Override
+ public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event)
+ {
+ if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE)
+ {
+ getDialog().cancel();
+ if (colorDialogListener != null) {
+ colorDialogListener.onCanceled();
+ }
+ return true;
+ }
+ return false;
+ }
+ };
+
+ @Override
+ public void onSaveInstanceState( Bundle outState )
+ {
+ super.onSaveInstanceState(outState);
+ outState.putInt(KEY_COLOR, getColor());
+ outState.putBoolean(KEY_SHOWALPHA, showAlpha());
+
+ @SuppressWarnings("unchecked")
+ ArrayList colors = (ArrayList)recentColors_list.clone();
+ outState.putIntegerArrayList(KEY_RECENT, colors);
+ }
+
+ private void initViews(Context context, View dialogContent)
+ {
+ colorPagerTabs = (TabLayout) dialogContent.findViewById(R.id.color_pager_tabs);
+ colorPager = (ViewPager) dialogContent.findViewById(R.id.color_pager);
+
+ colorPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
+
+ @Override
+ public void onPageSelected(int position)
+ {
+ Context context = getContext();
+ if (context != null) {
+ SharedPreferences.Editor prefs = context.getSharedPreferences( PREFS_COLORDIALOG, Context.MODE_PRIVATE).edit();
+ prefs.putInt(KEY_COLORPICKER, position);
+ prefs.apply();
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {}
+ });
+ colorPager.setAdapter(colorPagerAdapter = new ColorPickerPagerAdapter(getChildFragmentManager()));
+
+ colorPagerTabs.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(colorPager));
+ colorPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(colorPagerTabs));
+
+ recentColors_adapter = new ColorsAdapter(recentColors_list);
+ recentColors_adapter.setOnColorButtonClickListener(new ColorChangeListener() {
+ @Override
+ public void onColorChanged(int color) {
+ setColor(color);
+ }
+ });
+
+ recentColors = (RecyclerView)dialogContent.findViewById(R.id.color_recent);
+ recentColors.setHasFixedSize(true);
+ recentColors.setItemViewCacheSize(16);
+ recentColors.setAdapter(recentColors_adapter);
+ recentColors.scrollToPosition(0);
+
+ SnapHelper snapHelper = new LinearSnapHelper();
+ snapHelper.attachToRecyclerView(recentColors);
+
+ Button btn_cancel = (Button) dialogContent.findViewById(R.id.dialog_button_cancel);
+ btn_cancel.setOnClickListener(onDialogCancelClick);
+
+ Button btn_accept = (Button) dialogContent.findViewById(R.id.dialog_button_accept);
+ btn_accept.setOnClickListener(onDialogAcceptClick);
+ }
+
+ private View.OnClickListener onDialogCancelClick = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ getDialog().cancel();
+ if (colorDialogListener != null) {
+ colorDialogListener.onCanceled();
+ }
+ }
+ };
+
+ private View.OnClickListener onDialogAcceptClick = new View.OnClickListener()
+ {
+ @Override
+ public void onClick(View v) {
+ dismiss();
+ if (colorDialogListener != null) {
+ colorDialogListener.onAccepted(getColor());
+ }
+ }
+ };
+
+ @Override
+ public void onResume()
+ {
+ super.onResume();
+ expandSheet(getDialog());
+ }
+
+ private void expandSheet(DialogInterface dialog)
+ {
+ if (dialog == null) {
+ return;
+ }
+
+ BottomSheetDialog bottomSheet = (BottomSheetDialog) dialog;
+ FrameLayout layout = (FrameLayout) bottomSheet.findViewById(android.support.design.R.id.design_bottom_sheet); // for AndroidX, resource is renamed to com.google.android.material.R.id.design_bottom_sheet
+ if (layout != null)
+ {
+ BottomSheetBehavior behavior = BottomSheetBehavior.from(layout);
+ behavior.setHideable(false);
+ behavior.setSkipCollapsed(true);
+ behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
+ }
+ }
+
+ private ArrayList recentColors_list = new ArrayList<>();
+ public void setRecentColors(ArrayList colors)
+ {
+ recentColors_list.clear();
+
+ if (colors != null) {
+ recentColors_list.addAll(colors);
+ filterRecentColors();
+ }
+
+ if (recentColors_adapter != null) {
+ recentColors_adapter.setColors(recentColors_list);
+ }
+ }
+
+ private void filterRecentColors()
+ {
+ if (!showAlpha()) {
+ for (int i=recentColors_list.size()-1; i >= 0; i--) {
+ Integer color = recentColors_list.get(i);
+ if (color != null && Color.alpha(color) != 255) {
+ recentColors_list.remove(color);
+ }
+ }
+ }
+ }
+
+ /**
+ * ColorChangeListener
+ */
+ public static abstract class ColorChangeListener {
+ public void onColorChanged(int color) {}
+ }
+
+ /**
+ * ColorDialogListener
+ */
+ public static abstract class ColorDialogListener extends ColorChangeListener
+ {
+ public void onAccepted(int color) {}
+ public void onCanceled() {}
+ }
+ public ColorDialogListener colorDialogListener = null;
+ public void setColorDialogListener( ColorDialogListener listener ) {
+ this.colorDialogListener = listener;
+ }
+
+ /**
+ * ColorsAdapter
+ */
+ public static class ColorsAdapter extends RecyclerView.Adapter
+ {
+ private ArrayList colors = new ArrayList<>();
+
+ public ColorsAdapter(List colors) {
+ this.colors.addAll(colors);
+ }
+
+ public void setColors(List colors)
+ {
+ this.colors.clear();
+ this.colors.addAll(colors);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public ColorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater layout = LayoutInflater.from(parent.getContext());
+ View view = layout.inflate(R.layout.layout_listitem_color, parent, false);
+ return new ColorViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ColorViewHolder holder, int position)
+ {
+ Integer color = (position >= 0 && position < colors.size()) ? colors.get(position) : null;
+ holder.bindColorToView(color);
+ holder.colorButton.setOnClickListener(color != null ? onColorButtonClick(color) : null);
+ }
+
+ @Override
+ public int getItemCount() {
+ return colors.size();
+ }
+
+
+ private View.OnClickListener onColorButtonClick(final int color)
+ {
+ return new View.OnClickListener()
+ {
+ @Override
+ public void onClick(View v) {
+ if (onColorChangeListener != null) {
+ onColorChangeListener.onColorChanged(color);
+ }
+ }
+ };
+ }
+
+ private ColorChangeListener onColorChangeListener;
+ public void setOnColorButtonClickListener( ColorChangeListener listener ) {
+ onColorChangeListener = listener;
+ }
+ }
+
+ /**
+ * ColorViewHolder
+ */
+ public static class ColorViewHolder extends RecyclerView.ViewHolder
+ {
+ public Integer color;
+ public ImageButton colorButton;
+
+ public ColorViewHolder(View itemView) {
+ super(itemView);
+ colorButton = (ImageButton)itemView.findViewById(R.id.colorButton);
+ }
+
+ public void bindColorToView(Integer color)
+ {
+ this.color = color;
+
+ if (color != null)
+ {
+ Drawable d = colorButton.getDrawable();
+ if (d != null) {
+ GradientDrawable g = (GradientDrawable) d.mutate();
+ g.setColor(color);
+ g.invalidateSelf();
+ }
+ }
+ colorButton.setVisibility(color != null ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ /**
+ * ColorPickerPagerAdapter
+ */
+ protected class ColorPickerPagerAdapter extends FragmentPagerAdapter
+ {
+ protected ColorPickerFragment[] fragments;
+
+ public ColorPickerPagerAdapter(FragmentManager fragmentManager)
+ {
+ super(fragmentManager);
+
+ if (Build.VERSION.SDK_INT >= 14) {
+ fragments = new ColorPickerFragment[] { new QuadFlaskColorPickerFragment(), new QuadFlaskColorPickerFragment1(), new SimpleColorPickerFragment() };
+ } else {
+ fragments = new ColorPickerFragment[] { new SimpleColorPickerFragment() };
+ }
+ }
+
+ @Override
+ public Fragment getItem(int position)
+ {
+ fragments[position].setArguments(colorPagerArgs);
+ return fragments[position];
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int position)
+ {
+ fragments[position] = (ColorPickerFragment)super.instantiateItem(container, position);
+ fragments[position].setColorChangeListener(onColorChanged);
+ return fragments[position];
+ }
+
+ private ColorChangeListener onColorChanged = new ColorChangeListener() {
+ @Override
+ public void onColorChanged(int color)
+ {
+ colorPagerArgs.putInt(KEY_COLOR, color);
+ if (colorDialogListener != null) {
+ colorDialogListener.onColorChanged(color);
+ }
+ updateViews(getContext());
+ }
+ };
+
+ @Override
+ public int getCount() {
+ return fragments.length;
+ }
+
+ public void setColor(int color)
+ {
+ for (ColorPickerFragment fragment : fragments) {
+ if (fragment != null) {
+ fragment.setColor(color);
+ }
+ }
+ }
+
+ public void updateViews(Context context)
+ {
+ for (ColorPickerFragment fragment : fragments) {
+ if (fragment != null) {
+ fragment.updateViews(context);
+ }
+ }
+ }
+ }
+
+ /**
+ * ColorPickerFragment
+ */
+ public static class ColorPickerFragment extends Fragment
+ {
+ public ColorPickerFragment() {
+ setArguments(new Bundle());
+ }
+
+ protected ColorDialog.ColorChangeListener listener;
+ public void setColorChangeListener(ColorDialog.ColorChangeListener listener) {
+ this.listener = listener;
+ }
+
+ public void setColor( int color )
+ {
+ getArguments().putInt(KEY_COLOR, color);
+ if (listener != null) {
+ listener.onColorChanged(color);
+ }
+ }
+
+ public int getColor() {
+ return getArguments().getInt(KEY_COLOR, Color.WHITE);
+ }
+
+ public boolean showAlpha() {
+ return getArguments().getBoolean("showAlpha", false);
+ }
+
+ public void updateViews(Context context) {}
+ }
+
+ /**
+ * QuadFlaskColorPickerFragment
+ * Flower Mode
+ */
+ public static class QuadFlaskColorPickerFragment extends ColorPickerFragment
+ {
+ protected AlphaSlider alphaSlider;
+ protected ColorPickerView colorPicker;
+ protected View preview;
+
+ protected int getLayoutResID() {
+ return R.layout.layout_colors_quadflask;
+ }
+
+ protected void initViews(View view)
+ {
+ alphaSlider = (AlphaSlider) view.findViewById(R.id.color_alpha);
+ colorPicker = (ColorPickerView) view.findViewById(R.id.color_picker);
+ preview = view.findViewById(R.id.preview_color);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
+ {
+ View view = inflater.inflate(getLayoutResID(), container, false);
+ initViews(view);
+
+ colorPicker.addOnColorChangedListener(new OnColorChangedListener() {
+ @Override
+ public void onColorChanged(int color) {
+ setColor(color);
+ }
+ });
+
+ updateViews(getContext());
+ return view;
+ }
+
+ @Override
+ public void updateViews(Context context)
+ {
+ alphaSlider.setVisibility(showAlpha() ? View.VISIBLE : View.GONE);
+ colorPicker.setColor(getColor(), false);
+ preview.setBackgroundColor(getColor());
+ }
+ }
+
+ /**
+ * QuadFlaskColorPickerFragment1
+ * Circle Mode
+ */
+ public static class QuadFlaskColorPickerFragment1 extends QuadFlaskColorPickerFragment
+ {
+ @Override
+ protected int getLayoutResID() {
+ return R.layout.layout_colors_quadflask1;
+ }
+
+ @Override
+ protected void initViews(View view)
+ {
+ alphaSlider = (AlphaSlider) view.findViewById(R.id.color_alpha1);
+ colorPicker = (ColorPickerView) view.findViewById(R.id.color_picker1);
+ preview = view.findViewById(R.id.preview_color1);
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/settings/colors/NoSwipeViewPager.java b/app/src/main/java/com/forrestguice/suntimeswidget/settings/colors/NoSwipeViewPager.java
new file mode 100644
index 000000000..27ad0360c
--- /dev/null
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/settings/colors/NoSwipeViewPager.java
@@ -0,0 +1,45 @@
+/**
+ Copyright (C) 2020 Forrest Guice
+ This file is part of SuntimesWidget.
+
+ SuntimesWidget 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.
+
+ SuntimesWidget 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 SuntimesWidget. If not, see .
+*/
+
+package com.forrestguice.suntimeswidget.settings.colors;
+
+import android.content.Context;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+public class NoSwipeViewPager extends ViewPager
+{
+ public NoSwipeViewPager(Context context) {
+ super(context);
+ }
+
+ public NoSwipeViewPager(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return false;
+ }
+}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/settings/colors/SimpleColorPickerFragment.java b/app/src/main/java/com/forrestguice/suntimeswidget/settings/colors/SimpleColorPickerFragment.java
new file mode 100644
index 000000000..794a2f308
--- /dev/null
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/settings/colors/SimpleColorPickerFragment.java
@@ -0,0 +1,234 @@
+/**
+ Copyright (C) 2020 Forrest Guice
+ This file is part of SuntimesWidget.
+
+ SuntimesWidget 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.
+
+ SuntimesWidget 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 SuntimesWidget. If not, see .
+*/
+
+package com.forrestguice.suntimeswidget.settings.colors;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.SeekBar;
+
+import com.forrestguice.suntimeswidget.R;
+
+import java.util.HashMap;
+
+/**
+ * QuadFlaskColorPickerFragment1
+ * Circle Mode
+ */
+public class SimpleColorPickerFragment extends ColorDialog.ColorPickerFragment
+{
+ protected EditText edit_r, edit_g, edit_b, edit_a;
+ protected SeekBar seek_r, seek_g, seek_b, seek_a;
+ protected View preview, layout_a;
+ protected HashMap onTextChanged = new HashMap<>();
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
+ {
+ View view = inflater.inflate(R.layout.layout_colors_simple, container, false);
+ initViews(view);
+ setListeners();
+ updateViews(getContext());
+ return view;
+ }
+
+ protected void initViews(View view)
+ {
+ edit_r = (EditText) view.findViewById(R.id.color_edit_r);
+ edit_g = (EditText) view.findViewById(R.id.color_edit_g);
+ edit_b = (EditText) view.findViewById(R.id.color_edit_b);
+ edit_a = (EditText) view.findViewById(R.id.color_edit_a);
+ seek_r = (SeekBar) view.findViewById(R.id.color_seek_r);
+ seek_g = (SeekBar) view.findViewById(R.id.color_seek_g);
+ seek_b = (SeekBar) view.findViewById(R.id.color_seek_b);
+ seek_a = (SeekBar) view.findViewById(R.id.color_seek_a);
+ layout_a = view.findViewById(R.id.color_layout_a);
+ preview = view.findViewById(R.id.preview_color);
+
+ seek_r.setMax(255);
+ seek_g.setMax(255);
+ seek_b.setMax(255);
+ seek_a.setMax(255);
+
+ seek_r.setOnSeekBarChangeListener(onSliderChangedRGB(edit_r));
+ seek_g.setOnSeekBarChangeListener(onSliderChangedRGB(edit_g));
+ seek_b.setOnSeekBarChangeListener(onSliderChangedRGB(edit_b));
+ seek_a.setOnSeekBarChangeListener(onSliderChangedRGB(edit_a));
+
+ onTextChanged.put(edit_r, onValueChangedRGB(edit_r));
+ onTextChanged.put(edit_g, onValueChangedRGB(edit_g));
+ onTextChanged.put(edit_b, onValueChangedRGB(edit_b));
+ onTextChanged.put(edit_a, onValueChangedRGB(edit_a));
+ }
+
+ protected void setListeners()
+ {
+ edit_r.addTextChangedListener(onTextChanged.get(edit_r));
+ edit_g.addTextChangedListener(onTextChanged.get(edit_g));
+ edit_b.addTextChangedListener(onTextChanged.get(edit_b));
+ edit_a.addTextChangedListener(onTextChanged.get(edit_a));
+ }
+
+ protected void clearListeners()
+ {
+ edit_r.removeTextChangedListener(onTextChanged.get(edit_r));
+ edit_g.removeTextChangedListener(onTextChanged.get(edit_g));
+ edit_b.removeTextChangedListener(onTextChanged.get(edit_b));
+ edit_a.removeTextChangedListener(onTextChanged.get(edit_a));
+ }
+
+ protected int[] getRGB()
+ {
+ int[] value = new int[3];
+
+ try {
+ value[0] = Integer.parseInt(edit_r.getText().toString());
+ } catch (NumberFormatException e) {
+ value[0] = 0;
+ }
+ try {
+ value[1] = Integer.parseInt(edit_g.getText().toString());
+ } catch (NumberFormatException e) {
+ value[1] = 0;
+ }
+ try {
+ value[2] = Integer.parseInt(edit_b.getText().toString());
+ } catch (NumberFormatException e) {
+ value[2] = 0;
+ }
+
+ for (int i=0; i 255) {
+ value[i] = 255;
+ } else if (value[i] < 0) {
+ value[i] = 0;
+ }
+ }
+ return value;
+ }
+
+ protected int getAlpha()
+ {
+ try {
+ int a = Integer.parseInt(edit_a.getText().toString());
+ return a >= 0 && a < 255 ? a : 255;
+ } catch (NumberFormatException e) {
+ return 255;
+ }
+ }
+
+ protected SeekBar.OnSeekBarChangeListener onSliderChangedRGB(final EditText edit)
+ {
+ return new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ edit.setText(Integer.toString(progress));
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {}
+ };
+ }
+
+ protected TextWatcher onValueChangedRGB(final EditText edit)
+ {
+ return new TextWatcher() {
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+ public void onTextChanged(CharSequence s, int start, int before, int count) {}
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void afterTextChanged(Editable s)
+ {
+ if (ignoreNextChange) {
+ ignoreNextChange = false;
+ return;
+ }
+
+ if (!s.toString().isEmpty())
+ {
+ int v = getRGBValue(s.toString());
+ int[] rgb = getRGB();
+ setColor(Color.argb(getAlpha(), rgb[0], rgb[1], rgb[2]));
+
+ ignoreNextChange = true;
+ edit.setText(Integer.toString(v));
+ edit.setSelection(edit.getText().length()); // TODO: fix frequent IndexOutOfBoundsException here
+ }
+ }
+
+ private boolean ignoreNextChange = false;
+ };
+ }
+
+ protected int getRGBValue(String s)
+ {
+ int v;
+ try
+ {
+ v = Integer.parseInt(s);
+ if (v < 0) {
+ v = 0;
+ } else if (v >= 256) {
+ v = 255;
+ }
+ } catch (NumberFormatException e) {
+ v = 0;
+ }
+ return v;
+ }
+
+ @Override
+ public void updateViews(Context context)
+ {
+ clearListeners();
+
+ int color = getColor();
+ preview.setBackgroundColor(color);
+
+ int r = Color.red(color);
+ int g = Color.green(color);
+ int b = Color.blue(color);
+ int a = Color.alpha(color);
+
+ edit_r.setText(Integer.toString(r));
+ edit_g.setText(Integer.toString(g));
+ edit_b.setText(Integer.toString(b));
+ edit_a.setText(showAlpha() ? Integer.toString(a) : "255");
+ layout_a.setVisibility(showAlpha() ? View.VISIBLE : View.GONE);
+
+ seek_r.setProgress(r);
+ seek_g.setProgress(g);
+ seek_b.setProgress(b);
+ seek_a.setProgress(a);
+
+ setListeners();
+ }
+
+}
diff --git a/app/src/main/java/com/forrestguice/suntimeswidget/themes/WidgetThemeConfigActivity.java b/app/src/main/java/com/forrestguice/suntimeswidget/themes/WidgetThemeConfigActivity.java
index b07009adc..64b7363ff 100644
--- a/app/src/main/java/com/forrestguice/suntimeswidget/themes/WidgetThemeConfigActivity.java
+++ b/app/src/main/java/com/forrestguice/suntimeswidget/themes/WidgetThemeConfigActivity.java
@@ -25,12 +25,14 @@
import android.content.SharedPreferences;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.ColorUtils;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
@@ -67,17 +69,20 @@
import com.forrestguice.suntimeswidget.map.WorldMapEquirectangular;
import com.forrestguice.suntimeswidget.map.WorldMapTask;
import com.forrestguice.suntimeswidget.settings.AppSettings;
-import com.forrestguice.suntimeswidget.settings.ColorChooserView;
+import com.forrestguice.suntimeswidget.settings.colors.ColorChooserView;
import com.forrestguice.suntimeswidget.settings.PaddingChooser;
import com.forrestguice.suntimeswidget.settings.SizeEditView;
import com.forrestguice.suntimeswidget.settings.WidgetSettings;
import com.forrestguice.suntimeswidget.settings.WidgetThemes;
+import com.forrestguice.suntimeswidget.settings.colors.ColorDialog;
import com.forrestguice.suntimeswidget.themes.defaults.DarkTheme;
import java.security.InvalidParameterException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
import static com.forrestguice.suntimeswidget.themes.SuntimesThemeContract.THEME_ACCENTCOLOR;
import static com.forrestguice.suntimeswidget.themes.SuntimesThemeContract.THEME_ACTIONCOLOR;
@@ -179,6 +184,7 @@ public UIMode getMode()
private ThemeNameChooser chooseName;
private PaddingChooser choosePadding;
+ private ArrayList recentColors = new ArrayList<>();
private ColorChooser chooseColorRise, chooseColorRiseIconFill, chooseColorRiseIconStroke;
private ColorChooser chooseColorNoon, chooseColorNoonIconFill, chooseColorNoonIconStroke;
private ColorChooser chooseColorSet, chooseColorSetIconFill, chooseColorSetIconStroke;
@@ -606,6 +612,12 @@ private ColorChooser createColorChooser(Context context, int labelID, int editID
private ColorChooser createColorChooser(Context context, TextView label, EditText edit, ImageButton button, String id)
{
ColorChooser chooser = new ColorChooser(context, label, edit, button, id);
+ chooser.setColorChangeListener(new ColorDialog.ColorChangeListener() {
+ @Override
+ public void onColorChanged(int color) {
+ addRecentColor(color);
+ }
+ });
colorChoosers.add(chooser);
return chooser;
}
@@ -640,6 +652,41 @@ private void initColorFields()
}
}
+ private void addRecentColor(int color)
+ {
+ if (!recentColors.contains(color)) {
+ recentColors.add(0, color);
+ }
+ }
+
+ private void updateRecentColors()
+ {
+ recentColors.clear();
+ for (ColorChooser chooser : colorChoosers) {
+ addRecentColor(chooser.getColor());
+ }
+ Collections.sort(recentColors, new Comparator() {
+ @Override
+ public int compare(Integer o1, Integer o2)
+ {
+ double[] lab0 = new double[3];
+ double[] lab1 = new double[3];
+ double[] lab2 = new double[3];
+ ColorUtils.colorToLAB(Color.BLACK, lab0);
+ ColorUtils.colorToLAB(o1, lab1);
+ ColorUtils.colorToLAB(o2, lab2);
+
+ Double e1 = ColorUtils.distanceEuclidean(lab1, lab0);
+ Double e2 = ColorUtils.distanceEuclidean(lab2, lab0);
+ return e2.compareTo(e1);
+ }
+ });
+
+ for (ColorChooser chooser : colorChoosers) {
+ chooser.setRecentColors(recentColors);
+ }
+ }
+
private void initSizeFields()
{
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
@@ -1242,6 +1289,7 @@ public void onSaveInstanceState( Bundle outState )
outState.putInt(chooser.getID(), chooser.getColor());
}
outState.putIntArray(THEME_PADDING, choosePadding.getPadding());
+ outState.putIntegerArrayList(ColorDialog.KEY_RECENT, recentColors);
}
/**
@@ -1273,15 +1321,23 @@ public void onRestoreInstanceState(@NonNull Bundle savedState)
spinBackground.setSelection(0);
}
- for (SizeChooser chooser : sizeChoosers)
- {
+ for (SizeChooser chooser : sizeChoosers) {
chooser.setValue(savedState);
}
+
+ ArrayList colors = savedState.getIntegerArrayList(ColorDialog.KEY_RECENT);
+ if (colors != null) {
+ recentColors.clear();
+ recentColors.addAll(colors);
+ }
+
for (ColorChooser chooser : colorChoosers)
{
+ chooser.setRecentColors(recentColors);
chooser.setColor(savedState);
}
- choosePadding.setPadding(savedState.getIntArray(THEME_PADDING));
+
+ choosePadding.setPadding(savedState.getIntArray(THEME_PADDING)); // TODO: might be null (check)
}
protected void flipToPreview( int previewID )
@@ -1307,7 +1363,7 @@ public boolean onCreateOptionsMenu(Menu menu)
inflater.inflate(R.menu.themeconfig, menu);
final MenuItem saveItem = menu.findItem(R.id.saveTheme);
- preview.getHandler().postDelayed(new Runnable()
+ preview.getHandler().postDelayed(new Runnable() // TODO: bug here: npe this line, sometimes
{
public void run()
{
@@ -1439,6 +1495,7 @@ protected void loadTheme( String themeName )
toggleRiseSetIconFill(usingRiseSetIconFill(), true);
toggleRiseSetIconStroke(usingRiseSetIconStroke(), true);
toggleNoonIconColor(usingNoonIconColor(), true);
+ updateRecentColors();
}
private void setSelectedBackground(SuntimesTheme.ThemeBackground themeBackground)
@@ -1738,7 +1795,7 @@ public void updatePreview()
/**
* ColorChooser
*/
- private class ColorChooser extends com.forrestguice.suntimeswidget.settings.ColorChooser
+ private class ColorChooser extends com.forrestguice.suntimeswidget.settings.colors.ColorChooser
{
public ColorChooser(Context context, TextView txtLabel, EditText editField, ImageButton imgButton, String id)
{
diff --git a/app/src/main/res/drawable-anydpi/checkbox_unchecked.xml b/app/src/main/res/drawable-anydpi/checkbox_unchecked.xml
new file mode 100644
index 000000000..cb0693feb
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/checkbox_unchecked.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/checkbox_unchecked_light.xml b/app/src/main/res/drawable-anydpi/checkbox_unchecked_light.xml
new file mode 100644
index 000000000..14406c3c1
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/checkbox_unchecked_light.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_action_back.xml b/app/src/main/res/drawable-anydpi/ic_action_back.xml
new file mode 100644
index 000000000..ee35fabb2
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_action_back.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_action_back_light.xml b/app/src/main/res/drawable-anydpi/ic_action_back_light.xml
new file mode 100644
index 000000000..98478bec9
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_action_back_light.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_action_doneall.xml b/app/src/main/res/drawable-anydpi/ic_action_doneall.xml
new file mode 100644
index 000000000..85d9ffd19
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_action_doneall.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_action_doneall_light.xml b/app/src/main/res/drawable-anydpi/ic_action_doneall_light.xml
new file mode 100644
index 000000000..8f6859cf1
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_action_doneall_light.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_action_search.xml b/app/src/main/res/drawable-anydpi/ic_action_search.xml
new file mode 100644
index 000000000..afb0429dd
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_action_search.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_action_search_light.xml b/app/src/main/res/drawable-anydpi/ic_action_search_light.xml
new file mode 100644
index 000000000..42f073e0d
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_action_search_light.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_action_sort.xml b/app/src/main/res/drawable-anydpi/ic_action_sort.xml
new file mode 100644
index 000000000..8ea7f413f
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_action_sort.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_action_sort_light.xml b/app/src/main/res/drawable-anydpi/ic_action_sort_light.xml
new file mode 100644
index 000000000..2a70d5783
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_action_sort_light.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-hdpi/checkbox_unchecked.png b/app/src/main/res/drawable-hdpi/checkbox_unchecked.png
new file mode 100644
index 000000000..660c22a5b
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/checkbox_unchecked.png differ
diff --git a/app/src/main/res/drawable-hdpi/checkbox_unchecked_light.png b/app/src/main/res/drawable-hdpi/checkbox_unchecked_light.png
new file mode 100644
index 000000000..5b3335784
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/checkbox_unchecked_light.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_back.png b/app/src/main/res/drawable-hdpi/ic_action_back.png
new file mode 100644
index 000000000..1c0e6f1f4
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_back.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_back_light.png b/app/src/main/res/drawable-hdpi/ic_action_back_light.png
new file mode 100644
index 000000000..ebdf58407
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_back_light.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_doneall.png b/app/src/main/res/drawable-hdpi/ic_action_doneall.png
new file mode 100644
index 000000000..3d0e27aa9
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_doneall.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_doneall_light.png b/app/src/main/res/drawable-hdpi/ic_action_doneall_light.png
new file mode 100644
index 000000000..9cb3d1f87
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_doneall_light.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_expand_less.png b/app/src/main/res/drawable-hdpi/ic_action_expand_less.png
new file mode 100644
index 000000000..69ef37e62
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_expand_less.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_expand_less_light.png b/app/src/main/res/drawable-hdpi/ic_action_expand_less_light.png
new file mode 100644
index 000000000..30be179b2
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_expand_less_light.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_expand_more.png b/app/src/main/res/drawable-hdpi/ic_action_expand_more.png
new file mode 100644
index 000000000..e61c398a2
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_expand_more.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_expand_more_light.png b/app/src/main/res/drawable-hdpi/ic_action_expand_more_light.png
new file mode 100644
index 000000000..70faedd07
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_expand_more_light.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_extension.png b/app/src/main/res/drawable-hdpi/ic_action_extension.png
new file mode 100644
index 000000000..f724f526e
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_extension.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_extension_light.png b/app/src/main/res/drawable-hdpi/ic_action_extension_light.png
new file mode 100644
index 000000000..e82f3408b
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_extension_light.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_search.png b/app/src/main/res/drawable-hdpi/ic_action_search.png
new file mode 100644
index 000000000..f9c0afe19
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_search.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_search_light.png b/app/src/main/res/drawable-hdpi/ic_action_search_light.png
new file mode 100644
index 000000000..c3caa9b61
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_search_light.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_sort.png b/app/src/main/res/drawable-hdpi/ic_action_sort.png
new file mode 100644
index 000000000..20c3ef161
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_sort.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_sort_light.png b/app/src/main/res/drawable-hdpi/ic_action_sort_light.png
new file mode 100644
index 000000000..c2dcc66a9
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_sort_light.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_vibration.png b/app/src/main/res/drawable-hdpi/ic_action_vibration.png
new file mode 100644
index 000000000..c93e01963
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_vibration.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_vibration_light.png b/app/src/main/res/drawable-hdpi/ic_action_vibration_light.png
new file mode 100644
index 000000000..4f0dd38e1
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_vibration_light.png differ
diff --git a/app/src/main/res/drawable-mdpi/checkbox_unchecked.png b/app/src/main/res/drawable-mdpi/checkbox_unchecked.png
new file mode 100644
index 000000000..81e346f11
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/checkbox_unchecked.png differ
diff --git a/app/src/main/res/drawable-mdpi/checkbox_unchecked_light.png b/app/src/main/res/drawable-mdpi/checkbox_unchecked_light.png
new file mode 100644
index 000000000..afe4a6a3e
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/checkbox_unchecked_light.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_back.png b/app/src/main/res/drawable-mdpi/ic_action_back.png
new file mode 100644
index 000000000..512013824
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_back.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_back_light.png b/app/src/main/res/drawable-mdpi/ic_action_back_light.png
new file mode 100644
index 000000000..24a492818
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_back_light.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_doneall.png b/app/src/main/res/drawable-mdpi/ic_action_doneall.png
new file mode 100644
index 000000000..c0e88e9fe
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_doneall.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_doneall_light.png b/app/src/main/res/drawable-mdpi/ic_action_doneall_light.png
new file mode 100644
index 000000000..45cc6ec77
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_doneall_light.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_expand_less.png b/app/src/main/res/drawable-mdpi/ic_action_expand_less.png
new file mode 100644
index 000000000..755333a79
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_expand_less.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_expand_less_light.png b/app/src/main/res/drawable-mdpi/ic_action_expand_less_light.png
new file mode 100644
index 000000000..4b7d371f7
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_expand_less_light.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_expand_more.png b/app/src/main/res/drawable-mdpi/ic_action_expand_more.png
new file mode 100644
index 000000000..139e4389e
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_expand_more.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_expand_more_light.png b/app/src/main/res/drawable-mdpi/ic_action_expand_more_light.png
new file mode 100644
index 000000000..50d6cc31f
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_expand_more_light.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_extension.png b/app/src/main/res/drawable-mdpi/ic_action_extension.png
new file mode 100644
index 000000000..4e648094e
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_extension.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_extension_light.png b/app/src/main/res/drawable-mdpi/ic_action_extension_light.png
new file mode 100644
index 000000000..80ba3efa2
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_extension_light.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_search.png b/app/src/main/res/drawable-mdpi/ic_action_search.png
new file mode 100644
index 000000000..6fd5a13ce
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_search.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_search_light.png b/app/src/main/res/drawable-mdpi/ic_action_search_light.png
new file mode 100644
index 000000000..0f50ebcb5
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_search_light.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_sort.png b/app/src/main/res/drawable-mdpi/ic_action_sort.png
new file mode 100644
index 000000000..de275ed9b
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_sort.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_sort_light.png b/app/src/main/res/drawable-mdpi/ic_action_sort_light.png
new file mode 100644
index 000000000..e5de8b643
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_sort_light.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_vibration.png b/app/src/main/res/drawable-mdpi/ic_action_vibration.png
new file mode 100644
index 000000000..e2358926a
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_vibration.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_vibration_light.png b/app/src/main/res/drawable-mdpi/ic_action_vibration_light.png
new file mode 100644
index 000000000..c693f7022
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_vibration_light.png differ
diff --git a/app/src/main/res/drawable-v21/card_alarmitem_pressed_dark1.xml b/app/src/main/res/drawable-v21/card_alarmitem_pressed_dark1.xml
new file mode 100644
index 000000000..36ddafd33
--- /dev/null
+++ b/app/src/main/res/drawable-v21/card_alarmitem_pressed_dark1.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/card_alarmitem_pressed_light.xml b/app/src/main/res/drawable-v21/card_alarmitem_pressed_light.xml
new file mode 100644
index 000000000..c46a94986
--- /dev/null
+++ b/app/src/main/res/drawable-v21/card_alarmitem_pressed_light.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/chip_dark_pressed.xml b/app/src/main/res/drawable-v21/chip_dark_pressed.xml
new file mode 100644
index 000000000..bd1e1d8ad
--- /dev/null
+++ b/app/src/main/res/drawable-v21/chip_dark_pressed.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/chip_light_pressed.xml b/app/src/main/res/drawable-v21/chip_light_pressed.xml
new file mode 100644
index 000000000..163b236bb
--- /dev/null
+++ b/app/src/main/res/drawable-v21/chip_light_pressed.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/selectable_item_dark.xml b/app/src/main/res/drawable-v21/selectable_item_dark.xml
new file mode 100644
index 000000000..545b206b3
--- /dev/null
+++ b/app/src/main/res/drawable-v21/selectable_item_dark.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable-v21/selectable_item_light.xml b/app/src/main/res/drawable-v21/selectable_item_light.xml
new file mode 100644
index 000000000..dc46998ff
--- /dev/null
+++ b/app/src/main/res/drawable-v21/selectable_item_light.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c7bd21dbd..000000000
--- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable-xhdpi/checkbox_unchecked.png b/app/src/main/res/drawable-xhdpi/checkbox_unchecked.png
new file mode 100644
index 000000000..24ffd5111
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/checkbox_unchecked.png differ
diff --git a/app/src/main/res/drawable-xhdpi/checkbox_unchecked_light.png b/app/src/main/res/drawable-xhdpi/checkbox_unchecked_light.png
new file mode 100644
index 000000000..8e123b571
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/checkbox_unchecked_light.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_back.png b/app/src/main/res/drawable-xhdpi/ic_action_back.png
new file mode 100644
index 000000000..8b7ee56c1
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_back.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_back_light.png b/app/src/main/res/drawable-xhdpi/ic_action_back_light.png
new file mode 100644
index 000000000..77c9552e0
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_back_light.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_doneall.png b/app/src/main/res/drawable-xhdpi/ic_action_doneall.png
new file mode 100644
index 000000000..d6036992c
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_doneall.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_doneall_light.png b/app/src/main/res/drawable-xhdpi/ic_action_doneall_light.png
new file mode 100644
index 000000000..d3b98d076
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_doneall_light.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_expand_less.png b/app/src/main/res/drawable-xhdpi/ic_action_expand_less.png
new file mode 100644
index 000000000..09ab920ea
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_expand_less.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_expand_less_light.png b/app/src/main/res/drawable-xhdpi/ic_action_expand_less_light.png
new file mode 100644
index 000000000..710d0d561
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_expand_less_light.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_expand_more.png b/app/src/main/res/drawable-xhdpi/ic_action_expand_more.png
new file mode 100644
index 000000000..237291e89
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_expand_more.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_expand_more_light.png b/app/src/main/res/drawable-xhdpi/ic_action_expand_more_light.png
new file mode 100644
index 000000000..7a4078fd9
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_expand_more_light.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_extension.png b/app/src/main/res/drawable-xhdpi/ic_action_extension.png
new file mode 100644
index 000000000..fcc25c37b
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_extension.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_extension_light.png b/app/src/main/res/drawable-xhdpi/ic_action_extension_light.png
new file mode 100644
index 000000000..053133617
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_extension_light.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_search.png b/app/src/main/res/drawable-xhdpi/ic_action_search.png
new file mode 100644
index 000000000..aad212cca
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_search.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_search_light.png b/app/src/main/res/drawable-xhdpi/ic_action_search_light.png
new file mode 100644
index 000000000..3c937e936
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_search_light.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_sort.png b/app/src/main/res/drawable-xhdpi/ic_action_sort.png
new file mode 100644
index 000000000..63bb3dbdc
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_sort.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_sort_light.png b/app/src/main/res/drawable-xhdpi/ic_action_sort_light.png
new file mode 100644
index 000000000..42060ba8f
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_sort_light.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_vibration.png b/app/src/main/res/drawable-xhdpi/ic_action_vibration.png
new file mode 100644
index 000000000..dff7419a5
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_vibration.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_vibration_light.png b/app/src/main/res/drawable-xhdpi/ic_action_vibration_light.png
new file mode 100644
index 000000000..400a5154b
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_vibration_light.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/checkbox_unchecked.png b/app/src/main/res/drawable-xxhdpi/checkbox_unchecked.png
new file mode 100644
index 000000000..4ad45283c
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/checkbox_unchecked.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/checkbox_unchecked_light.png b/app/src/main/res/drawable-xxhdpi/checkbox_unchecked_light.png
new file mode 100644
index 000000000..addc81862
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/checkbox_unchecked_light.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_back.png b/app/src/main/res/drawable-xxhdpi/ic_action_back.png
new file mode 100644
index 000000000..10b5b40e9
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_back.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_back_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_back_light.png
new file mode 100644
index 000000000..d7c6207da
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_back_light.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_doneall.png b/app/src/main/res/drawable-xxhdpi/ic_action_doneall.png
new file mode 100644
index 000000000..da71795bd
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_doneall.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_doneall_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_doneall_light.png
new file mode 100644
index 000000000..c3085c267
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_doneall_light.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_expand_less.png b/app/src/main/res/drawable-xxhdpi/ic_action_expand_less.png
new file mode 100644
index 000000000..e214cddf5
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_expand_less.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_expand_less_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_expand_less_light.png
new file mode 100644
index 000000000..e4b7e117f
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_expand_less_light.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_expand_more.png b/app/src/main/res/drawable-xxhdpi/ic_action_expand_more.png
new file mode 100644
index 000000000..01fb3f877
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_expand_more.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_expand_more_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_expand_more_light.png
new file mode 100644
index 000000000..2ab27e812
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_expand_more_light.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_extension.png b/app/src/main/res/drawable-xxhdpi/ic_action_extension.png
new file mode 100644
index 000000000..357ebc060
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_extension.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_extension_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_extension_light.png
new file mode 100644
index 000000000..12e19e68b
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_extension_light.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_search.png b/app/src/main/res/drawable-xxhdpi/ic_action_search.png
new file mode 100644
index 000000000..64b54caf7
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_search.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_search_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_search_light.png
new file mode 100644
index 000000000..8533f6442
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_search_light.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_sort.png b/app/src/main/res/drawable-xxhdpi/ic_action_sort.png
new file mode 100644
index 000000000..b6aca1386
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_sort.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_sort_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_sort_light.png
new file mode 100644
index 000000000..57929fcee
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_sort_light.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_vibration.png b/app/src/main/res/drawable-xxhdpi/ic_action_vibration.png
new file mode 100644
index 000000000..393e42cd4
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_vibration.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_vibration_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_vibration_light.png
new file mode 100644
index 000000000..2dde90fe6
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_vibration_light.png differ
diff --git a/app/src/main/res/drawable/bottom_sheet_edge.xml b/app/src/main/res/drawable/bottom_sheet_edge.xml
new file mode 100644
index 000000000..bb5fae66d
--- /dev/null
+++ b/app/src/main/res/drawable/bottom_sheet_edge.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/card_alarmitem_disabled_dark0.xml b/app/src/main/res/drawable/card_alarmitem_disabled_dark0.xml
new file mode 100644
index 000000000..b0dc4bab5
--- /dev/null
+++ b/app/src/main/res/drawable/card_alarmitem_disabled_dark0.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/card_alarmitem_disabled_dark1.xml b/app/src/main/res/drawable/card_alarmitem_disabled_dark1.xml
new file mode 100644
index 000000000..b49101903
--- /dev/null
+++ b/app/src/main/res/drawable/card_alarmitem_disabled_dark1.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/card_alarmitem_disabled_light.xml b/app/src/main/res/drawable/card_alarmitem_disabled_light.xml
index 3870ee52c..fe6d3d7c2 100644
--- a/app/src/main/res/drawable/card_alarmitem_disabled_light.xml
+++ b/app/src/main/res/drawable/card_alarmitem_disabled_light.xml
@@ -4,8 +4,8 @@
-
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/card_alarmitem_disabled_light0.xml b/app/src/main/res/drawable/card_alarmitem_disabled_light0.xml
new file mode 100644
index 000000000..66132d7f4
--- /dev/null
+++ b/app/src/main/res/drawable/card_alarmitem_disabled_light0.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/card_alarmitem_enabled_dark0.xml b/app/src/main/res/drawable/card_alarmitem_enabled_dark0.xml
new file mode 100644
index 000000000..07542416b
--- /dev/null
+++ b/app/src/main/res/drawable/card_alarmitem_enabled_dark0.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/card_alarmitem_enabled_dark1.xml b/app/src/main/res/drawable/card_alarmitem_enabled_dark1.xml
new file mode 100644
index 000000000..ba8b2659e
--- /dev/null
+++ b/app/src/main/res/drawable/card_alarmitem_enabled_dark1.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/card_alarmitem_enabled_light.xml b/app/src/main/res/drawable/card_alarmitem_enabled_light.xml
index c8067835c..392bac497 100644
--- a/app/src/main/res/drawable/card_alarmitem_enabled_light.xml
+++ b/app/src/main/res/drawable/card_alarmitem_enabled_light.xml
@@ -4,8 +4,8 @@
-
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/card_alarmitem_enabled_light0.xml b/app/src/main/res/drawable/card_alarmitem_enabled_light0.xml
new file mode 100644
index 000000000..fcfe3baa1
--- /dev/null
+++ b/app/src/main/res/drawable/card_alarmitem_enabled_light0.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/card_alarmitem_pressed_dark1.xml b/app/src/main/res/drawable/card_alarmitem_pressed_dark1.xml
new file mode 100644
index 000000000..d8603a241
--- /dev/null
+++ b/app/src/main/res/drawable/card_alarmitem_pressed_dark1.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/card_alarmitem_pressed_light.xml b/app/src/main/res/drawable/card_alarmitem_pressed_light.xml
new file mode 100644
index 000000000..4f33f37f2
--- /dev/null
+++ b/app/src/main/res/drawable/card_alarmitem_pressed_light.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/check_vibrate_dark.xml b/app/src/main/res/drawable/check_vibrate_dark.xml
new file mode 100644
index 000000000..6a8173a1b
--- /dev/null
+++ b/app/src/main/res/drawable/check_vibrate_dark.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/check_vibrate_light.xml b/app/src/main/res/drawable/check_vibrate_light.xml
new file mode 100644
index 000000000..5e53c2195
--- /dev/null
+++ b/app/src/main/res/drawable/check_vibrate_light.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/chip_dark.xml b/app/src/main/res/drawable/chip_dark.xml
new file mode 100644
index 000000000..3b49dfb4d
--- /dev/null
+++ b/app/src/main/res/drawable/chip_dark.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/chip_dark_disabled.xml b/app/src/main/res/drawable/chip_dark_disabled.xml
new file mode 100644
index 000000000..fea7f8405
--- /dev/null
+++ b/app/src/main/res/drawable/chip_dark_disabled.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/chip_dark_enabled.xml b/app/src/main/res/drawable/chip_dark_enabled.xml
new file mode 100644
index 000000000..f31f4fe5e
--- /dev/null
+++ b/app/src/main/res/drawable/chip_dark_enabled.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/chip_dark_pressed.xml b/app/src/main/res/drawable/chip_dark_pressed.xml
new file mode 100644
index 000000000..a7f3c4495
--- /dev/null
+++ b/app/src/main/res/drawable/chip_dark_pressed.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/chip_light.xml b/app/src/main/res/drawable/chip_light.xml
new file mode 100644
index 000000000..1c907b808
--- /dev/null
+++ b/app/src/main/res/drawable/chip_light.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/chip_light_disabled.xml b/app/src/main/res/drawable/chip_light_disabled.xml
new file mode 100644
index 000000000..fea7f8405
--- /dev/null
+++ b/app/src/main/res/drawable/chip_light_disabled.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/chip_light_enabled.xml b/app/src/main/res/drawable/chip_light_enabled.xml
new file mode 100644
index 000000000..07f3254c7
--- /dev/null
+++ b/app/src/main/res/drawable/chip_light_enabled.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/chip_light_pressed.xml b/app/src/main/res/drawable/chip_light_pressed.xml
new file mode 100644
index 000000000..4cc7f64d2
--- /dev/null
+++ b/app/src/main/res/drawable/chip_light_pressed.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/chip_tray_dark.xml b/app/src/main/res/drawable/chip_tray_dark.xml
new file mode 100644
index 000000000..6e5c6339c
--- /dev/null
+++ b/app/src/main/res/drawable/chip_tray_dark.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/chip_tray_light.xml b/app/src/main/res/drawable/chip_tray_light.xml
new file mode 100644
index 000000000..cd0ff8e6c
--- /dev/null
+++ b/app/src/main/res/drawable/chip_tray_light.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_action_expand.xml b/app/src/main/res/drawable/ic_action_expand.xml
new file mode 100644
index 000000000..eb5529d22
--- /dev/null
+++ b/app/src/main/res/drawable/ic_action_expand.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_action_expand_light.xml b/app/src/main/res/drawable/ic_action_expand_light.xml
new file mode 100644
index 000000000..7b9e50164
--- /dev/null
+++ b/app/src/main/res/drawable/ic_action_expand_light.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_action_suntimes.xml b/app/src/main/res/drawable/ic_action_suntimes.xml
index 76066d488..8a9a590dc 100644
--- a/app/src/main/res/drawable/ic_action_suntimes.xml
+++ b/app/src/main/res/drawable/ic_action_suntimes.xml
@@ -1,19 +1,16 @@
-
-
-
-
-
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_action_suntimes_huge.xml b/app/src/main/res/drawable/ic_action_suntimes_huge.xml
index 9df58ed61..e99686781 100644
--- a/app/src/main/res/drawable/ic_action_suntimes_huge.xml
+++ b/app/src/main/res/drawable/ic_action_suntimes_huge.xml
@@ -1,19 +1,14 @@
-
-
-
+
-
-
diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml
index 2aaee4308..6372edd13 100644
--- a/app/src/main/res/drawable/ic_launcher_foreground.xml
+++ b/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -1,18 +1,16 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_place_96dp.xml b/app/src/main/res/drawable/ic_place_96dp.xml
new file mode 100644
index 000000000..49bf27e2d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_place_96dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_suntimesalarms.xml b/app/src/main/res/drawable/ic_suntimesalarms.xml
new file mode 100644
index 000000000..2c0af5896
--- /dev/null
+++ b/app/src/main/res/drawable/ic_suntimesalarms.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_transparent1.xml b/app/src/main/res/drawable/ic_transparent1.xml
new file mode 100644
index 000000000..01b990982
--- /dev/null
+++ b/app/src/main/res/drawable/ic_transparent1.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/selectable_item_dark.xml b/app/src/main/res/drawable/selectable_item_dark.xml
new file mode 100644
index 000000000..d4aa11c62
--- /dev/null
+++ b/app/src/main/res/drawable/selectable_item_dark.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/selectable_item_light.xml b/app/src/main/res/drawable/selectable_item_light.xml
new file mode 100644
index 000000000..2afcec94f
--- /dev/null
+++ b/app/src/main/res/drawable/selectable_item_light.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/svg_moon_q1.xml b/app/src/main/res/drawable/svg_moon_q1.xml
new file mode 100644
index 000000000..8f4aa5ba8
--- /dev/null
+++ b/app/src/main/res/drawable/svg_moon_q1.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/drawable/svg_moon_q3.xml b/app/src/main/res/drawable/svg_moon_q3.xml
new file mode 100644
index 000000000..1a043031e
--- /dev/null
+++ b/app/src/main/res/drawable/svg_moon_q3.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/drawable/svg_season.xml b/app/src/main/res/drawable/svg_season.xml
new file mode 100644
index 000000000..4c1fb6cb4
--- /dev/null
+++ b/app/src/main/res/drawable/svg_season.xml
@@ -0,0 +1,12 @@
+
+
+
diff --git a/app/src/main/res/drawable/svg_sunrise.xml b/app/src/main/res/drawable/svg_sunrise.xml
new file mode 100644
index 000000000..598b6c9f3
--- /dev/null
+++ b/app/src/main/res/drawable/svg_sunrise.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/drawable/svg_sunset.xml b/app/src/main/res/drawable/svg_sunset.xml
new file mode 100644
index 000000000..f46f3d126
--- /dev/null
+++ b/app/src/main/res/drawable/svg_sunset.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/layout-land/layout_about_app.xml b/app/src/main/res/layout-land/layout_about_app.xml
new file mode 100644
index 000000000..90496eecb
--- /dev/null
+++ b/app/src/main/res/layout-land/layout_about_app.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/layout_colors_quadflask.xml b/app/src/main/res/layout-land/layout_colors_quadflask.xml
new file mode 100644
index 000000000..43d8d28b8
--- /dev/null
+++ b/app/src/main/res/layout-land/layout_colors_quadflask.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/layout_colors_quadflask1.xml b/app/src/main/res/layout-land/layout_colors_quadflask1.xml
new file mode 100644
index 000000000..1892bbbf0
--- /dev/null
+++ b/app/src/main/res/layout-land/layout_colors_quadflask1.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/layout_colors_simple.xml b/app/src/main/res/layout-land/layout_colors_simple.xml
new file mode 100644
index 000000000..632f42d20
--- /dev/null
+++ b/app/src/main/res/layout-land/layout_colors_simple.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/layout_dialog_alarmcreate.xml b/app/src/main/res/layout-land/layout_dialog_alarmcreate.xml
new file mode 100644
index 000000000..a3c57b99c
--- /dev/null
+++ b/app/src/main/res/layout-land/layout_dialog_alarmcreate.xml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/layout_dialog_alarmtime.xml b/app/src/main/res/layout-land/layout_dialog_alarmtime.xml
index c3ce9401b..16b9b992b 100644
--- a/app/src/main/res/layout-land/layout_dialog_alarmtime.xml
+++ b/app/src/main/res/layout-land/layout_dialog_alarmtime.xml
@@ -18,9 +18,7 @@
-->
+ android:padding="8dp" android:orientation="horizontal">
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/layout_dialog_colors.xml b/app/src/main/res/layout-land/layout_dialog_colors.xml
new file mode 100644
index 000000000..6f7c1d051
--- /dev/null
+++ b/app/src/main/res/layout-land/layout_dialog_colors.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/layout_listitem_alarmclock1.xml b/app/src/main/res/layout-land/layout_listitem_alarmclock1.xml
new file mode 100644
index 000000000..1b17935dd
--- /dev/null
+++ b/app/src/main/res/layout-land/layout_listitem_alarmclock1.xml
@@ -0,0 +1,273 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/layout_listitem_alarmtime.xml b/app/src/main/res/layout-land/layout_listitem_alarmtime.xml
new file mode 100644
index 000000000..79560d496
--- /dev/null
+++ b/app/src/main/res/layout-land/layout_listitem_alarmtime.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-v11/layout_listitem_solarevent.xml b/app/src/main/res/layout-v11/layout_listitem_solarevent.xml
index ec33dd8d7..5b64b7c6c 100644
--- a/app/src/main/res/layout-v11/layout_listitem_solarevent.xml
+++ b/app/src/main/res/layout-v11/layout_listitem_solarevent.xml
@@ -17,7 +17,7 @@
along with SuntimesWidget. If not, see .
-->
diff --git a/app/src/main/res/layout-v14/layout_listitem_alarmclock_switch.xml b/app/src/main/res/layout-v14/layout_listitem_alarmclock_switch.xml
index a45b06f2f..d8bb6b467 100644
--- a/app/src/main/res/layout-v14/layout_listitem_alarmclock_switch.xml
+++ b/app/src/main/res/layout-v14/layout_listitem_alarmclock_switch.xml
@@ -25,7 +25,7 @@
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp" android:layout_marginRight="8dp"
android:checked="true"
- app:switchMinWidth="100dp" />
+ app:switchMinWidth="48dp" />
\ No newline at end of file
diff --git a/app/src/main/res/layout-v14/layout_settings_location2.xml b/app/src/main/res/layout-v14/layout_settings_location2.xml
index e059318ff..25e4864f9 100644
--- a/app/src/main/res/layout-v14/layout_settings_location2.xml
+++ b/app/src/main/res/layout-v14/layout_settings_location2.xml
@@ -1,6 +1,6 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_about_app.xml b/app/src/main/res/layout/layout_about_app.xml
index 749df56fd..42851d937 100644
--- a/app/src/main/res/layout/layout_about_app.xml
+++ b/app/src/main/res/layout/layout_about_app.xml
@@ -18,15 +18,15 @@
diff --git a/app/src/main/res/layout/layout_activity_actionlist.xml b/app/src/main/res/layout/layout_activity_actionlist.xml
new file mode 100644
index 000000000..0445d6935
--- /dev/null
+++ b/app/src/main/res/layout/layout_activity_actionlist.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_activity_alarmclock1.xml b/app/src/main/res/layout/layout_activity_alarmclock1.xml
new file mode 100644
index 000000000..0f3d0b3d4
--- /dev/null
+++ b/app/src/main/res/layout/layout_activity_alarmclock1.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_activity_alarmclock_bottomsheet.xml b/app/src/main/res/layout/layout_activity_alarmclock_bottomsheet.xml
new file mode 100644
index 000000000..829ac2656
--- /dev/null
+++ b/app/src/main/res/layout/layout_activity_alarmclock_bottomsheet.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_activity_alarmclock_content.xml b/app/src/main/res/layout/layout_activity_alarmclock_content.xml
new file mode 100644
index 000000000..886c0f8f5
--- /dev/null
+++ b/app/src/main/res/layout/layout_activity_alarmclock_content.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_activity_alarmclock_fab.xml b/app/src/main/res/layout/layout_activity_alarmclock_fab.xml
new file mode 100644
index 000000000..a2f9d4b86
--- /dev/null
+++ b/app/src/main/res/layout/layout_activity_alarmclock_fab.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_activity_alarmedit.xml b/app/src/main/res/layout/layout_activity_alarmedit.xml
new file mode 100644
index 000000000..038d67177
--- /dev/null
+++ b/app/src/main/res/layout/layout_activity_alarmedit.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_activity_colors.xml b/app/src/main/res/layout/layout_activity_colors.xml
new file mode 100644
index 000000000..5aa299b93
--- /dev/null
+++ b/app/src/main/res/layout/layout_activity_colors.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_activity_places.xml b/app/src/main/res/layout/layout_activity_places.xml
new file mode 100644
index 000000000..7b56f43b0
--- /dev/null
+++ b/app/src/main/res/layout/layout_activity_places.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_colors_quadflask.xml b/app/src/main/res/layout/layout_colors_quadflask.xml
new file mode 100644
index 000000000..a21f02038
--- /dev/null
+++ b/app/src/main/res/layout/layout_colors_quadflask.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_colors_quadflask1.xml b/app/src/main/res/layout/layout_colors_quadflask1.xml
new file mode 100644
index 000000000..57086930b
--- /dev/null
+++ b/app/src/main/res/layout/layout_colors_quadflask1.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_colors_simple.xml b/app/src/main/res/layout/layout_colors_simple.xml
new file mode 100644
index 000000000..dba771e3e
--- /dev/null
+++ b/app/src/main/res/layout/layout_colors_simple.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_colors_simple_hsv.xml b/app/src/main/res/layout/layout_colors_simple_hsv.xml
new file mode 100644
index 000000000..4a906204c
--- /dev/null
+++ b/app/src/main/res/layout/layout_colors_simple_hsv.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_colors_simple_rgb.xml b/app/src/main/res/layout/layout_colors_simple_rgb.xml
new file mode 100644
index 000000000..b5f9a4121
--- /dev/null
+++ b/app/src/main/res/layout/layout_colors_simple_rgb.xml
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_dialog_alarmcreate.xml b/app/src/main/res/layout/layout_dialog_alarmcreate.xml
new file mode 100644
index 000000000..9741f3bc0
--- /dev/null
+++ b/app/src/main/res/layout/layout_dialog_alarmcreate.xml
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_dialog_alarmitem.xml b/app/src/main/res/layout/layout_dialog_alarmitem.xml
new file mode 100644
index 000000000..dbec01fed
--- /dev/null
+++ b/app/src/main/res/layout/layout_dialog_alarmitem.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_dialog_alarmlist.xml b/app/src/main/res/layout/layout_dialog_alarmlist.xml
new file mode 100644
index 000000000..81e0efedf
--- /dev/null
+++ b/app/src/main/res/layout/layout_dialog_alarmlist.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_dialog_alarmtime.xml b/app/src/main/res/layout/layout_dialog_alarmtime.xml
index 356e52dd6..f87197534 100644
--- a/app/src/main/res/layout/layout_dialog_alarmtime.xml
+++ b/app/src/main/res/layout/layout_dialog_alarmtime.xml
@@ -16,31 +16,32 @@
You should have received a copy of the GNU General Public License
along with SuntimesWidget. If not, see .
-->
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_dialog_colors.xml b/app/src/main/res/layout/layout_dialog_colors.xml
new file mode 100644
index 000000000..6ed2f8983
--- /dev/null
+++ b/app/src/main/res/layout/layout_dialog_colors.xml
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_dialog_intent_load.xml b/app/src/main/res/layout/layout_dialog_intent_load.xml
new file mode 100644
index 000000000..40223fb3d
--- /dev/null
+++ b/app/src/main/res/layout/layout_dialog_intent_load.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_dialog_intent_save.xml b/app/src/main/res/layout/layout_dialog_intent_save.xml
new file mode 100644
index 000000000..e801bae38
--- /dev/null
+++ b/app/src/main/res/layout/layout_dialog_intent_save.xml
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_dialog_location.xml b/app/src/main/res/layout/layout_dialog_location.xml
index 57dbb8c76..0c8250cee 100644
--- a/app/src/main/res/layout/layout_dialog_location.xml
+++ b/app/src/main/res/layout/layout_dialog_location.xml
@@ -1,6 +1,6 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_dialog_placeslist.xml b/app/src/main/res/layout/layout_dialog_placeslist.xml
new file mode 100644
index 000000000..6794d6a92
--- /dev/null
+++ b/app/src/main/res/layout/layout_dialog_placeslist.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_dialog_schedalarm.xml b/app/src/main/res/layout/layout_dialog_schedalarm.xml
index b13f986e6..9528e8eb5 100644
--- a/app/src/main/res/layout/layout_dialog_schedalarm.xml
+++ b/app/src/main/res/layout/layout_dialog_schedalarm.xml
@@ -1,6 +1,6 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_listitem_actions.xml b/app/src/main/res/layout/layout_listitem_actions.xml
new file mode 100644
index 000000000..cf4d2373b
--- /dev/null
+++ b/app/src/main/res/layout/layout_listitem_actions.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_listitem_alarmclock.xml b/app/src/main/res/layout/layout_listitem_alarmclock.xml
index 66a1475fd..311d19ad9 100644
--- a/app/src/main/res/layout/layout_listitem_alarmclock.xml
+++ b/app/src/main/res/layout/layout_listitem_alarmclock.xml
@@ -87,7 +87,8 @@
app:layout_constraintBottom_toTopOf="@+id/layout_options"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/layout_header" />
+ app:layout_constraintTop_toBottomOf="@+id/layout_header"
+ android:transitionName="transition_alarmitem" />
+ app:layout_constraintStart_toStartOf="@+id/text_event"
+ app:layout_constraintEnd_toEndOf="@+id/text_location"
+ app:layout_constraintTop_toBottomOf="@+id/text_location" />
+ app:layout_constraintStart_toEndOf="@+id/text_event" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_listitem_alarmclock2.xml b/app/src/main/res/layout/layout_listitem_alarmclock2.xml
new file mode 100644
index 000000000..f88595f16
--- /dev/null
+++ b/app/src/main/res/layout/layout_listitem_alarmclock2.xml
@@ -0,0 +1,235 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_listitem_alarmtime.xml b/app/src/main/res/layout/layout_listitem_alarmtime.xml
new file mode 100644
index 000000000..121232241
--- /dev/null
+++ b/app/src/main/res/layout/layout_listitem_alarmtime.xml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_listitem_alarmtype.xml b/app/src/main/res/layout/layout_listitem_alarmtype.xml
new file mode 100644
index 000000000..53a6d27f3
--- /dev/null
+++ b/app/src/main/res/layout/layout_listitem_alarmtype.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_listitem_alarmtz.xml b/app/src/main/res/layout/layout_listitem_alarmtz.xml
new file mode 100644
index 000000000..6c5aeefe0
--- /dev/null
+++ b/app/src/main/res/layout/layout_listitem_alarmtz.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_listitem_color.xml b/app/src/main/res/layout/layout_listitem_color.xml
new file mode 100644
index 000000000..982ba94c8
--- /dev/null
+++ b/app/src/main/res/layout/layout_listitem_color.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_listitem_places.xml b/app/src/main/res/layout/layout_listitem_places.xml
new file mode 100644
index 000000000..e8f2be78d
--- /dev/null
+++ b/app/src/main/res/layout/layout_listitem_places.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_pref_action.xml b/app/src/main/res/layout/layout_pref_action.xml
new file mode 100644
index 000000000..9e5de711d
--- /dev/null
+++ b/app/src/main/res/layout/layout_pref_action.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_settings.xml b/app/src/main/res/layout/layout_settings.xml
index ddc95d23e..c64001e34 100644
--- a/app/src/main/res/layout/layout_settings.xml
+++ b/app/src/main/res/layout/layout_settings.xml
@@ -91,7 +91,7 @@
+ android:layout_width="match_parent" android:layout_height="wrap_content" />
diff --git a/app/src/main/res/layout/layout_settings_action.xml b/app/src/main/res/layout/layout_settings_action.xml
index ccd4805f7..b16b47f95 100644
--- a/app/src/main/res/layout/layout_settings_action.xml
+++ b/app/src/main/res/layout/layout_settings_action.xml
@@ -44,33 +44,14 @@
-
-
-
-
-
-
-
-
+
diff --git a/app/src/main/res/layout/layout_settings_location2.xml b/app/src/main/res/layout/layout_settings_location2.xml
index 2236f1123..19fbe3b9e 100644
--- a/app/src/main/res/layout/layout_settings_location2.xml
+++ b/app/src/main/res/layout/layout_settings_location2.xml
@@ -1,6 +1,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_view_place.xml b/app/src/main/res/layout/layout_view_place.xml
new file mode 100644
index 000000000..d63b70b93
--- /dev/null
+++ b/app/src/main/res/layout/layout_view_place.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/alarmclock.xml b/app/src/main/res/menu/alarmclock.xml
index 14cfdf745..b0d52019f 100644
--- a/app/src/main/res/menu/alarmclock.xml
+++ b/app/src/main/res/menu/alarmclock.xml
@@ -3,25 +3,23 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
-
-
+ app:showAsAction="never"
+ android:orderInCategory="997" />
+ app:showAsAction="never"
+ android:orderInCategory="998" />-->
+ app:showAsAction="never"
+ android:orderInCategory="999" />
diff --git a/app/src/main/res/menu/alarmcontext.xml b/app/src/main/res/menu/alarmcontext.xml
index 5e76de8ad..919c0e512 100644
--- a/app/src/main/res/menu/alarmcontext.xml
+++ b/app/src/main/res/menu/alarmcontext.xml
@@ -48,5 +48,10 @@
android:icon="?attr/icActionAlarm"
app:showAsAction="never" />
+
+
diff --git a/app/src/main/res/menu/alarmcontext1.xml b/app/src/main/res/menu/alarmcontext1.xml
new file mode 100644
index 000000000..1787d936d
--- /dev/null
+++ b/app/src/main/res/menu/alarmcontext1.xml
@@ -0,0 +1,13 @@
+
+
diff --git a/app/src/main/res/menu/alarmedit.xml b/app/src/main/res/menu/alarmedit.xml
new file mode 100644
index 000000000..764bf1619
--- /dev/null
+++ b/app/src/main/res/menu/alarmedit.xml
@@ -0,0 +1,37 @@
+
+
diff --git a/app/src/main/res/menu/alarmlist.xml b/app/src/main/res/menu/alarmlist.xml
new file mode 100644
index 000000000..2fafd126e
--- /dev/null
+++ b/app/src/main/res/menu/alarmlist.xml
@@ -0,0 +1,37 @@
+
+
diff --git a/app/src/main/res/menu/editintent.xml b/app/src/main/res/menu/editintent.xml
new file mode 100644
index 000000000..0c55a452f
--- /dev/null
+++ b/app/src/main/res/menu/editintent.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/editintent1.xml b/app/src/main/res/menu/editintent1.xml
new file mode 100644
index 000000000..beeaa3c3f
--- /dev/null
+++ b/app/src/main/res/menu/editintent1.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/editintent2.xml b/app/src/main/res/menu/editintent2.xml
new file mode 100644
index 000000000..bbc15b0db
--- /dev/null
+++ b/app/src/main/res/menu/editintent2.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/placesactivity.xml b/app/src/main/res/menu/placesactivity.xml
new file mode 100644
index 000000000..43aa52f75
--- /dev/null
+++ b/app/src/main/res/menu/placesactivity.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/placescontext.xml b/app/src/main/res/menu/placescontext.xml
new file mode 100644
index 000000000..32b6bc4c8
--- /dev/null
+++ b/app/src/main/res/menu/placescontext.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/placesedit.xml b/app/src/main/res/menu/placesedit.xml
new file mode 100644
index 000000000..48c411b46
--- /dev/null
+++ b/app/src/main/res/menu/placesedit.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/placeslist.xml b/app/src/main/res/menu/placeslist.xml
new file mode 100644
index 000000000..82aa1db8f
--- /dev/null
+++ b/app/src/main/res/menu/placeslist.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/widgetlist.xml b/app/src/main/res/menu/widgetlist.xml
index 2ff21cdd3..09c5ab474 100644
--- a/app/src/main/res/menu/widgetlist.xml
+++ b/app/src/main/res/menu/widgetlist.xml
@@ -8,6 +8,11 @@
android:title="@string/configLabel_widgetThemeList"
app:showAsAction="ifRoom" />
+
+
@string/trackingMode_soonest
- No fer res
- Mostrar nota següent
- Mostrar nota anterior
- Reset note
- Mostra aplicació de calendari
- Intercanviar targetes (avui/demà)
- Posar una alarma a la nota
+ No fer res
+ Mostrar nota següent
+ Mostrar nota anterior
+ Reset note
+ Mostra aplicació de calendari
+ Intercanviar targetes (avui/demà)
+ Posar una alarma a la notaper desfase
@@ -989,6 +989,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnightsortida de sol astronòmica
@@ -1016,6 +1018,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnight
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 928858849..8ba9a454c 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -785,13 +785,13 @@
Helles Thema / dunkles Thema (Nachtmodus)
- Nichts tun
- Nächste Kurzinfo anzeigen
- Vorherige Kurzinfo anzeigen
- Reset note
- Öffne Kalender-App
- Ansicht tauschen (heute/morgen)
- Alarm basierend auf Kurzinfo einstellen
+ Nichts tun
+ Nächste Kurzinfo anzeigen
+ Vorherige Kurzinfo anzeigen
+ Reset note
+ Öffne Kalender-App
+ Ansicht tauschen (heute/morgen)
+ Alarm basierend auf Kurzinfo einstellennach Versatz
@@ -924,6 +924,8 @@
summer solsticeHerbst-Tag-Nacht-GleicheWintersonnenwende
+ lunar noon
+ lunar midnightAstronomische Morgendämmerung (Beginn)
@@ -951,6 +953,8 @@
summer solsticeHerbst-Tag-Nacht-GleicheWintersonnenwende
+ lunar noon
+ lunar midnight
@@ -1060,7 +1064,7 @@
%1$s, und %2$sDanksagungen
-
+
Bibliotheken: ]]>%1$s
diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml
index 1efab94f8..8055ddd50 100644
--- a/app/src/main/res/values-eo/strings.xml
+++ b/app/src/main/res/values-eo/strings.xml
@@ -778,13 +778,13 @@
Hela (tage) / malhela (nokte)
- Fari nenion
- Montri sekvan noton
- Montri antaŭan noton
- Restarigi noton
- Montri aplikaĵon de kalendaro
- Baskuli langetojn (hodiaŭ/morgaŭ)
- Agordi averton pri noto
+ Fari nenion
+ Montri sekvan noton
+ Montri antaŭan noton
+ Restarigi noton
+ Montri aplikaĵon de kalendaro
+ Baskuli langetojn (hodiaŭ/morgaŭ)
+ Agordi averton pri notoTempolimo de GPS
@@ -881,6 +881,8 @@
somera solsticoaŭtuna ekvinoksovintra solstico
+ lunar noon
+ lunar midnightastronomia sunleviĝo
@@ -908,6 +910,8 @@
somera sunekstremoaŭtuna tagnoktegalecovintra sunekstremo
+ lunar noon
+ lunar midnight
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index ae6c11137..fad66430a 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -796,13 +796,13 @@
- No hacer nada
- Mostrar nota siguiente
- Mostrar nota anterior
- Reset note
- Muestra aplicación de calendario
- Intercambiar tarjetas (hoy/mañana)
- Añadir alarma a la nota
+ No hacer nada
+ Mostrar nota siguiente
+ Mostrar nota anterior
+ Reset note
+ Muestra aplicación de calendario
+ Intercambiar tarjetas (hoy/mañana)
+ Añadir alarma a la notapor desfase
@@ -991,6 +991,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnightAmanecer astronómico
@@ -1018,6 +1020,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnight
diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml
index a4cd8b2b7..a608e3b40 100644
--- a/app/src/main/res/values-eu/strings.xml
+++ b/app/src/main/res/values-eu/strings.xml
@@ -802,13 +802,13 @@
@string/trackingMode_soonest
- Ez egin ezer
- Hurrengo oharra erakutsi
- Aurreko oharra erakutsi
- Reset note
- Egutegiaren aplikazioa erakutsi
- Fitxak alderantzikatu (gaur/bihar)
- Alarma ezarri oharrarentzako
+ Ez egin ezer
+ Hurrengo oharra erakutsi
+ Aurreko oharra erakutsi
+ Reset note
+ Egutegiaren aplikazioa erakutsi
+ Fitxak alderantzikatu (gaur/bihar)
+ Alarma ezarri oharrarentzakokonpentsazioaren arabera
@@ -996,6 +996,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnightgoizeko krepuskulu astronomikoa (hasiera)
@@ -1023,6 +1025,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnight
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 7baeec52f..dcc21b024 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -773,13 +773,13 @@
Automatique (jour/nuit)
- Ne rien faire
- Affichage suivant
- Affichage précédent
- Reset note
- Afficher le calendrier
- Inverser (aujourd\'hui/demain)
- Régler l\'alarme
+ Ne rien faire
+ Affichage suivant
+ Affichage précédent
+ Reset note
+ Afficher le calendrier
+ Inverser (aujourd\'hui/demain)
+ Régler l\'alarmepar décalage
@@ -966,6 +966,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnightcrépuscule astronomique du matin
@@ -993,6 +995,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnight
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 7fb57348a..cf3cfe7b0 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -778,13 +778,13 @@
@string/timeFormatMode_24hr
- Semmit se tesz
- Mutasd a következő bejegyzést
- Mutasd az előző bejegyzést
- Reset note
- Mutassa a naptárat
- Vált (ma/holnap között)
- Emlékeztető beállítása a bejegyzéshez
+ Semmit se tesz
+ Mutasd a következő bejegyzést
+ Mutasd az előző bejegyzést
+ Reset note
+ Mutassa a naptárat
+ Vált (ma/holnap között)
+ Emlékeztető beállítása a bejegyzéshezIdőeltolódás szerint
@@ -971,6 +971,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnightreggel csillagászati szürkület (kezdete)
@@ -998,6 +1000,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnight
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 4fdaf0806..2cf9e37c7 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -809,13 +809,13 @@
@string/trackingMode_soonest
- Do nothing
- Show next note
- Show previous note
- Reset note
- Show calendar app
- Swap cards (today/tomorrow)
- Set alarm for note
+ Do nothing
+ Show next note
+ Show previous note
+ Reset note
+ Show calendar app
+ Swap cards (today/tomorrow)
+ Set alarm for noteper compensazione
@@ -1003,6 +1003,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnightmorning astronomical twilight (start)
@@ -1030,6 +1032,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnight
diff --git a/app/src/main/res/values-ldrtl-v21/styles.xml b/app/src/main/res/values-ldrtl-v21/styles.xml
new file mode 100644
index 000000000..5c0c7681a
--- /dev/null
+++ b/app/src/main/res/values-ldrtl-v21/styles.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml
index d64acd80d..d1e6c9539 100644
--- a/app/src/main/res/values-nb/strings.xml
+++ b/app/src/main/res/values-nb/strings.xml
@@ -789,13 +789,13 @@
Lyst tema / Mørkt tema (nattmodus)
- Ikke gjør noe
- Vis neste notis
- Vis forrige notis
- Tilbakestill notis
- Vis kalender-app
- Bytt visning (i dag / i morgen)
- Still inn alarm for notis
+ Ikke gjør noe
+ Vis neste notis
+ Vis forrige notis
+ Tilbakestill notis
+ Vis kalender-app
+ Bytt visning (i dag / i morgen)
+ Still inn alarm for notisetter forskyvning
@@ -983,6 +983,8 @@
sommersolvervhøstjevndøgnvintersolverv
+ lunar noon
+ lunar midnightastronomisk tussmørke morgen (start)
@@ -1010,6 +1012,8 @@
sommersolvervhøstjevndøgnvintersolverv
+ lunar noon
+ lunar midnight
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 50c7a3977..09bb809e8 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -790,13 +790,13 @@
Jasna skórka (dzień) / Ciemna skórka (noc)
- Nie rób nic
- Pokaż następną notatkę
- Pokaż poprzednią notatkę
- Resetuj notatkę
- Uruchom aplikację Kalendarz
- Przełącz karty (dziś/jutro)
- Ustaw alarm dla notatki
+ Nie rób nic
+ Pokaż następną notatkę
+ Pokaż poprzednią notatkę
+ Resetuj notatkę
+ Uruchom aplikację Kalendarz
+ Przełącz karty (dziś/jutro)
+ Ustaw alarm dla notatkiwg przesunięcia
@@ -975,7 +975,9 @@
równonoc wiosennaprzesilenie letnierównonoc jesienna
- przesilenie zimowe
+ przesilenie zimowe
+ lunar noon
+ lunar midnightświt astronomiczny
@@ -1003,6 +1005,8 @@
przesilenie letnierównonoc jesiennaprzesilenie zimowe
+ lunar noon
+ lunar midnight
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 49074a337..0e8e05f07 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -845,13 +845,13 @@
@string/trackingMode_soonest
- Fazer nada
- Exibir próxima nota
- Exibir nota anterior
- Reset note
- Mostrar app de calendário
- Inverter (hoje/amanhã)
- Definir alarme para a nota
+ Fazer nada
+ Exibir próxima nota
+ Exibir nota anterior
+ Reset note
+ Mostrar app de calendário
+ Inverter (hoje/amanhã)
+ Definir alarme para a notapor offset
@@ -1039,6 +1039,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnightcrepúsculo astronômico da manhã (início)
@@ -1066,6 +1068,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnight
diff --git a/app/src/main/res/values-v11/styles.xml b/app/src/main/res/values-v11/styles.xml
new file mode 100644
index 000000000..95722ab96
--- /dev/null
+++ b/app/src/main/res/values-v11/styles.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/values-v14/styles.xml b/app/src/main/res/values-v14/styles.xml
index 3d4bd2f3f..983950ed0 100644
--- a/app/src/main/res/values-v14/styles.xml
+++ b/app/src/main/res/values-v14/styles.xml
@@ -3,4 +3,9 @@
+
+
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 000000000..c5c39e8f6
--- /dev/null
+++ b/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index ecb5cdfb9..ea1a35c7c 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -809,13 +809,13 @@
@string/trackingMode_soonest
- 什麼也不做
- 顯示下一個便簽
- 顯示上一個便簽
- Reset note
- 顯示日曆應用
- 切換卡片 (今天/明天)
- 為便簽設置鬧鐘
+ 什麼也不做
+ 顯示下一個便簽
+ 顯示上一個便簽
+ Reset note
+ 顯示日曆應用
+ 切換卡片 (今天/明天)
+ 為便簽設置鬧鐘by offset
@@ -1003,6 +1003,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnight天文曙光
@@ -1030,6 +1032,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnight
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 9f6e40aa5..3c43a05e0 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -13,10 +13,14 @@
+
+
+
+
@@ -73,6 +77,7 @@
+
@@ -80,6 +85,7 @@
+
@@ -88,13 +94,17 @@
+
+
+
+
@@ -108,10 +118,20 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 080827311..35515130f 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -34,11 +34,13 @@
@color/text_accent_light
+ #80009688@color/text_accent_light@color/grey_50@color/grey_100@color/text_accent_dark
+ #8080cbc4@color/text_accent_dark@color/grey_900@color/grey_800
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 11b7d422f..4f998bc8f 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -29,6 +29,14 @@
32dip8dip
+ 16dip
+ 8dip
+ 32dip
+ 6dip
+ 4dip
+
+ 56dip
+
-9dip9dip18dip
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 52692e146..b74ed5807 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -17,6 +17,7 @@
Show MapView location usingUnable to find map application!
+ CalendarSet AlarmSet Time ZoneSet Date
@@ -26,9 +27,11 @@
World MapMoonHelp
+ MoreSettingsAbout
+
@string/configAction_sunDialog
@@ -167,6 +170,8 @@
blue hourmoonrisemoonset
+ lunar noon
+ lunar midnightSunrise
@@ -421,6 +426,13 @@
Launch AppFlip Views
+ Activity
+ Broadcast
+ Service
+
+ More
+ Less
+
Action SettingsOn Tap:On Clock Tap
@@ -442,6 +454,7 @@
0Name:(optional)
+ Place NameGet LocationEdit
@@ -496,8 +509,60 @@
Show Solar Noon
- Activity:
- com.forrestguice.suntimeswidget.SuntimesActivity
+ ID:
+ Class:
+
+ Action Title
+ Short description
+ Type:
+ Action:
+ <none>
+ Data:
+ <none>
+ Mime:
+ text/plain
+ Extras:
+ ]]>
+
+ %1$s %2$s
+ (%1$s)
+ No Action
+
+ custom%1$s
+ Custom Action %1$s
+ ID must not be empty (or contain spaces).
+ Display string must not be empty.
+ ID is already taken. This action will be overwritten.
+
+ Are you sure you want to delete %1$s (%2$s)?
+ Default actions cannot be removed.\n\nRevert to defaults?
+ Delete
+ Revert
+ Cancel
+
+ Are you sure you want to reset to defaults?\n\nAll custom actions will be removed.
+ Clear
+ Actions reset to defaults.
+
+ Add Action
+ Save
+ Suggest
+ Saved action \"%1$s\" (%2$s).
+
+ Actions
+ Loaded action \"%1$s\".
+
+ Failed to start %s!
+
+ Add
+ Edit
+ Test
+ Save
+ Load
+ Delete
+ Clear All
+ Export
+ ImportThemes
@@ -641,8 +706,12 @@
No AlarmsSet AlarmSet Notification
+ EditDeleteClear
+ Sort
+ Alarm Time
+ Creation DateClearSet OffsetSet Type
@@ -651,8 +720,13 @@
Set RepeatingSet EventOverride Time
+ Set ActionSet SoundAlarm Menu
+ Save
+ Save & Enable
+ Save & Disable
+ UndoDismissSnooze
@@ -667,9 +741,14 @@
AlarmNotification
+ Schedule:
+ On Alert:
+ On Dismiss:
+
VibrateNo Sound[icon]%1$s
+ [icon]%1$sClock TimeRepeat
@@ -681,6 +760,12 @@
DeleteCancel%1$s at %2$s (%3$s) was deleted.
+ %1$s was deleted.
+
+ There are unsaved changes.
+ Discard
+ Cancel
+ SaveClear AlarmsAre you sure you want to delete all alarms?
@@ -691,6 +776,7 @@
LabelApplyCancel
+ LabelRepeat AlarmApply
@@ -706,6 +792,7 @@
Cancel%1$s set for %2$s from now.
+ %1$s in %2$sBattery optimizationNot optimized (recommended)
@@ -791,11 +878,24 @@
Places
- Export Places
- Clear Places
+ @string/configLabel_places
+ %s places
+ Export Places
+ Clear PlacesAdd World PlacesScan locales and add cities to the database.
+
+ Add
+ Copy
+ Select
+ Search
+ Share
+ Delete
+ Clear
+ Export
+
+
User Interface
@@ -873,12 +973,12 @@
- @string/configAction_doNothing
- @string/configAction_showCalendar
- @string/configAction_setAlarm
- @string/configAction_setDate
- @string/configAction_swapCards
- @string/configAction_resetNote
+ @string/configActionDesc_doNothing
+ @string/configActionDesc_showCalendar
+ @string/configActionDesc_setAlarm
+ @string/configActionDesc_setDate
+ @string/configActionDesc_swapCards
+ @string/configActionDesc_resetNoteNOTHING
@@ -891,13 +991,13 @@
- @string/configAction_doNothing
- @string/configAction_showCalendar
- @string/configAction_setAlarm
- @string/configAction_setTimeZone
- @string/configAction_nextNote
- @string/configAction_prevNote
- @string/configAction_resetNote
+ @string/configActionDesc_doNothing
+ @string/configActionDesc_showCalendar
+ @string/configActionDesc_setAlarm
+ @string/configActionDesc_setTimeZone
+ @string/configActionDesc_nextNote
+ @string/configActionDesc_prevNote
+ @string/configActionDesc_resetNoteNOTHING
@@ -911,11 +1011,11 @@
- @string/configAction_doNothing
- @string/configAction_setAlarmForNote
- @string/configAction_nextNote
- @string/configAction_prevNote
- @string/configAction_resetNote
+ @string/configActionDesc_doNothing
+ @string/configActionDesc_setAlarmForNote
+ @string/configActionDesc_nextNote
+ @string/configActionDesc_prevNote
+ @string/configActionDesc_resetNoteNOTHING
@@ -925,26 +1025,87 @@
RESET_NOTE
- Do nothing
- Show next note
- Show previous note
- Show upcoming event
- Open calendar
- Swap cards (today/tomorrow)
- Set alarm for note
+ Do nothing
+ Show next note
+ Show previous note
+ Show upcoming event
+ Open calendar
+ Show location on map
+ Set an alarm
+ World Map Dialog
+ Solstices Dialog
+ Moon Dialog
+ Sun Dialog
+ Set date
+ Set time zone
+ Set location
+ Swap cards (today/tomorrow)
+ Show next card
+ Show previous card
+ Set alarm for note
+ Alarm List
+ Theme List
+ Action List
+ Widget List
+
+
+ @string/configActionDesc_doNothing
+ @string/app_name
+
+ @string/app_name
+ @string/app_name
+ @string/app_name
+
+ @string/app_name
+ @string/app_name
+ @string/app_name
+
+ @string/app_name
+ @string/app_name
+ @string/app_name
+
+ @string/app_name
+ @string/app_name
+ @string/app_name
+ @string/app_name
+
+ @string/app_name
+ @string/app_name
+ @string/app_name
+ @string/app_name
-
- @string/configAction_doNothing
- @string/configAction_swapCards@string/configAction_showCalendar
- @string/configAction_setDate
- @string/configAction_setAlarm
- @string/configAction_setTimeZone
- @string/configAction_nextNote
- @string/configAction_prevNote
- @string/configAction_resetNote
+ @string/configAction_mapLocation
+
+ @string/configActionDesc_doNothing
+ @string/configActionDesc_setAlarm
+
+ @string/configActionDesc_nextCard
+ @string/configActionDesc_prevCard
+ @string/configActionDesc_swapCards
+
+ @string/configActionDesc_nextNote
+ @string/configActionDesc_prevNote
+ @string/configActionDesc_resetNote
+
+ @string/configActionDesc_setDate
+ @string/configActionDesc_setLocation
+ @string/configActionDesc_setTimeZone
+
+ @string/configActionDesc_worldMap
+ @string/configActionDesc_equinoxDialog
+ @string/configActionDesc_moon
+ @string/configActionDesc_sunDialog
+ @string/configActionDesc_alarmList
+ @string/configActionDesc_themeList
+ @string/configActionDesc_actionList
+ @string/configActionDesc_widgetList
+
+ @string/configActionDesc_showCalendar
+ @string/configActionDesc_mapLocation
+ by offset
@@ -1174,13 +1335,17 @@
Schedule an alarm forSchedule a notification for@string/configAction_setAlarm
+ Event
+ TimeScheduleCancelAlarm was not set…Alarm cannot be set…about %s from now.
+ ~%s from now%s does not occur.%1$s (%2$s)
+ %1$sastronomical twilightnautical twilight
@@ -1207,6 +1372,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnightmorning astronomical twilight (start)
@@ -1234,6 +1401,8 @@
summer solsticeautumn equinoxwinter solstice
+ lunar noon
+ lunar midnight
@@ -1272,28 +1441,38 @@
This is not a valid latitude.This is not a valid longitude.This is not a valid elevation.
+ Enter a place name.Copied to clipboard]]>%1$s
+
+ Remove Place?
+ Remove Places?
+ Are you sure you want to remove %s?
+ Place was removed.
+ %s places were removed.
+ Remove
+ Cancel
+
Clear Places?
- Are you sure you want to clear the database of places?
+ Are you sure you want to clear the list of places?ClearCancelClearing Places
- Clearing the database…
- The database of places has been cleared.
+ Clearing the list of places…
+ The list of places has been cleared.Building Database
- Building a database of places…
- Added %1$s places to the database.
+ Building a list of places…
+ Added %1$s places.Exporting Places
- Exporting the database to csv file.
+ Exporting places to csv file.Exporting Themes
@@ -1321,6 +1500,14 @@
Choose ColourSelectCancel
+ 1
+ 2
+ 3
+ R
+ G
+ B
+ A
+ 0–255About
@@ -1378,6 +1565,7 @@
* %dY for year (e.g. 2018)
* %dt for time (of last update)
* %dT for time with seconds (of last update)
+ * %dm for milliseconds (of last update)
* %loc for label (e.g. Prescott)
* %lat for latitude (e.g. 35.5409)
@@ -1475,8 +1663,25 @@
Blue Hour: The sun is between 8 degrees and 4 degrees below the horizon. The start and end of the blue hour falls between nautical and civil twilight.
]]>
- ]]>
+ See Intents and Intent Filters (https://developer.android.com/guide/components/intents-filters).
+
+ All fields are optional (values may be left empty for an implicit intent).
+
+
+ Action: An action string. e.g. android.intent.action.VIEW
+
+
+ Class: A fully qualified class name. e.g. suntimeswidget.SuntimesActivity
+
+
+ Data: A Uri that contains or points to attached data. Supports %substitutions. e.g. geo:%lat,%lon
+
+
+ Mime: The mime type of attached data.
+
+
+ Extras: An & delimited string containing extra values. Supports %substitutions.
Values may be int, long, double, float, boolean or String. e.g. key1=value1 & int=1 & long=1L & double=1D & float=1F & bool=true & etc=%dm
+ ]]>add or remove a widget you must navigate to the home screen.