/* * Copyright (c) 2016 LibreTasks - https://github.com/biotinker/LibreTasks * * This file is free software: you may copy, redistribute 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. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * This file incorporates work covered by the following copyright and * permission notice: /******************************************************************************* * Copyright 2009, 2010 Omnidroid - http://code.google.com/p/omnidroid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package libretasks.app.view.simple; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.AlertDialog.Builder; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.DialogInterface.OnDismissListener; import android.content.res.Resources; import android.graphics.Typeface; import android.os.Bundle; import android.text.Html; import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import android.widget.AdapterView.OnItemClickListener; import libretasks.app.R; import libretasks.app.controller.datatypes.DataType; import libretasks.app.controller.datatypes.OmniText; import libretasks.app.view.simple.factoryui.ActionParameterViewFactory; import libretasks.app.view.simple.model.ModelAction; import libretasks.app.view.simple.model.ModelAttribute; import libretasks.app.view.simple.model.ModelRuleAction; import libretasks.app.view.simple.viewitem.ViewItem; import libretasks.app.view.simple.viewitem.ViewItemGroup; /** * This dialog is a shell to contain UI elements specific to different actions. Given an action ID, * we can construct the inner UI elements using {@link ActionParameterViewFactory}. */ public class ActivityDlgActionInput extends Activity { private static final String TAG = ActivityDlgActionInput.class.getSimpleName(); /** Layout dynamically generated on our action type by FactoryActions. */ private LinearLayout llContent; // Container for the dynamically created view private ViewItemGroup viewItems; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Link up controls from the xml layout resource file. initializeUI(savedInstanceState); } @Override protected void onSaveInstanceState(Bundle bundle) { viewItems.saveState(bundle); } private void initializeUI(Bundle bundle) { setContentView(R.layout.activity_dlg_action_input); Button btnOk = (Button) findViewById(R.id.activity_dlg_action_input_btnOk); btnOk.setOnClickListener(listenerBtnClickOk); Button btnAttributes = (Button) findViewById(R.id.activity_dlg_action_input_btnAttributes); btnAttributes.setOnClickListener(listenerBtnClickAttributes); Button btnHelp = (Button) findViewById(R.id.activity_dlg_action_input_btnHelp); btnHelp.setOnClickListener(listenerBtnClickHelp); llContent = (LinearLayout) findViewById(R.id.activity_dlg_action_input_llDynamicContent); // Add dynamic content now based on our action type. ModelAction modelAction = RuleBuilder.instance().getChosenModelAction(); ArrayList<DataType> ruleActionDataOld = RuleBuilder.instance().getChosenRuleActionDataOld(); viewItems = ActionParameterViewFactory.buildUIFromAction(modelAction, ruleActionDataOld, this); llContent.addView(viewItems.getLayout()); ArrayList<View> textEdits = llContent.getFocusables(View.FOCUS_FORWARD); for (View t : textEdits) { t.setOnFocusChangeListener(editTextFocusChangeListener); } try { viewItems.loadState(bundle); } catch (Exception e) { Log.e(TAG, "Failed during loadState", e); } setTitle(modelAction.getTypeName()); } private View.OnClickListener listenerBtnClickOk = new View.OnClickListener() { public void onClick(View v) { // Have the listener try to construct a full ModelRuleAction for us now // based on our dynamic UI content. ModelRuleAction action; try { action = ActionParameterViewFactory.buildActionFromUI(RuleBuilder.instance() .getChosenModelAction(), viewItems); } catch (Exception ex) { // TODO: (markww) Make sure DataType classes are providing meaningful error output, then // remove the static string below and only use the contents of the exception. Resources resource = v.getContext().getResources(); UtilUI.showAlert(v.getContext(), resource.getString(R.string.sorry), resource .getString(R.string.bad_data_format) + ex.getMessage()); return; } // Set our constructed action so the parent activity can pick it up. RuleBuilder.instance().setChosenRuleAction(action); finish(); } }; private View.OnClickListener listenerBtnClickAttributes = new View.OnClickListener() { public void onClick(View v) { /* * For the selected control, try to pop up a list of attribute parameters that can be used in * the action based on the data type. For example, if the user has an OmniText UI control * selected, and they hit this button, pop up a list of all attributes from the root event * that are also OmniText. */ ViewItem focusedItem = getFocusedItem(); if (focusedItem != null) { showDialogAttributes(focusedItem); } else { Resources resource = v.getContext().getResources(); UtilUI.showAlert(v.getContext(), resource.getString(R.string.sorry), resource .getString(R.string.select_control_for_param_alert_inst)); } } }; private View.OnClickListener listenerBtnClickHelp = new View.OnClickListener() { public void onClick(View v) { Builder help = new AlertDialog.Builder(v.getContext()); help.setIcon(android.R.drawable.ic_menu_help); help.setTitle(R.string.help); help.setMessage(Html.fromHtml(getString(R.string.help_dlgactioninput))); help.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }); help.show(); } }; /** * Get the current item on focus. This method can only get the focused item if it was created * dynamically by the factory. * * @return the {@link ViewItem} instance that is currently being focused. null otherwise */ private ViewItem getFocusedItem() { View viewFocused = viewItems.getLayout().getFocusedChild(); if (viewFocused == null) { return null; } else { return viewItems.get(viewFocused.getId()); } } /** * For a selected event, retrieve any attributes that could be useful for the action * * @param viewItem * -the selected view item * @return List of attributes applicable to the view item */ private List<ModelAttribute> getAttributes(final ViewItem viewItem) { long datatypeId = viewItem.getDataTypeDbID(); // Get all attributes that have the same data type ID. List<ModelAttribute> attributes = UIDbHelperStore.instance().db().getAttributesForEvent( RuleBuilder.instance().getChosenEvent()); List<ModelAttribute> attributesValid; if (datatypeId != UIDbHelperStore.instance().getDatatypeLookup() .getDataTypeID(OmniText.DB_NAME)) { attributesValid = new ArrayList<ModelAttribute>(); for (int i = 0; i < attributes.size(); i++) { ModelAttribute attribute = attributes.get(i); if (attribute.getDatatype() == datatypeId) { attributesValid.add(attribute); } } } else { attributesValid = attributes; } return attributesValid; } /** * Check whether the selected {@code viewItem} has any attributes * * @param viewItem * the view item in question * @return true if there are attributes related to the view item */ private boolean checkForAttributes(final ViewItem viewItem) { return !getAttributes(viewItem).isEmpty(); } /** * If an input field gains focus, enable or disable the attributes button if there are any * attributes from the original event. */ private View.OnFocusChangeListener editTextFocusChangeListener = new View.OnFocusChangeListener() { public void onFocusChange(View v, boolean hasFocus) { Button btnAttributes = (Button) findViewById(R.id.activity_dlg_action_input_btnAttributes); if (hasFocus) { btnAttributes.setEnabled(checkForAttributes(getFocusedItem())); } } }; /** * Show the attributes dialog for the specified {@code viewItem} if applicable * * @param viewItem * the item chosen to show the attributes */ private void showDialogAttributes(final ViewItem viewItem) { ArrayList<ModelAttribute> attributesValid = (ArrayList<ModelAttribute>) getAttributes(viewItem); // Show the dialog finally if they have any choice. if (!attributesValid.isEmpty()) { DlgAttributes dlg = new DlgAttributes(this, attributesValid); dlg.setOnDismissListener(new OnDismissListener() { public void onDismiss(DialogInterface dialog) { // Fetch the attribute they chose, if any. ModelAttribute modelAttribute = ((DlgAttributes) dialog).getSelectedAttribute(); if (modelAttribute != null) { viewItem.insertAttribute(modelAttribute); } } }); dlg.show(); } else { // Shouldn't happen but leaving in for safety UtilUI.showAlert(this, "Sorry!", "There are no matching parameters for the selected attribute type!"); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); viewItems.onActivityResult(requestCode, resultCode, data); } /** * Shows attributes for the root event that can work as parameters for the action. */ private static class DlgAttributes extends Dialog { private ListView listView; private int selectedIndex; private AdapterAttributes adapterAttributes; public DlgAttributes(Context context, ArrayList<ModelAttribute> attributes) { super(context); selectedIndex = -1; setContentView(R.layout.dlg_attributes_for_action); setTitle("Attributes"); adapterAttributes = new AdapterAttributes(getContext(), attributes); listView = (ListView) findViewById(R.id.dlg_attributes_for_action_listview); listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); listView.setAdapter(adapterAttributes); listView.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View v, int position, long id) { selectedIndex = position; dismiss(); } }); UtilUI.inflateDialog((LinearLayout) findViewById(R.id.dlg_attributes_for_action_ll_main)); } public ModelAttribute getSelectedAttribute() { return adapterAttributes.getItem(selectedIndex); } public class AdapterAttributes extends BaseAdapter { private Context context; private final List<ModelAttribute> attributes; public AdapterAttributes(Context context, List<ModelAttribute> attributes) { this.context = context; this.attributes = attributes; } public int getCount() { return attributes.size(); } public ModelAttribute getItem(int position) { ModelAttribute item; try { item = attributes.get(position); } catch (IndexOutOfBoundsException ex) { item = null; } return item; } public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { LinearLayout ll = new LinearLayout(context); ll.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); ll.setMinimumHeight(50); ll.setOrientation(LinearLayout.HORIZONTAL); ll.setGravity(Gravity.CENTER_VERTICAL); ImageView iv = new ImageView(context); iv.setImageResource(attributes.get(position).getIconResId()); iv.setAdjustViewBounds(true); iv.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); if (listView.getCheckedItemPosition() == position) { iv.setBackgroundResource(R.drawable.icon_hilight); } TextView tv = new TextView(context); tv.setText(attributes.get(position).getDescriptionShort()); tv.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); tv.setGravity(Gravity.CENTER_VERTICAL); tv.setPadding(10, 0, 0, 0); tv.setTextSize(14.0f); tv.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); tv.setTextColor(0xFFFFFFFF); tv.setMinHeight(46); ll.addView(iv); ll.addView(tv); return ll; } } } }