
WandaVision

Implemented the Command line interface menu (CLIM) for .NET core also. Available on Nuget and Github. Check it out here.
CLIM is an open source project.
This post shows a way to use the CLIM library inside an Android app.
We’d like to use a simple activity with an EditText (maybe multiline) as output and another EditText as input. Since the CLIM’s engine use Standard input/output streams as default, we need to implement our custom ones.
The layout of our activity will consists on:
The activity_main.xml :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <EditText android:id="@+id/etout" android:layout_width="match_parent" android:layout_height="0dp" android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:gravity="top|left" android:inputType="textMultiLine" android:lines="200" android:minLines="10" android:scrollbars="vertical" app:layout_constraintBottom_toTopOf="@+id/etin" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" android:clickable="false" android:cursorVisible="false" android:focusable="false" android:focusableInTouchMode="false" /> <EditText android:id="@+id/etin" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginBottom="8dp" android:ems="10" android:text="" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/btnok" app:layout_constraintStart_toStartOf="parent" /> <Button android:id="@+id/btnok" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:text="ok" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> |
First things first: let’s add dependancy to CLIM in the gradle project-level build.gradle :
1 2 3 4 5 6 7 8 9 |
allprojects { repositories { google() jcenter() maven { url "https://dl.bintray.com/ianovir/CLIM" } } } |
then the app-level build.gradle:
1 2 3 4 5 |
dependencies { ... implementation 'com.ianovir.clim:CLIM:1.1' } |
Inside our activity we will prepare our CLIM engine:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); launchClim(); } private void launchClim() { Engine engine = new Engine("CLIM DEMO"); Menu mainMenu = engine.buildMenu("Control Console"); mainMenu.addEntry(new Entry("access security") { @Override public void onAction() { } }); mainMenu.addEntry(new Entry("access security grid") { @Override public void onAction() { } }); mainMenu.addEntry(new Entry("access main security grid") { @Override public void onAction() { } }); //configure our custom streams: prepareStreams(engine); engine.addOnTop(mainMenu); engine.start(); } |
For an implementation with sense, please have a look at the Demo.java class.
We will see the body of prepareStreams(Engine engine) in the next section.
Now, we need to implement our custom input and output streams to be linked to our EditTexts etin and etout respectively.
Let’s see the content of prepareStreams():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
private void prepareStreams(Engine engine) { final EditText outputText = findViewById(R.id.etout); final OutputStream out = new OutputStream(){ @Override public void open() {} @Override public void close() {} @Override public void onOutput(final String s) { runOnUiThread(new Runnable() { @Override public void run() { outputText.append("\n"+ s); } }); } }; TextInputStream in = new TextInputStream(engine, (EditText) findViewById(R.id.etin), (Button) findViewById(R.id.btnok), out); engine.setInputStream(in); engine.setOutStream(out); } |
As you can see, the implementation of the OutputStream is quite simple, since we will redirect the input string to the output EditText (in the UI thread, of course).
The TextInputStream needs some extra considerations:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
public class TextInputStream extends InputStream { private final EditText et; private final Button btn; private final OutputStream outs; private String lastContent; public TextInputStream(Engine subscriber, EditText et, Button commit, OutputStream outs) { super(subscriber); this.et = et; this.btn = commit; this.outs = outs; btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setLastContent(TextInputStream.this.et.getText().toString()); TextInputStream.this.outs.onOutput(TextInputStream.this.et.getText().toString()); TextInputStream.this.et.setText(""); } }); } @Override public String forceRead() { String ret = null; //we can do this, since we're on another thread while(getLastContent()==null){ } ret = lastContent; setLastContent(null); return ret; } private synchronized String getLastContent() { return lastContent; } private synchronized void setLastContent(String lastContent) { this.lastContent = lastContent; } @Override public void close() { super.close(); } } |
You can notice that the TextInputStream take the engine as the main subscriber and saves to lastContent the inputted text. The access to lastContent is managed by two synchronized methods (getLastContent() and setLastContent(…)). Finally, notice that the forceRead() method is called inside a dedicated Thread from the parent class InputStream, so it is safe to use that while loop inside.
Let’s watch and enjoy our app (using the engine as in Demo.java) :
Command line interface menu (CLIM) engine for JVM offers a very simple system to manage UI menus via the cli. It may be useful when prototyping core libraries in their early stage, in absence of advanced GUI yet.
CLIM is an open source project.
Check it out on Github
I was “cleaning out the shelves” when I found some interesting stuffs I made in the past while learning/using ZBrush. Why not to share them?
Here they are, available on my Artstation and Gumroad pages!
Making some art for the site. Often I forget how comforting can be… just drawing!
The Probability Solver of Absorbing States in Markov Chain is my solution for an online programming game. The simple code in this repository provides a method to compute probabilities for the absorbing states in a given Markov chain. The code is available on GitHub.
Well, often it’s so difficult to read the instruction booklet of something before using it. This is the reason I made the new app Hyper Gyro!
Since some gamers can’t lose their precious time to understand the purpose of an app like HyperIMU nor the effort made by its developer, I made what they wished for: a simple rotating wheel spinning in the background.
No settings, no protocols, no problemos…
I guess this is an example of a manufacturer that meets the “needs” of the market…
I will ask for Hyper Gyro to be included in the next Android release as system app, together with their favourite multiplayer game maybe.