diff --git a/contrib/mobile/Android/res/layout/activity_model.xml b/contrib/mobile/Android/res/layout/activity_model.xml new file mode 100644 index 0000000000000000000000000000000000000000..285d00ebbad157ac0262e102e184b70eaa90fce1 --- /dev/null +++ b/contrib/mobile/Android/res/layout/activity_model.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/model_fragment" + android:name="org.geuz.onelab.ModelFragment" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginLeft="0dp" + android:layout_marginRight="0dp" + tools:context=".MainActivity" /> \ No newline at end of file diff --git a/contrib/mobile/Android/res/layout/activity_twopane.xml b/contrib/mobile/Android/res/layout/activity_twopane.xml new file mode 100644 index 0000000000000000000000000000000000000000..842d0b543e4a2e22f49d0ed871e7710d397cde9a --- /dev/null +++ b/contrib/mobile/Android/res/layout/activity_twopane.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:baselineAligned="false" + android:divider="?android:attr/dividerHorizontal" + android:orientation="horizontal" + android:showDividers="middle" + tools:context=".MainActivity" > + + <FrameLayout android:id="@+id/parameter_fragment" + android:name="org.geuz.onelab.OptionsDisplayFragment" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" /> + + <FrameLayout android:id="@+id/model_fragment" + android:name="org.geuz.onelab.ModelFragment" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_marginLeft="0dp" + android:layout_marginRight="0dp" + android:layout_weight="3" /> + +</LinearLayout> \ No newline at end of file diff --git a/contrib/mobile/Android/res/layout/fragment_model.xml b/contrib/mobile/Android/res/layout/fragment_model.xml new file mode 100644 index 0000000000000000000000000000000000000000..0b1475fcfd336d03bccf9a3f920dca16c0c7e5b6 --- /dev/null +++ b/contrib/mobile/Android/res/layout/fragment_model.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:id="@+id/glViewLayout" /> \ No newline at end of file diff --git a/contrib/mobile/Android/res/layout/fragment_options.xml b/contrib/mobile/Android/res/layout/fragment_options.xml new file mode 100644 index 0000000000000000000000000000000000000000..d184171ce0a19a5ca9e19a1fcd2d3185ab991d65 --- /dev/null +++ b/contrib/mobile/Android/res/layout/fragment_options.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:padding="4dip" + android:gravity="center_horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <FrameLayout android:id="@+id/options_fragment" + android:name="org.geuz.onelab.OptionsModelFragment" + android:layout_width="match_parent" + android:layout_height="0px" + android:layout_weight="1" /> + + <View android:layout_width="fill_parent" + android:layout_height="1dp" + android:background="@android:color/darker_gray" /> + <LinearLayout android:orientation="horizontal" + android:gravity="center" android:measureWithLargestChild="true" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="0"> + <Button android:id="@+id/goto_options_model" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Model"> + </Button> + <Button android:id="@+id/goto_options_display" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Display"> + </Button> + </LinearLayout> + +</LinearLayout> \ No newline at end of file diff --git a/contrib/mobile/Android/res/layout/fragment_options_display.xml b/contrib/mobile/Android/res/layout/fragment_options_display.xml new file mode 100644 index 0000000000000000000000000000000000000000..860d37227bc80f874cc534d07fb8150493ebea03 --- /dev/null +++ b/contrib/mobile/Android/res/layout/fragment_options_display.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<org.geuz.onelab.SeparatedListView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="48dp" + android:id="@+id/displayOptionsList"> + + +</org.geuz.onelab.SeparatedListView> \ No newline at end of file diff --git a/contrib/mobile/Android/src/org/geuz/onelab/GLESRender.java b/contrib/mobile/Android/src/org/geuz/onelab/GLESRender.java index 201be57e514d91eef2e25fa039d2a90d305650b3..a53a5cdab25932ceb321f0de5ef3ef4be566c035 100644 --- a/contrib/mobile/Android/src/org/geuz/onelab/GLESRender.java +++ b/contrib/mobile/Android/src/org/geuz/onelab/GLESRender.java @@ -51,7 +51,6 @@ public class GLESRender implements Renderer{ } public void onSurfaceCreated(GL10 gl, EGLConfig config) { - } } diff --git a/contrib/mobile/Android/src/org/geuz/onelab/MainActivity.java b/contrib/mobile/Android/src/org/geuz/onelab/MainActivity.java index bfa0e5be23ac907f5137cf74b962852c80393f00..9d99dc71b58239f7c076514cb5718783d407b79a 100644 --- a/contrib/mobile/Android/src/org/geuz/onelab/MainActivity.java +++ b/contrib/mobile/Android/src/org/geuz/onelab/MainActivity.java @@ -1,464 +1,125 @@ package org.geuz.onelab; -import java.util.ArrayList; -import java.util.List; - -import android.opengl.GLSurfaceView; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Handler; -import android.os.Vibrator; import android.app.ActionBar; import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; -import android.content.res.Configuration; import android.graphics.Color; -import android.graphics.Point; -import android.support.v4.view.PagerAdapter; -import android.support.v4.view.ViewPager; -import android.text.Editable; -import android.text.TextWatcher; -import android.util.DisplayMetrics; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.SubMenu; -import android.view.View.OnClickListener; -import android.view.ViewGroup.LayoutParams; -import android.view.KeyEvent; +import android.graphics.drawable.ColorDrawable; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.app.FragmentTransaction; import android.view.Menu; -import android.view.View; -import android.view.ViewGroup; +import android.view.MenuItem; +import android.view.Window; import android.view.WindowManager; -import android.view.inputmethod.InputMethodManager; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.ScrollView; -import android.widget.Spinner; -import android.widget.TableLayout; -import android.widget.TableRow; -import android.widget.TextView; - -public class MainActivity extends Activity { - private mGLSurfaceView glView; - private GLESRender renderer; - private ProgressDialog loading; - private AlertDialog.Builder dialogBuilder; - private ArrayList<String> errors; - private Dialog errorDialog; - private Gmsh gmsh; - private Button reset; - private MenuItem runStopItem; - private UndragableViewPager pager; - private List<Parameter> params = new ArrayList<Parameter>(); - private SeparatedListView paramListView; - private boolean compute = false; - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - ActionBar actionBar = getActionBar(); - actionBar.setDisplayHomeAsUpEnabled(true); - errors = new ArrayList<String>(); - LinearLayout layout = new LinearLayout(this); - loading = new ProgressDialog(this); - layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - layout.setOrientation(LinearLayout.VERTICAL); - pager = new UndragableViewPager(this); - pager.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - pager.setAdapter(new AdaptedPager()); - pager.setOffscreenPageLimit(2); - pager.setCurrentItem(1); - layout.addView(pager); - dialogBuilder = new AlertDialog.Builder(this); +public class MainActivity extends Activity implements OptionsDisplayFragment.OnOptionRequestRender{ - Intent intent = getIntent(); + private Gmsh _gmsh; + private boolean _compute, _twoPane; + private MenuItem _runStopMenuItem, _switchFragmentMenuItem; + private ModelFragment _modelFragment; + private OptionsFragment _optionsFragment; - gmsh = new Gmsh("", mainHandler); - Bundle extras = getIntent().getExtras(); + public MainActivity() { + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY); + setContentView(R.layout.main_activity_layout); + _gmsh = new Gmsh("", mainHandler); + ActionBar actionBar = getActionBar(); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#64000000"))); + Intent intent = getIntent(); + Bundle extras = intent.getExtras(); if(intent != null && intent.getAction() != null && intent.getAction().equals(Intent.ACTION_VIEW)) { String tmp = intent.getData().getPath(); - pager.setCurrentItem(1, true); - gmsh.load(tmp); + _gmsh.load(tmp); } else if(extras != null) { - pager.setCurrentItem(0, true); //extras.getInt("model"); //extras.getString("name"); String tmp = extras.getString("file"); - gmsh.load(tmp); - getAvailableParam(); - if(params.size()>0)params.get(params.size() - 1)._changed = true; // Hack for the first run + _gmsh.load(tmp); } else this.finish(); - setContentView(layout); - } - - @Override - protected void onDestroy() { - // TODO Unload library ? - super.onDestroy(); - } - - @Override + _twoPane = (findViewById(R.id.parameter_fragment) != null); + if(_twoPane) { + _optionsFragment = OptionsFragment.newInstance(_gmsh); + getFragmentManager().beginTransaction().add(R.id.parameter_fragment, _optionsFragment).commit(); + _modelFragment = ModelFragment.newInstance(_gmsh); + getFragmentManager().beginTransaction().add(R.id.model_fragment, _modelFragment).commit(); + } + else { + _optionsFragment = OptionsFragment.newInstance(_gmsh); + _modelFragment = ModelFragment.newInstance(_gmsh); + getFragmentManager().beginTransaction().add(R.id.model_fragment, _modelFragment).commit(); + } + } + @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); - - /* Menu: - * < (Back to model list) - * Model (show the model, only on small screen) - * Parameters - * > Model (show the model's parameters) - * > Display (show display options) - * Run/Stop (Compute / stop) - */ - SubMenu parametersItems = menu.addSubMenu(R.string.menu_parameters); - parametersItems.add(R.string.menu_parameters_model); - parametersItems.add(R.string.menu_parameters_display); - MenuItem parametersItem = parametersItems.getItem(); - parametersItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - if(screenInch() < 7) { - MenuItem modelItem = menu.add(R.string.menu_model); - modelItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + if(!_twoPane) { + _switchFragmentMenuItem = menu.add(R.string.menu_parameters); + _switchFragmentMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); } - runStopItem = menu.add(R.string.menu_run); - runStopItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + _runStopMenuItem = menu.add(R.string.menu_run); + _runStopMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); return true; } - @Override + @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { - if(item.getTitle().equals(getString(R.string.menu_parameters_model))) { - pager.setCurrentItem(0, true); - // TODO change fragment on pager 0 - } - else if (item.getTitle().equals(getString(R.string.menu_parameters_display))) { - pager.setCurrentItem(0, true); - // TODO change fragment on pager 0 + if (item.getTitle().equals(getString(R.string.menu_parameters))) { + item.setTitle(R.string.menu_model); + FragmentTransaction ft = getFragmentManager().beginTransaction(); + ft.replace(R.id.model_fragment, _optionsFragment); + ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + ft.commit(); } else if (item.getTitle().equals(getString(R.string.menu_model))) { - pager.setCurrentItem(1, true); + item.setTitle(R.string.menu_parameters); + FragmentTransaction ft = getFragmentManager().beginTransaction(); + ft.replace(R.id.model_fragment, _modelFragment); + ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE); + ft.commit(); } else if(item.getTitle().equals(getString(R.string.menu_run))){ - pager.setCurrentItem(1, true); - item.setTitle(R.string.menu_stop); - if(compute) { - loading.show(); - } - else { - new Run().execute(); - pager.setCurrentItem(1, true); + if(_switchFragmentMenuItem != null && _switchFragmentMenuItem.getTitle().equals(getString(R.string.menu_model))) { + _switchFragmentMenuItem.setTitle(R.string.menu_parameters); + FragmentTransaction ft = getFragmentManager().beginTransaction(); + ft.replace(R.id.model_fragment, _modelFragment); + ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE); + ft.commit(); } + new Run().execute(); } else if(item.getTitle().equals(getString(R.string.menu_stop))){ - gmsh.onelabCB("stop"); + _gmsh.onelabCB("stop"); } else if(item.getItemId() == android.R.id.home) { - if(this.compute) - loading.show(); + if(this._compute) + ;//TODO loading.show(); else this.finish(); } return super.onMenuItemSelected(featureId, item); } - - private class UndragableViewPager extends ViewPager{ - - public UndragableViewPager(Context context) { - super(context); - } - - @Override - public boolean onTouchEvent(MotionEvent arg0) { - return true; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent arg0) { - return false; - } - - } - - private class AdaptedPager extends PagerAdapter { - - @Override - public Object instantiateItem(ViewGroup container, int position) { - switch (position) { - case 0: // Parameters - (MainActivity.this).getAvailableParam(); - View paramView = (MainActivity.this).paramView(container.getContext()); - container.addView(paramView); - paramView.setPadding(15, 10, 10, 5); - return paramView; - case 1: // OpenGL ES view - renderer = new GLESRender(gmsh); - glView = new mGLSurfaceView(container.getContext(), renderer); - glView.setEGLContextClientVersion(1); - glView.setRenderer(renderer); - glView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); - glView.requestRender(); - container.addView(glView); - return glView; - default: // ??? - View defaultView = new View(container.getContext()); - defaultView.setBackgroundColor(Color.argb(255, 255, 0, 0)); - container.addView(defaultView); - return defaultView; - } - } - - @Override - public int getCount() { - return 3; - } - - @Override - public float getPageWidth(int position) { - if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) - { - if(screenInch() < 7) - return 1f; - switch (position) { - case 1: - return 0.25f; - case 2: - return 0.75f; - default: - return 1f; - } - } - - return 1f; - } - - @Override - public boolean isViewFromObject(View view, Object object) { - return(view == object); - } - - } - - public double screenInch() - { - Point size = new Point(); - getWindowManager().getDefaultDisplay().getSize(size); - int width = size.x; - int height = size.y; - DisplayMetrics dm = new DisplayMetrics(); - getWindowManager().getDefaultDisplay().getMetrics(dm); - double x = Math.pow(width/dm.xdpi,2); - double y = Math.pow(height/dm.ydpi,2); - return Math.sqrt(x+y); - } - - private void getAvailableParam(){ - String[] tmp = gmsh.getParams(); - for(String s : tmp){ // for each parameters in ONEALB - boolean found = false; - for(Parameter p : params){ // for each parameters - if(s.split("\n")[2].equals(p.getName())){ // the parameter already exist, just refresh it - if(p.getType().equals("ParameterNumber")) - ((ParameterNumber)p).fromString(s); - else if(p.getType().equals("ParameterString")) - ((ParameterString)p).fromString(s); - found = true; - break; - } - } - if(found) continue; - // add new parameter - if(s.split("\n")[1].equals("number")){ - final ParameterNumber mParam = new ParameterNumber(this, gmsh, glView, ""); - if(mParam.fromString(s) == -1) continue; - params.add(mParam); - if(paramListView != null){ - paramListView.addItem(mParam.getName().split("/")[0].equals("Parameters")? mParam.getName().split("/")[0] + " > " + mParam.getName().split("/")[1]: mParam.getName().split("/")[0], mParam.getView()); - } - } - else if(s.split("\n")[1].equals("string")){ - ParameterString mParam = new ParameterString(this, gmsh, glView, ""); - if(mParam.fromString(s) != -1){ - params.add(mParam); - if(paramListView != null) - paramListView.addItem(mParam.getName().split("/")[0], mParam.getView()); - } - } - } - } - - /*private View postproView() { - ScrollView scroll = new ScrollView(this); - scroll.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); - String[] PViews = gmsh.getPView(); - if(PViews.length < 1){ - LinearLayout layout = new LinearLayout(this); - TextView noPView = new TextView(this); - noPView.setText("There is no post processing data !"); - layout.addView(noPView); - return layout; - } - TableLayout table = new TableLayout(this); - table.setColumnShrinkable(0, true); - TableRow title = new TableRow(this); - TextView title_name = new TextView(this); - TextView title_intervalType = new TextView(this); - TextView title_intervals = new TextView(this); - title_name.setText("Name"); - title_intervals.setText("Intervals"); - title_intervalType.setText("Interval type"); - title.addView(title_name); - title.addView(title_intervalType); - title.addView(title_intervals); - table.addView(title); - for(int i=PViews.length-1; i >= 0;i--){ - TableRow row = new TableRow(this); - final int myID =i; - String[] infos = PViews[i].split("\n"); // name / IntervalsType (1=Iso 2=Continous 3=Discrete 4=Numeric) - CheckBox checkbox = new CheckBox(this); - final Spinner spinner = new Spinner(this); - final EditText nbIso = new EditText(this); - checkbox.setText(infos[0]); - checkbox.setChecked(infos[2].equals("1")); - checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - gmsh.setPView(myID, -1, (isChecked)? 1 : 0, -1); - spinner.setEnabled(isChecked); - glView.requestRender(); - } - }); - spinner.setEnabled(infos[2].equals("1")); - ArrayList<String> choices; - ArrayAdapter<String> adapter; - choices = new ArrayList<String>(); - choices.add("Iso-values"); - choices.add("Continous map"); - choices.add("Filled iso-values"); - adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, choices); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - spinner.setAdapter(adapter); - spinner.setSelection(Integer.parseInt(infos[1])-1); - spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - - public void onItemSelected(AdapterView<?> parent, View view, - int pos, long id) { - gmsh.setPView(myID, pos+1, -1, -1); - glView.requestRender(); - nbIso.setEnabled(pos == 0 || pos == 2); - } - public void onNothingSelected(AdapterView<?> arg0) {} // Unused Auto-generated method stub - }); - nbIso.setText(infos[3]); - nbIso.setRawInputType(Configuration.KEYBOARD_12KEY); - nbIso.setOnKeyListener(new View.OnKeyListener() { - - public boolean onKey(View v, int keyCode, KeyEvent event) { - if(keyCode == KeyEvent.KEYCODE_ENTER){ // hide the keyboard - InputMethodManager imm = (InputMethodManager)getSystemService( - Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(nbIso.getWindowToken(), 0); - return true; - } - return false; - } - }); - nbIso.addTextChangedListener(new TextWatcher() { - - public void onTextChanged(CharSequence s, int start, int before, int count) { - - int nIso = 1; - try { - if(s.length() < 1) nIso = 1; - else nIso = Integer.parseInt(s.toString()); - } - catch(NumberFormatException e) - { - nIso = 1; - nbIso.setText(""); - } - if(nIso > 1000) {gmsh.setPView(myID, -1, -1, 1000); nbIso.setText("1000");} - else if(nIso > 0) gmsh.setPView(myID, -1, -1, nIso); - else gmsh.setPView(myID, -1, -1, 1); - glView.requestRender(); - } - - public void beforeTextChanged(CharSequence s, int start, int count, int after) {} // UNUSED Auto-generated method stub - public void afterTextChanged(Editable s) {} // UNUSED Auto-generated method stub - - }); - row.addView(checkbox); - row.addView(spinner); - row.addView(nbIso); - table.addView(row); - } - scroll.addView(table); - return scroll; - }*/ - - private View paramView(Context ctx) { - LinearLayout layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); - reset = new Button(ctx); - reset.setText("Reset"); - reset.setOnClickListener(new OnClickListener() {public void onClick(View v) { - if(gmsh.onelabCB("reset") == 1){ - getAvailableParam(); - glView.requestRender(); - } - pager.setCurrentItem(0, true); - }}); - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); - lp.weight = 1; - CheckBox showMesh = new CheckBox(ctx); - showMesh.setText("Show the mesh"); - showMesh.setChecked((gmsh.getDoubleOption("Mesh", "SurfaceEdges") > 0.)); - showMesh.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - gmsh.setDoubleOption("Mesh", "SurfaceEdges", (isChecked)?1. : 0.); - glView.requestRender(); - } - }); - CheckBox showGeom = new CheckBox(ctx); - showGeom.setText("Show the geometry"); - showGeom.setChecked((gmsh.getDoubleOption("Geometry", "Lines") > 0.)); - showGeom.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - gmsh.setDoubleOption("Geometry", "Lines", (isChecked)?1. : 0.); - glView.requestRender(); - } - }); - paramListView = new SeparatedListView(ctx, new View[] {showMesh, showGeom, reset}); - paramListView.setDividerHeight(0); - for(Parameter p : params) - { - paramListView.addItem(p.getName().split("/")[0].equals("Parameters")? p.getName().split("/")[0] + " > " + p.getName().split("/")[1]: p.getName().split("/")[0], p.getView()); - p.setList(paramListView); - } - return paramListView; - } - - private class Run extends AsyncTask<Void, Void, Integer[]> { + + private class Run extends AsyncTask<Void, Void, Integer[]> { @Override protected void onPreExecute() { - compute = true; - loading.setTitle("Please wait"); + _compute = true; + _runStopMenuItem.setTitle(R.string.menu_stop); + /*loading.setTitle("Please wait"); loading.setButton(DialogInterface.BUTTON_NEUTRAL, "Hide", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { @@ -473,70 +134,48 @@ public class MainActivity extends Activity { }); loading.setMessage("..."); loading.show(); - reset.setEnabled(false); + reset.setEnabled(false);*/ super.onPreExecute(); } @Override protected Integer[] doInBackground(Void... params) { - gmsh.onelabCB("compute"); + _gmsh.onelabCB("compute"); return new Integer[] {1}; } @Override protected void onPostExecute(Integer[] result) { //(Vibrator) getSystemService(Context.VIBRATOR_SERVICE).vibrate(350); - runStopItem.setTitle(R.string.menu_run); - reset.setEnabled(true); - loading.dismiss(); - compute = false; + _runStopMenuItem.setTitle(R.string.menu_run); + //reset.setEnabled(true); + //loading.dismiss(); + _compute = false; super.onPostExecute(result); } } - - private void showError(){ - if(errors.size()>0){ - if(errorDialog != null && errorDialog.isShowing()) errorDialog.dismiss(); - errorDialog = dialogBuilder - .setTitle("Gmsh/GetDP Error(s)") - .setMessage(errors.get(errors.size()-1)) - .setNegativeButton("Stop", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - errors.clear(); - errorDialog.dismiss(); - } - }) - .setPositiveButton("Continue", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - errors.remove(errors.size()-1); - errorDialog.dismiss(); - showError(); - } - }) - .show(); - } - } - private final Handler mainHandler = new Handler(){ + public void onRequestRender() { + _modelFragment.requestRender(); + } + private final Handler mainHandler = new Handler(){ public void handleMessage(android.os.Message msg) { switch (msg.what) { case 0: // we get a message from gmsh library - //get(size() - 1) - String message =(String) msg.obj; - errors.add(message); - showError(); + //String message =(String) msg.obj; + //errors.add(message); + //showError(); break; case 1: // request render from gmsh library - if(glView != null) - glView.requestRender(); - getAvailableParam(); + if(_modelFragment != null) _modelFragment.requestRender(); + if(_optionsFragment != null) _optionsFragment.refresh(); break; case 2: // we get a message for loading - String loadingMessage =(String) msg.obj; - loading.setMessage(loadingMessage); + /*String loadingMessage =(String) msg.obj; + loading.setMessage(loadingMessage);*/ break; case 3: // we get a progress for loading - loading.setProgress(msg.arg1); + //loading.setProgress(msg.arg1); break; default: break; diff --git a/contrib/mobile/Android/src/org/geuz/onelab/ModelFragment.java b/contrib/mobile/Android/src/org/geuz/onelab/ModelFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..6a67d9615ad8ca9e0c9370bffda13bb869786bb5 --- /dev/null +++ b/contrib/mobile/Android/src/org/geuz/onelab/ModelFragment.java @@ -0,0 +1,51 @@ +package org.geuz.onelab; + +import android.app.Fragment; +import android.opengl.GLSurfaceView; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +public class ModelFragment extends Fragment{ + + Gmsh _gmsh; + mGLSurfaceView _glView; + + public static ModelFragment newInstance(Gmsh g) { + ModelFragment fragment = new ModelFragment(); + Bundle bundle = new Bundle(); + bundle.putParcelable("Gmsh", g); + fragment.setArguments(bundle); + return fragment; + } + + public ModelFragment() { + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + _gmsh = getArguments().getParcelable("Gmsh"); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_model, container, false); + LinearLayout glViewLayout = (LinearLayout)rootView.findViewById(R.id.glViewLayout); + GLESRender renderer = new GLESRender(_gmsh); + _glView = new mGLSurfaceView(glViewLayout.getContext(), renderer); + _glView.setEGLContextClientVersion(1); + _glView.setRenderer(renderer); + _glView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + _glView.requestRender(); + glViewLayout.addView(_glView); + return rootView; + } + + public void requestRender() { + _glView.requestRender(); + } +} diff --git a/contrib/mobile/Android/src/org/geuz/onelab/OptionsDisplayFragment.java b/contrib/mobile/Android/src/org/geuz/onelab/OptionsDisplayFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..81a248a386c4145d752db4b7ad3d089e8b089190 --- /dev/null +++ b/contrib/mobile/Android/src/org/geuz/onelab/OptionsDisplayFragment.java @@ -0,0 +1,123 @@ +package org.geuz.onelab; + +import android.os.Bundle; +import android.app.Activity; +import android.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.CompoundButton; + +public class OptionsDisplayFragment extends Fragment{ + + private Gmsh _gmsh; + private SeparatedListView _listView; + + public static OptionsDisplayFragment newInstance(Gmsh g) { + OptionsDisplayFragment fragment = new OptionsDisplayFragment(); + Bundle bundle = new Bundle(); + bundle.putParcelable("Gmsh", g); + fragment.setArguments(bundle); + return fragment; + } + + public OptionsDisplayFragment() { + super(); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + _gmsh = getArguments().getParcelable("Gmsh"); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + _listView = (SeparatedListView)inflater.inflate(R.layout.fragment_options_display, container, false); + CheckBox showGeomPoints = new CheckBox(_listView.getContext()); + showGeomPoints.setText("Show geometry points"); + showGeomPoints.setChecked((_gmsh.getDoubleOption("Geometry", "Points") > 0.)); + showGeomPoints.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + _gmsh.setDoubleOption("Geometry", "Points", (isChecked)?1. : 0.); + mCallback.onRequestRender(); + } + }); + _listView.addItem("Display", showGeomPoints); + CheckBox showGeomLines = new CheckBox(_listView.getContext()); + showGeomLines.setText("Show geometry lines"); + showGeomLines.setChecked((_gmsh.getDoubleOption("Geometry", "Lines") > 0.)); + showGeomLines.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + _gmsh.setDoubleOption("Geometry", "Lines", (isChecked)?1. : 0.); + mCallback.onRequestRender(); + } + }); + _listView.addItem("Display", showGeomLines); + CheckBox showMeshSurfaceEdges = new CheckBox(_listView.getContext()); + showMeshSurfaceEdges.setText("Show mesh surface edges"); + showMeshSurfaceEdges.setChecked((_gmsh.getDoubleOption("Mesh", "SurfaceEdges") > 0.)); + showMeshSurfaceEdges.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + _gmsh.setDoubleOption("Mesh", "SurfaceEdges", (isChecked)?1. : 0.); + mCallback.onRequestRender(); + } + }); + _listView.addItem("Display", showMeshSurfaceEdges); + CheckBox showMeshVolumesEdges = new CheckBox(_listView.getContext()); + showMeshVolumesEdges.setText("Show mesh volume edges"); + showMeshVolumesEdges.setChecked((_gmsh.getDoubleOption("Mesh", "VolumeEdges") > 0.)); + showMeshVolumesEdges.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + _gmsh.setDoubleOption("Mesh", "VolumeEdges", (isChecked)?1. : 0.); + mCallback.onRequestRender(); + } + }); + _listView.addItem("Display", showMeshVolumesEdges); + this.refresh(); + return _listView; + } + + public void refresh() { + if(_gmsh == null) return; + String[] PViews = _gmsh.getPView(); + for(int i=_listView.itemsCountInSection("Result"); i < PViews.length;i++){ + String[] infos = PViews[i].split("\n"); // name / IntervalsType (1=Iso 2=Continous 3=Discrete 4=Numeric) + final int myID = i; + CheckBox checkbox = new CheckBox(_listView.getContext()); + checkbox.setText(infos[0]); + checkbox.setChecked(infos[2].equals("1")); + checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + _gmsh.setPView(myID, -1, (isChecked)? 1 : 0, -1); + mCallback.onRequestRender(); + } + }); + _listView.addItem("Result", checkbox); + } + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + mCallback = (OnOptionRequestRender) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement OnHeadlineSelectedListener"); + } + + } + + private OnOptionRequestRender mCallback; + public interface OnOptionRequestRender { + public void onRequestRender(); + } +} diff --git a/contrib/mobile/Android/src/org/geuz/onelab/OptionsFragment.java b/contrib/mobile/Android/src/org/geuz/onelab/OptionsFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..96438ef88436cc0fd3c2cc7c357fdc0347ff65c1 --- /dev/null +++ b/contrib/mobile/Android/src/org/geuz/onelab/OptionsFragment.java @@ -0,0 +1,69 @@ +package org.geuz.onelab; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.LinearLayout; +import android.app.Fragment; +import android.app.FragmentTransaction; + +public class OptionsFragment extends Fragment{ + + private Gmsh _gmsh; + private OptionsDisplayFragment _optionDisplayFragment; + private OptionsModelFragment _optionModelFragment; + + public static OptionsFragment newInstance(Gmsh g) { + OptionsFragment fragment = new OptionsFragment(); + Bundle bundle = new Bundle(); + bundle.putParcelable("Gmsh", g); + fragment.setArguments(bundle); + return fragment; + } + + public OptionsFragment() { + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + _gmsh = getArguments().getParcelable("Gmsh"); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + LinearLayout rootView = (LinearLayout)inflater.inflate(R.layout.fragment_options, container, false); + _optionModelFragment = OptionsModelFragment.newInstance(_gmsh); + getFragmentManager().beginTransaction().add(R.id.options_fragment, _optionModelFragment).commit(); + _optionDisplayFragment = OptionsDisplayFragment.newInstance(_gmsh); + Button optionModel = (Button) rootView.findViewById(R.id.goto_options_model); + Button optionDisplay = (Button) rootView.findViewById(R.id.goto_options_display); + optionModel.setOnClickListener(new View.OnClickListener() { + + public void onClick(View v) { + FragmentTransaction ft = getFragmentManager().beginTransaction(); + ft.replace(R.id.options_fragment, _optionModelFragment); + ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + ft.commit(); + } + }); + optionDisplay.setOnClickListener(new View.OnClickListener() { + + public void onClick(View v) { + FragmentTransaction ft = getFragmentManager().beginTransaction(); + ft.replace(R.id.options_fragment, _optionDisplayFragment); + ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + ft.commit(); + } + }); + return rootView; + } + + public void refresh() { + _optionDisplayFragment.refresh(); + _optionModelFragment.refresh(); + } +} diff --git a/contrib/mobile/Android/src/org/geuz/onelab/OptionsModelFragment.java b/contrib/mobile/Android/src/org/geuz/onelab/OptionsModelFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..9834b441f159fca39fc7bb03a501c2eecdcdedbf --- /dev/null +++ b/contrib/mobile/Android/src/org/geuz/onelab/OptionsModelFragment.java @@ -0,0 +1,87 @@ +package org.geuz.onelab; + +import java.util.ArrayList; +import java.util.List; + +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +public class OptionsModelFragment extends Fragment{ + private Gmsh _gmsh; + private SeparatedListView _listView; + private List<Parameter> params = new ArrayList<Parameter>(); + + public static OptionsModelFragment newInstance(Gmsh g) { + OptionsModelFragment fragment = new OptionsModelFragment(); + Bundle bundle = new Bundle(); + bundle.putParcelable("Gmsh", g); + fragment.setArguments(bundle); + return fragment; + } + + public OptionsModelFragment() { + super(); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + _gmsh = getArguments().getParcelable("Gmsh"); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + params.clear(); + _listView = (SeparatedListView)inflater.inflate(R.layout.fragment_options_display, container, false); + _listView.setDividerHeight(0); + this.refresh(); + return _listView; + } + + public void refresh() { + if(_gmsh == null) return; + this.getAvailableParam(); + } + + private void getAvailableParam(){ + String[] tmp = _gmsh.getParams(); + for(String s : tmp){ // for each parameters in ONEALB + boolean found = false; + for(Parameter p : params){ // for each parameters + if(s.split("\n")[2].equals(p.getName())){ // the parameter already exist, just refresh it + if(p.getType().equals("ParameterNumber")) + ((ParameterNumber)p).fromString(s); + else if(p.getType().equals("ParameterString")) + ((ParameterString)p).fromString(s); + found = true; + break; + } + } + if(found) continue; + // add new parameter + if(s.split("\n")[1].equals("number")){ + final ParameterNumber mParam = new ParameterNumber(_listView.getContext(), _gmsh, mCallback, ""); + if(mParam.fromString(s) == -1) continue; + params.add(mParam); + if(_listView != null) + _listView.addItem(mParam.getName().split("/")[0].equals("Parameters")? mParam.getName().split("/")[0] + " > " + mParam.getName().split("/")[1]: mParam.getName().split("/")[0], mParam.getView()); + } + else if(s.split("\n")[1].equals("string")){ + ParameterString mParam = new ParameterString(_listView.getContext(), _gmsh, mCallback, ""); + if(mParam.fromString(s) != -1){ + params.add(mParam); + if(_listView != null) + _listView.addItem(mParam.getName().split("/")[0], mParam.getView()); + } + } + } + } + private OnOptionRequestRender mCallback; + public interface OnOptionRequestRender { + public void onRequestRender(); + } +} diff --git a/contrib/mobile/Android/src/org/geuz/onelab/Parameter.java b/contrib/mobile/Android/src/org/geuz/onelab/Parameter.java index 4e0040f566a13fdde0b2df72907c57f033c5e33d..328eb662b9572dacce097285a43feba4d0c028f9 100644 --- a/contrib/mobile/Android/src/org/geuz/onelab/Parameter.java +++ b/contrib/mobile/Android/src/org/geuz/onelab/Parameter.java @@ -1,6 +1,8 @@ package org.geuz.onelab; +import org.geuz.onelab.OptionsModelFragment.OnOptionRequestRender; + import android.content.Context; import android.graphics.Color; import android.widget.LinearLayout; @@ -11,17 +13,17 @@ public class Parameter { protected Context _context; protected Gmsh _gmsh; protected SeparatedListView _listView; - protected mGLSurfaceView _glView; + protected OnOptionRequestRender _callback; protected String _name; protected String _label; protected boolean _readOnly; protected boolean _changed; protected TextView _title; - public Parameter(Context context, Gmsh gmsh, mGLSurfaceView glView, String name){ + public Parameter(Context context, Gmsh gmsh, OnOptionRequestRender callback, String name){ _context = context; _gmsh = gmsh; - _glView = glView; + _callback = callback; _readOnly = false; _name = name; _title = new TextView(context); @@ -29,8 +31,8 @@ public class Parameter { _title.setTextAppearance(context, android.R.style.TextAppearance_DeviceDefault_Medium); _title.setTextColor(Color.DKGRAY); } - public Parameter(Context context, Gmsh gmsh, mGLSurfaceView glView, String name, boolean readOnly){ - this(context, gmsh, glView, name); + public Parameter(Context context, Gmsh gmsh, OnOptionRequestRender callback, String name, boolean readOnly){ + this(context, gmsh, callback, name); _readOnly = readOnly; _changed = false; } diff --git a/contrib/mobile/Android/src/org/geuz/onelab/ParameterNumber.java b/contrib/mobile/Android/src/org/geuz/onelab/ParameterNumber.java index 73f0ce165940502b134093c81427183d9beab619..49e47f7e29c022e02da7708cb0e917fabdb13fff 100644 --- a/contrib/mobile/Android/src/org/geuz/onelab/ParameterNumber.java +++ b/contrib/mobile/Android/src/org/geuz/onelab/ParameterNumber.java @@ -2,6 +2,8 @@ package org.geuz.onelab; import java.util.ArrayList; +import org.geuz.onelab.OptionsModelFragment.OnOptionRequestRender; + import android.content.Context; import android.text.Editable; import android.text.TextWatcher; @@ -28,20 +30,20 @@ public class ParameterNumber extends Parameter{ private CheckBox _checkbox; private EditText _edittext; - public ParameterNumber(Context context, Gmsh gmsh, mGLSurfaceView glView, String name){ - super(context, gmsh, glView, name); + public ParameterNumber(Context context, Gmsh gmsh, OnOptionRequestRender callback, String name){ + super(context, gmsh, callback, name); } - public ParameterNumber(Context context, Gmsh gmsh, mGLSurfaceView glView, String name, double value, double min, double max, double step) + public ParameterNumber(Context context, Gmsh gmsh, OnOptionRequestRender callback, String name, double value, double min, double max, double step) { - this(context, gmsh, glView, name); + this(context, gmsh, callback, name); _value = value; _min = min; _max = max; _step = step; } - public ParameterNumber(Context context, Gmsh gmsh, mGLSurfaceView glView, String name, boolean readOnly, double value, double min, double max, double step) + public ParameterNumber(Context context, Gmsh gmsh, OnOptionRequestRender callback, String name, boolean readOnly, double value, double min, double max, double step) { - this(context, gmsh, glView, name, value, min, max, step); + this(context, gmsh, callback, name, value, min, max, step); _readOnly = readOnly; } @@ -185,8 +187,8 @@ public class ParameterNumber extends Parameter{ if(_listView != null) _listView.refresh(); setValue(_values.get(pos)); _gmsh.setParam(getType(), getName(), String.valueOf(_values.get(pos))); - if(_gmsh.onelabCB("check") == 1 && _glView != null) - _glView.requestRender(); + if(_gmsh.onelabCB("check") == 1 && _callback != null) + _callback.onRequestRender(); } }); @@ -198,8 +200,8 @@ public class ParameterNumber extends Parameter{ public void onStopTrackingTouch(SeekBar seekBar) { _gmsh.setParam(getType(), getName(), String.valueOf(getValue())); // update parameter and the perform a check - if(_gmsh.onelabCB("check") == 1 && _glView != null) - _glView.requestRender(); + if(_gmsh.onelabCB("check") == 1 && _callback != null) + _callback.onRequestRender(); } public void onStartTrackingTouch(SeekBar seekBar) {} diff --git a/contrib/mobile/Android/src/org/geuz/onelab/ParameterString.java b/contrib/mobile/Android/src/org/geuz/onelab/ParameterString.java index 9b8e937fad6808a9db9f1b5602637962cf0e687b..81be749cb58ea6b6c34ecd5970c8eeecf5618c32 100644 --- a/contrib/mobile/Android/src/org/geuz/onelab/ParameterString.java +++ b/contrib/mobile/Android/src/org/geuz/onelab/ParameterString.java @@ -2,6 +2,8 @@ package org.geuz.onelab; import java.util.ArrayList; +import org.geuz.onelab.OptionsModelFragment.OnOptionRequestRender; + import android.content.Context; import android.text.Editable; import android.text.TextWatcher; @@ -23,8 +25,8 @@ public class ParameterString extends Parameter{ private Spinner _spinner; private EditText _edittext; - public ParameterString(Context context, Gmsh gmsh, mGLSurfaceView glView, String name) { - super(context, gmsh, glView, name); + public ParameterString(Context context, Gmsh gmsh, OnOptionRequestRender callback, String name) { + super(context, gmsh, callback, name); _choices = new ArrayList<String>(); _choices.add("-"); // Default choice } @@ -109,8 +111,8 @@ public class ParameterString extends Parameter{ int pos, long id) { setValue(pos); _gmsh.setParam(getType(), getName(), String.valueOf(getValue())); - if(_gmsh.onelabCB("check") == 1 && _glView != null) - _glView.requestRender(); + if(_gmsh.onelabCB("check") == 1 && _callback != null) + _callback.onRequestRender(); if(_listView != null) _listView.refresh(); } diff --git a/contrib/mobile/Android/src/org/geuz/onelab/SeparatedListView.java b/contrib/mobile/Android/src/org/geuz/onelab/SeparatedListView.java index a88285ef465ea752709c7ec5aa1ca0b6e3daee2d..3d699033920eba2c54c6326a46b8803904dd7e64 100644 --- a/contrib/mobile/Android/src/org/geuz/onelab/SeparatedListView.java +++ b/contrib/mobile/Android/src/org/geuz/onelab/SeparatedListView.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import android.content.Context; +import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -23,6 +24,9 @@ public class SeparatedListView extends ListView{ adapter = new SeparatedListAdaptater(); this.setAdapter(adapter); } + public SeparatedListView(Context context, AttributeSet attrs) { + this(context); + } public SeparatedListView(Context context, View[] footer) { super(context); _context = context; @@ -39,7 +43,9 @@ public class SeparatedListView extends ListView{ adapter.notifyDataSetChanged(); this.invalidateViews(); } - + public int itemsCountInSection(String header) { + return adapter.getCountForSection(header); + } public void refresh() { adapter.notifyDataSetChanged(); @@ -102,7 +108,12 @@ public class SeparatedListView extends ListView{ for(Section s : sections) count += s.getItemsCount() + 1; return count; } - + public int getCountForSection(String header) { + for(Section s : sections) + if(s.getName().equals(header)) + return s.getItemsCount(); + return 0; + } //@Override public Object getItem(int position) { // UNUSED Auto-generated method stub