This post shows a way to use the CLIM library inside an Android app.
The problem
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
The layout of our activity will consists on:
- Multiline EditText etout as output
- EditText etin as input
- Button btnok to commit the input (equivalent to ENTER key)

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> |
CLIM dependency
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' } |
The Activity
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.
The Streams
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.
Result
Let’s watch and enjoy our app (using the engine as in Demo.java) :
