Skip to content

Commit

Permalink
File picker (#172)
Browse files Browse the repository at this point in the history
* model picker from storage implementation
* autopilot check
* dialog to check whether model already exists
  • Loading branch information
dhruv2295 committed Mar 31, 2021
1 parent 5afbc34 commit a578459
Show file tree
Hide file tree
Showing 11 changed files with 340 additions and 7 deletions.
1 change: 1 addition & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,5 @@ dependencies {
// When using the BoM, you don't specify versions in Firebase library dependencies
implementation 'com.google.firebase:firebase-crashlytics'
implementation 'com.google.firebase:firebase-analytics'
implementation 'com.nononsenseapps:filepicker:4.1.0'
}
21 changes: 21 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,33 @@

</activity>

<activity
android:name=".ai.BackHandlingFilePickerActivity"
android:label="@string/app_name"
android:theme="@style/FilePickerTheme">
<intent-filter>
<action android:name="android.intent.action.GET_CONTENT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

<service
android:name=".logging.SensorService"
android:enabled="true"
android:launchMode="singleTask" />

<service android:name="net.majorkernelpanic.streaming.rtsp.RtspServer"/>

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>

</application>

</manifest>
117 changes: 114 additions & 3 deletions android/app/src/main/java/org/openbot/ai/AIFragment.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package org.openbot.ai;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
Expand All @@ -18,12 +22,18 @@
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.camera.core.ImageProxy;
import androidx.navigation.Navigation;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.nononsenseapps.filepicker.Utils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
Expand Down Expand Up @@ -78,6 +88,68 @@ public class AIFragment extends CameraFragment implements ServerListener {
private int numThreads = -1;

private ArrayAdapter<CharSequence> modelAdapter;
private ActivityResultLauncher<Intent> mStartForResult;
private int selectedModelIndex = 0;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mStartForResult =
registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {

Intent intent = result.getData();
// Handle the Intent
List<Uri> files = Utils.getSelectedFilesFromResult(intent);

String fileName = new File(files.get(0).getPath()).getName();
if (org.openbot.utils.Utils.checkFileExistence(requireActivity(), fileName)) {
AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
builder.setTitle(R.string.file_available_title);
builder.setMessage(R.string.file_available_body);
builder.setPositiveButton(
"Yes",
(dialog, id) -> {
processModelFromStorage(files, fileName);
});
builder.setNegativeButton(
"Cancel",
(dialog, id) -> {
// User cancelled the dialog
});
AlertDialog dialog = builder.create();
dialog.show();
} else {
processModelFromStorage(files, fileName);
}
}
});
}

private void processModelFromStorage(List<Uri> files, String fileName) {
try {
InputStream inputStream =
requireActivity().getContentResolver().openInputStream(files.get(0));
org.openbot.utils.Utils.copyFile(
inputStream, fileName, requireActivity().getFilesDir().getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}

modelAdapter.clear();
modelAdapter.addAll(Arrays.asList(getResources().getTextArray(R.array.models)));
modelAdapter.addAll(getModelFiles());
modelAdapter.add("Choose From Device");
modelAdapter.notifyDataSetChanged();
binding.modelSpinner.setSelection(modelAdapter.getPosition(fileName));
setModel(new Model(fileName));

Toast.makeText(
requireContext().getApplicationContext(), "Model added: " + model, Toast.LENGTH_SHORT)
.show();
}

@Override
public View onCreateView(
Expand All @@ -102,7 +174,8 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
modelAdapter =
new ArrayAdapter<>(requireContext(), R.layout.spinner_item, new ArrayList<>(models));
modelAdapter.addAll(getModelFiles());
modelAdapter.setDropDownViewResource(android.R.layout.simple_list_item_checked);
modelAdapter.add("Choose From Device");
modelAdapter.setDropDownViewResource(android.R.layout.simple_dropdown_item_1line);
binding.modelSpinner.setAdapter(modelAdapter);

setAnalyserResolution(Enums.Preview.HD.getValue());
Expand All @@ -111,7 +184,16 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String selected = parent.getItemAtPosition(position).toString();
setModel(Model.fromId(selected.toUpperCase()));
if (selected.equals("Choose From Device")) {
binding.modelSpinner.setSelection(selectedModelIndex);
openPicker();
} else
try {
setModel(Model.fromId(selected.toUpperCase()));
} catch (IllegalArgumentException e) {
setModel(new Model(selected));
}
selectedModelIndex = position;
}

@Override
Expand Down Expand Up @@ -182,6 +264,28 @@ public void onNothingSelected(AdapterView<?> parent) {}
binding.autoSwitch.setOnClickListener(v -> setNetworkEnabled(binding.autoSwitch.isChecked()));
}

private void openPicker() {

Intent i = new Intent(requireActivity(), BackHandlingFilePickerActivity.class);
// This works if you defined the intent filter
// Intent i = new Intent(Intent.ACTION_GET_CONTENT);

// Set these depending on your use case. These are the defaults.
i.putExtra(BackHandlingFilePickerActivity.EXTRA_ALLOW_MULTIPLE, false);
i.putExtra(BackHandlingFilePickerActivity.EXTRA_ALLOW_CREATE_DIR, false);
i.putExtra(BackHandlingFilePickerActivity.EXTRA_MODE, BackHandlingFilePickerActivity.MODE_FILE);

// Configure initial directory by specifying a String.
// You could specify a String like "/storage/emulated/0/", but that can
// dangerous. Always use Android's API calls to get paths to the SD-card or
// internal memory.
i.putExtra(
BackHandlingFilePickerActivity.EXTRA_START_PATH,
Environment.getExternalStorageDirectory().getPath());

mStartForResult.launch(i);
}

private void updateCropImageInfo() {
// Timber.i("%s x %s",getPreviewSize().getWidth(), getPreviewSize().getHeight());
// Timber.i("%s x %s",getMaxAnalyseImageSize().getWidth(),
Expand Down Expand Up @@ -283,7 +387,14 @@ private void recreateNetwork(Model model, Network.Device device, int numThreads)
} catch (IllegalArgumentException | IOException e) {
String msg = "Failed to create network.";
Timber.e(e, msg);
Toast.makeText(requireContext().getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
requireActivity()
.runOnUiThread(
() ->
Toast.makeText(
requireContext().getApplicationContext(),
e.getMessage(),
Toast.LENGTH_LONG)
.show());
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.openbot.ai;

import android.os.Environment;
import com.nononsenseapps.filepicker.AbstractFilePickerFragment;
import com.nononsenseapps.filepicker.FilePickerActivity;
import java.io.File;

public class BackHandlingFilePickerActivity extends FilePickerActivity {

/** Need access to the fragment */
BackHandlingFilePickerFragment currentFragment;

/** Return a copy of the new fragment and set the variable above. */
@Override
protected AbstractFilePickerFragment<File> getFragment(
final String startPath,
final int mode,
final boolean allowMultiple,
final boolean allowDirCreate,
final boolean allowExistingFile,
final boolean singleClick) {

// startPath is allowed to be null.
// In that case, default folder should be SD-card and not "/"
String path =
(startPath != null ? startPath : Environment.getExternalStorageDirectory().getPath());

currentFragment = new BackHandlingFilePickerFragment();
currentFragment.setArgs(
path, mode, allowMultiple, allowDirCreate, allowExistingFile, singleClick);
return currentFragment;
}

/** Override the back-button. */
@Override
public void onBackPressed() {
// If at top most level, normal behaviour
if (currentFragment.isBackTop()) {
super.onBackPressed();
} else {
// Else go up
currentFragment.goUp();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.openbot.ai;

import androidx.annotation.NonNull;
import com.nononsenseapps.filepicker.FilePickerFragment;
import java.io.File;

public class BackHandlingFilePickerFragment extends FilePickerFragment {

/**
* For consistency, the top level the back button checks against should be the start path. But it
* will fall back on /.
*/
public File getBackTop() {
return getPath(getArguments().getString(KEY_START_PATH, "/"));
}

/** @return true if the current path is the startpath or / */
public boolean isBackTop() {
return 0 == compareFiles(mCurrentPath, getBackTop())
|| 0 == compareFiles(mCurrentPath, new File("/"));
}

/** Go up on level, same as pressing on "..". */
public void goUp() {
mCurrentPath = getParent(mCurrentPath);
mCheckedItems.clear();
mCheckedVisibleViewHolders.clear();
refresh(mCurrentPath);
}

// File extension to filter on
private static final String EXTENSION = ".tflite";

/**
* @param file
* @return The file extension. If file has no extension, it returns null.
*/
private String getExtension(@NonNull File file) {
String path = file.getPath();
int i = path.lastIndexOf(".");
if (i < 0) {
return null;
} else {
return path.substring(i);
}
}

@Override
protected boolean isItemVisible(final File file) {
boolean ret = super.isItemVisible(file);
if (ret && !isDir(file) && (mode == MODE_FILE || mode == MODE_FILE_AND_DIR)) {
String ext = getExtension(file);
return EXTENSION.equalsIgnoreCase(ext);
}
return ret;
}
}
11 changes: 9 additions & 2 deletions android/app/src/main/java/org/openbot/tflite/Autopilot.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import org.openbot.env.Control;

public abstract class Autopilot extends Network {
Expand All @@ -27,14 +28,20 @@ public abstract class Autopilot extends Network {
protected ByteBuffer indicatorBuffer = null;

public static Autopilot create(Activity activity, Model model, Device device, int numThreads)
throws IOException {
throws IOException, IllegalArgumentException {
return new AutopilotFloat(activity, model, device, numThreads);
}

/** Initializes a {@code Autopilot}. */
protected Autopilot(Activity activity, Model model, Device device, int numThreads)
throws IOException {
throws IOException, IllegalArgumentException {
super(activity, model, device, numThreads);
tflite.getInputIndex("cmd_input");
if (!Arrays.equals(
tflite.getInputTensor(tflite.getInputIndex("img_input")).shape(),
new int[] {1, getImageSizeY(), getImageSizeX(), 3}))
throw new IllegalArgumentException("Invalid tensor dimensions");

indicatorBuffer = ByteBuffer.allocateDirect(4);
indicatorBuffer.order(ByteOrder.nativeOrder());
LOGGER.d("Created a Tensorflow Lite Autopilot.");
Expand Down
Loading

0 comments on commit a578459

Please sign in to comment.