Initializing Cyborg
Application Object:
We'll start by declaring your custom Application object, and initializing Cyborg in the Application object's onCreate.
Declare your application in your manifest.xml:
<application
android:allowBackup="true"
android:name=".YourApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher">
...
</application>
In YourApplication initialize Cyborg using the CyborgConfiguration object:
public class YourApplication
extends Application {
@Override
@SuppressWarnings("unchecked")
public void onCreate() {
super.onCreate();
// If you plan on using your custom Launching Activity.
CyborgBuilder.startCyborg(new CyborgConfiguration(this));
// Providing the first layout to preset once the application launches.
CyborgBuilder.startCyborg(new CyborgConfiguration(this, R.first_layout_to_display));
// Cyborg allows you to define a name for each of your screens, it can be useful with analytics.
CyborgBuilder.startCyborg(new CyborgConfiguration(this, R.first_layout_to_display,"Screen Name"));
}
}
Application Launcher Activity:
Cyborg has many advantages, the most significant one been that Cyborg practically eliminates the need to declare new Activity or Fragment types, so also launching your application can done via Cyborg's default launching activity.
The ApplicationLauncher Activity is already declared in Cyborg's manifest.xml:
<application>
...
<activity
android:name="com.nu.art.software.cyborg.ui.ApplicationLauncher">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
...
</application>
To change some properties of the ApplicationLauncher, you can declare it as well in your project manifest.xml, and edit the properties, for example:
<application
android:name=".YourApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher">
...
<activity
android:name="com.nu.art.software.cyborg.ui.ApplicationLauncher"
# Disable Cyborg's default launcher and use your own launching activity
android:enabled="false"
# Change the label
android:label="@string/another_name"
# Change the icon
android:icon="@mipmap/another_icon">
</activity>
...
</application>
The View
Creating Screen Layouts (Activity layout):
As mentioned Earlier, there is no need to define Fragments, because Cyborg does not use them instead your controller layout will look somewhat like this (You could always wrap two in a regular layout... see the demo project):
<?xml version="1.0" encoding="utf-8"?>
<com.nu.art.software.cyborg.core.CyborgView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:cyborg="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
cyborg:controller=".MyController"
cyborg:tag="mytag"/>
Note the bold texts and keep in mind:
- In order to fully access the advantages of Cyborg you will have to use CyborgViews instead of what you used Fragments.
- Define the Cyborg controller class type FQN (com.app.package.MyController) or RQN (.MyController) associated with the CyborgView.
- Make sure you define a unique ids to your views.
- Keep in mind that CyborgControllers with a little help will save their own states when onSaveState is called - so give your CyborgViews a unique tag.
The Controller
The Power of Controllers:
The result of the redundency of Activities and Fragments is the CyborgController, in a framework of bizzare MVC this controller provides some structure. The lifecycle of the controller matches to the lifecycle of an Activity, and its function is to control a group of views and their events and update the Model, for example:
public class InjectionExampleController
extends CyborgController {
// Inject multiple views and set an OnClickListener.
@ViewIdentifier(viewIds = {R.id.View1, R.id.View2, R.id.View3}, listeners = ViewListener.OnClick)
View[] views;
// Inject view and set an OnTextChangedListener.
@ViewIdentifier(viewId = R.id.InputText, listeners = ViewListener.OnTextChangedListener)
EditText inputText;
// Inject view and set an OnLongClickListener and an OnClickListener.
@ViewIdentifier(viewId = R.id.ResultTextView, listeners = {ViewListener.OnLongClick, ViewListener.OnClick})
TextView resultTextView;
// Inject view and set an OnClickListener.
@ViewIdentifier(viewId = R.id.UpdateTextButton, listeners = {ViewListener.OnClick})
Button updateTextButton;
// Will inject the value back after restore state.
@Restorable
String toSave;
public InjectionExampleController() {
super(R.layout.v1_controller__injection_example);
}
@Override
public void afterTextChanged(TextView view, Editable editableValue) {
if (view == inputText) {
logInfo("Text Changed: " + inputText.getText().toString());
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.ResultTextView:
resultTextView.setText("onClick");
break;
case R.id.UpdateTextButton:
resultTextView.setText(toSave = inputText.getText().toString());
}
}
@Override
public boolean onLongClick(View v) {
switch (v.getId()) {
case R.id.ResultTextView:
resultTextView.setText("onLongClick");
return true;
}
return false;
}
@Override
protected void prepareToSaveState() {
toSave = resultTextView.getText().toString();
}
@Override
protected void onPostRestoredState() {
inputText.setText(toSave);
}
}
The Model
Modules:
Most problematic issue in Android is the Model management, the lack in the ability to pass instances between Activities is a real hole in the design of Android, and this is where Cyborg takes every corner of Android to the next level.
If you have ever tried to use some feature of Android like: track the location of the device, catch networks drop events, develop a Bluetooth or NFC based application, integrate ads, use JNI libs, use Google analytics and anything else you might consider, together with the vast versions of Android, you'll find out that this requires a lot of configuration and is not trivial also for experienced developers but especially for beginners, and this is where the Modules come in...
Modules encapsulate the problematic logic and provide a consistant API for you to use hiding all the possible overhead from you and can be accessed from ANYWHERE in the code.
So if you have an application and you need to wrap some logic, you can create your own Module and access it from any part of the code, for example:
@ModuleDescriptor
public class MyModule
extends CyborgModule {
private ArrayList<String> listOfStrings = new ArrayList<String>();
@Override
protected void init() {
// if your module require initialization it can be performed here
listOfStrings.add("1");
listOfStrings.add("2");
listOfStrings.add("3");
}
public String getString(int index) {
return listOfStrings.get(index);
}
public void addString(String string) {
listOfStrings.add(string);
}
public int getCount() {
return listOfStrings.size();
}
}
Module Packs:
In most cases you would need to configure the each Module, and in some cases you would have a few Modules packed together conversing about the state of your application each manage its own logic, so for a single or multiple Modules you'll need to declare a ModulePack for example:
@SuppressWarnings("unchecked")
public class MyModulePack
extends ModulesPack {
private MyModulePack() {
super(MyModule.class);
}
@Override
protected void preBuildModules() {
// You can get any module declared in the constructor and PRE-CONFIGURE it before it is initialized.
getModule(MyModule.class).addString("0");
}
@Override
protected void postBuildModules() {
// Technically you can ask for any module and POST-CONFIGURE after it has initialized.
getModule(MyModule.class).addString("4");
}
}