# Android Project

### Add totoframework library to project

1. First, create the same project as the one we created in the [Android Native](/mobile-developer/add-oz-viewer-to-project.md).
2. Download **oztoto80\_android\_version.number.tar.gz** from 👉 [Product Downloads](/product-downloads.md)
3. Get the [**oztotoframework\_android.jar**](https://drive.google.com/file/d/1D_LdjF_llRLPWwWTxB4vOMPPDaP3RBdI/view?usp=sharing) and add it to the **libs** folder. Of clause, you have to set library dependencies too.

### Configurations

{% tabs %}
{% tab title="AndroidManifest.xml" %}

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.forcs.mobile">

    <application
        android:allowBackup="true"
        android:hardwareAccelerated="true"
        android:largeHeap="true"
        android:supportsRtl="true"
        android:usesCleartextTraffic="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:theme="@style/Theme.AppCompat.Light.DarkActionBar">
        <activity
            android:name="com.forcs.mobile.MainActivity"
            android:configChanges="orientation|screenSize"
            android:exported="true"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name="com.forcs.mobile.OnlineActivity" />
        <activity android:name="com.forcs.mobile.OfflineActivity" />
        <activity android:name="com.forcs.mobile.OZViewerActivity" android:windowSoftInputMode="adjustResize"  android:configChanges = "orientation|screenSize" />
    </application>

    <uses-feature    android:name="android.hardware.camera" />
    <uses-feature    android:name="android.hardware.camera.autofocus" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.NFC" />
</manifest>
```

{% endtab %}

{% tab title="build.gradle (:app)" %}

```
apply plugin: 'com.android.application'

android {
    compileSdk 31
    buildToolsVersion "31.0.0"

    defaultConfig {
        applicationId "com.forcs.toto"
        minSdk 21
        targetSdk 31
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            ndk {
                abiFilters 'arm64-v8a'
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
    implementation 'androidx.annotation:annotation:1.3.0'
    implementation 'com.google.android.material:material:1.4.0'
    implementation files('libs/ozrv_android.jar')
    implementation files('libs/oztotoframework_android.jar')
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
```

{% endtab %}
{% endtabs %}

### res/layout

{% tabs %}
{% tab title="activity\_main.xml" %}

```xml
<?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">

    <Button
        android:layout_marginTop="200dp"
        android:id="@+id/button1"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:backgroundTint="#29A329"
        android:onClick="online"
        android:text="Server Login"
        android:textAllCaps="false"
        android:textStyle="bold"
        android:textColor="@android:color/white" />

    <Button
        android:id="@+id/button2"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="20dp"
        android:backgroundTint="#0174df"
        android:onClick="offline"
        android:text="Local Forms"
        android:textAllCaps="false"
        android:textStyle="bold"
        android:textColor="@android:color/white" />

</LinearLayout>
```

{% endtab %}

{% tab title="activity\_online.xml" %}

```xml
<?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">
</LinearLayout>
```

{% endtab %}

{% tab title="activity\_ozviewer.xml" %}

```xml
<?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">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize">
        <Button
            android:id="@+id/toolbarbtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button"
            android:layout_gravity="right"/>
    </androidx.appcompat.widget.Toolbar>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </FrameLayout>

</LinearLayout>
```

{% endtab %}

{% tab title="activity\_offline.xml" %}

```xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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"
    android:orientation="vertical">

    <TextView
        android:layout_marginTop="10dp"
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:text="Local Forms"
        android:textStyle="bold"
        android:textSize="16sp" />

    <ListView
        android:padding="5dp"
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:background="#EAECEE" />
        />

</LinearLayout>
```

{% endtab %}

{% tab title="activity\_listview\.xml" %}

```xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/label"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="10dip"
    android:textSize="16dip"
    android:textStyle="bold" >
</TextView>
```

{% endtab %}
{% endtabs %}

### res/menu

{% tabs %}
{% tab title="menu\_save.xml" %}

```xml
<?xml version="1.0" encoding="utf-8"?>
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:id="@+id/save"
        android:title="Save"
        app:showAsAction="always" />
</menu>
```

{% endtab %}
{% endtabs %}

### res/values

{% tabs %}
{% tab title="colors.xml" %}

```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <color name="colorAccent">#D81B60</color>
    <color name="background_dark">#3498DB</color>
</resources>

```

{% endtab %}

{% tab title="styles.xml" %}

```xml
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>
```

{% endtab %}
{% endtabs %}

### Activitis

{% tabs %}
{% tab title="MainActivity" %}

```java
package com.forcs.mobile;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.view.View;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestAppPermission(this);
        setContentView(R.layout.activity_main);
    }

    public void online(View view) {
        Intent intent = new Intent(this, OnlineActivity.class);
        startActivity(intent);
    }

    public void offline(View view) {
        Intent intent = new Intent(this, OfflineActivity.class);
        startActivity(intent);
    }

    static boolean requestAppPermission(Context context) {
        if (Build.VERSION.SDK_INT >= 23) {
            String[] need = {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.READ_PHONE_STATE};
            for (int i = 0; i < need.length; i++) {
                if (context.checkSelfPermission(need[i]) != PackageManager.PERMISSION_GRANTED) {
                    if (context instanceof Activity) {
                        ((Activity) context).requestPermissions(need, 1);
                    }
                    return false;
                }
            }
        }
        return true;
    }

    static void onRequestPermissionsResultCustom(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == 1 && Build.VERSION.SDK_INT >= 23) {
            boolean isallgranted = true;
            for (int i = 0; i < grantResults.length; i++) {
                if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                    isallgranted = false;
                }
            }
            if (isallgranted) {
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        onRequestPermissionsResultCustom(requestCode, permissions, grantResults);
    }
}
```

{% endtab %}

{% tab title="OnlineActivity" %}

```java
package com.forcs.mobile;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.Window;
import android.widget.FrameLayout;
import android.widget.Toast;

import androidx.annotation.RequiresApi;

import oz.toto.framework.OZTotoError;
import oz.toto.framework.OZTotoEvent;
import oz.toto.framework.OZTotoEventHandler;
import oz.toto.framework.OZTotoRuntime;
import oz.toto.framework.OZTotoWebView;
import oz.toto.framework.OZTotoWebViewListener;

//import static oz.toto.framework.OZTotoWebView.NAVIGATOR_VISIBLE;

public class OnlineActivity extends Activity {

    FrameLayout parentView = null;
    OZTotoWebView toto = null;
    OZTotoRuntime toto_runtime = null;
    private long backKeyPressedTime = 0;
    private Toast toast;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        parentView = new FrameLayout(this);
        toto = new OZTotoWebView(this);
        OZTotoWebView.setDebugMode(true);
        parentView.addView(toto, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
        setContentView(parentView);
        OZTotoWebView.setDebugMode(true);
        String url = "http://oz.ozeform.io/oz/edu/toto";
        String pageName = "";
        String param = "";

        toto.setTotoWebViewListener(new OZTotoWebViewListener() {

            @Override
            public void onPageLoad(final OZTotoRuntime runtime) {
                runtime.getFramework().addEventListener("_exitApp_", OZTotoFrameworkEvent);
                toto_runtime = runtime;
            }

            @Override
            public void onPageUnload(OZTotoRuntime runtime) {
            }

            @Override
            public void onSelectedMenuButton() {
            }

            @Override
            public void onPageLoadFailure(OZTotoError ozTotoError) {
                home();
            }

            OZTotoEventHandler OZTotoFrameworkEvent = new OZTotoEventHandler() {
                @RequiresApi(api = Build.VERSION_CODES.M)
                @Override
                public void onEvent(OZTotoEvent e) {
                    if (e.eventName.equals("_exitApp_")) {
                        exit();
                    }
                }
            };
        });
        toto.run(url, pageName, param);
    }

    private void exit() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                new AlertDialog.Builder(OnlineActivity.this)
                        .setIcon(android.R.drawable.ic_dialog_alert)
                        .setTitle("Online Mode")
                        .setMessage("Do you want to log out?")
                        .setPositiveButton("YES", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                finish();
                            }
                        })
                        .setNegativeButton("NO", null)
                        .show();
            }
        });
    }

    private void home() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                new AlertDialog.Builder(OnlineActivity.this)
                        .setIcon(android.R.drawable.ic_dialog_alert)
                        .setTitle("Online Mode")
                        .setMessage("Cannot connect to the server.")
                        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                finish();
                            }
                        })
                        .show();
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (toto != null) {
            toto.onActivityResult( this, requestCode, resultCode, data);
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    public void onBackPressed() {
        if(toto.canGoBack()) {
            toto.goBack();
        } else{
            if (System.currentTimeMillis() > backKeyPressedTime + 2000) {
                backKeyPressedTime = System.currentTimeMillis();
                toast = Toast.makeText(this, "Press the Back button again to exit.", Toast.LENGTH_SHORT);
                toast.show();
                return;
            }
            if (System.currentTimeMillis() <= backKeyPressedTime + 2000) {
                finish();
                toast.cancel();
            }
        }
    }

}
```

{% endtab %}

{% tab title="OfflineActivity" %}

```java
package com.forcs.mobile;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;

// import OZ Viewer API
import java.io.File;

import static android.graphics.Color.WHITE;

public class OfflineActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_offline);

        String tag = "Local storage: ";
        //String path = Environment.getExternalStorageDirectory().toString()+"/toto";
        String path = "data/user/0/com.forcs.toto/toto/";
        Log.d(tag, "Path: " + path);
        File directory = new File(path);
        File[] files = directory.listFiles();

        if (files != null) {
            String[] names = new String[files.length];
            Log.d(tag, "Count: " + files.length);

            for (int i = 0; i < files.length; i++) {
                names[i] = files[i].getName();
                Log.d(tag, "FileName: " + names[i]);
            }
            ArrayAdapter adapter = new ArrayAdapter<String>(this,
                    R.layout.activity_listview, names);
            ListView listView = (ListView) findViewById(R.id.listView);
            listView.setAdapter(adapter);
            listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int i, long l) {
                    String selectedItem = (String) parent.getItemAtPosition(i);
                    Log.d(tag, "Selected: " + selectedItem);
                    Intent intent = new Intent(view.getContext(), OZViewerActivity.class);
                    intent.putExtra("fileName", selectedItem);
                    startActivity(intent);
                }
            });
        } else {
            exit();
        }
    }

    private void exit() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                new AlertDialog.Builder(OfflineActivity.this)
                        .setIcon(android.R.drawable.ic_dialog_alert)
                        .setTitle("Offline Mode")
                        .setMessage("No files found.")
                        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                finish();
                            }
                        })
                        .show();
            }
        });
    }
}
```

{% endtab %}

{% tab title="OZViewerActivity" %}

```java
package com.forcs.mobile;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.content.Intent;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.FrameLayout;
import android.app.AlertDialog;
import android.content.DialogInterface;

// import OZ Viewer API
import oz.api.OZReportAPI;
import oz.api.OZReportCommandListener;
import oz.api.OZReportViewer;

public class OZViewerActivity extends AppCompatActivity {

    // Load OZ Viewer library
    static {
        try {
            System.loadLibrary("skia_android");
            System.loadLibrary("ozrv");
        } catch (Exception e) {
            Log.e("OZRV", "Cannot load OZ Viewer library");
            e.printStackTrace();
        }
    }

    private FrameLayout mLayout; // declare content view for OZ Viewer
    private OZReportViewer mViewer; // declare OZ Viewer
    String ozFile = null;
    String savePath = "/data/user/0/com.forcs.toto/toto";
    String param = "export.applyformat=ozd; ozd.saveall=true; export.mode=silent; export.confirmsave=false; viewer.exportcommand=true; export.path=" + savePath ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mLayout = new FrameLayout(this);
        setContentView(mLayout);
        Intent intent = getIntent();
        ozFile = intent.getExtras().getString("fileName");
        openViewer(ozFile);
    }

    private void openViewer(String ozFile) {
        closeViewer();
        mViewer = OZReportAPI.createViewer(mLayout, ozListener, "connection.openfile=file:///data/user/0/com.forcs.toto/toto/" + ozFile);
    }

    private void closeViewer() {
        if(mViewer != null) {
            mViewer.dispose();
            mViewer = null;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        closeViewer();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_save, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.save:
                param += ";  export.filename=" + ozFile + ";";
                mViewer.ScriptEx("save", param, ";");
                return true;
            default: return super.onOptionsItemSelected(item);
        }
    }

    private void dialogBox(String data) {
        AlertDialog.Builder dialog=new AlertDialog.Builder(this);
        dialog.setMessage(data);
        //dialog.setTitle("OZ e-Form App");
        dialog.setPositiveButton("OK",
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        return;
                    }
                });
        AlertDialog alertDialog=dialog.create();
        alertDialog.show();
    }

    OZReportCommandListener ozListener = new OZReportCommandListener() {
        @Override
        public void OZExportCommand(String code, String path, String filename, String pagecount, String filepaths)
        {
            String msg;
            msg = code.equals("1") ? (filename + ":\nSaved successfully.") : ("Error Code: \n" + code);
            dialogBox(msg);
        }
    };

}

```

{% endtab %}
{% endtabs %}

### Build and Run the App

1. Connect an Android device to your Windows or Mac and allow USB debugging.
2. Build and run the App.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://edu.ozeform.io/mobile-developer/toto-framework/sample-app.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
