I'm having some troubles when I try to generate a library that contains another one in Android Studio. First of all I am using Android Studio 2.0 and Unity 5.3.4f1. What I want to do is integrate FFMpeg in Unity so I decided to create my own library that use FFMpeg library as an intermediary. The FFMpeg library is here.
To start I created a new empty project in Android Studio and inside of it, created a new module, which will be my library, using New->Module->Android Library and named it "ffmpegcodec". After that I opened build.gradle file inside the new module folder and paste:
compile 'com.writingminds:FFmpegAndroid:0.3.2'
inside dependencies brackets, and click Sync (It did not show any error).
After that create a new Java Class inside src/main/java/package-name/ and named it FFMpegCodec. Inside this paste this code:
public class FFMpegCodec {
private static FFmpeg ffmpeg;
private static Context context;
private static FFMpegCodec INSTANCE = null;
public FFMpegCodec(){
INSTANCE = this;
}
public static FFMpegCodec instance(){
if(INSTANCE == null){
INSTANCE = new FFMpegCodec();
}
return INSTANCE;
}
public void setContext(Context ctx){
this.context = ctx;
}
public void loadFFMpegBinary() {
try {
if (ffmpeg == null) {
ffmpeg = FFmpeg.getInstance(context);
}
ffmpeg.loadBinary(new LoadBinaryResponseHandler() {
#Override
public void onFailure() {
Log.e("FFMPEG", "ffmpeg : NOT correct Loaded");
}
#Override
public void onSuccess() {
Log.e("FFMPEG", "ffmpeg : correct Loaded");
}
});
} catch (FFmpegNotSupportedException e) {
} catch (Exception e) {
}
}
public void execFFmpegCommand(final String[] command) {
try {
ffmpeg.execute(command, new ExecuteBinaryResponseHandler() {
#Override
public void onFailure(String s) {
Log.e("FFMPEG", "FAILED with output : " + s);
}
#Override
public void onSuccess(String s) {
Log.e("FFMPEG", "SUCCESS with output : " + s);
}
#Override
public void onProgress(String s) {
Log.e("FFMPEG", "Started command : ffmpeg " + command);
Log.e("FFMPEG", "progress : " + s);
}
#Override
public void onStart() {
Log.e("FFMPEG", "Started command : ffmpeg " + command);
}
#Override
public void onFinish() {
Log.e("FFMPEG", "Finished command : ffmpeg " + command);
}
});
} catch (FFmpegCommandAlreadyRunningException e) {
// do nothing for now
}
}
It basically generates an instance of FFmpeg and makes calls of ffmpeg commands.
Once I have my lib done I decided to test it in my project so I include my lib in my app graddle using:
compile project(':ffmpegcodec')
After that I just paste this code to my MainActivity:
public class MainActivity extends AppCompatActivity {
FFMpegCodec ffmpeg;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button execCommandButton = (Button) findViewById(R.id.execCommandButton);
ffmpeg = FFMpegCodec.instance();
execCommandButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "This is a Toast!!", Toast.LENGTH_SHORT).show();
ffmpeg.setContext(MainActivity.this);
ffmpeg.loadFFMpegBinary();
String []cmd = new String[1];
cmd[0] = "-version";
ffmpeg.execFFmpegCommand(cmd);
}
});
}
After that, I run my project and when I press the button it returns that everythings its ok, showing ffmpeg version.
Once I have check that my lib works I decide to move it to Unity so copy the ffmpegcodec-realese.arr file inside ffmpegcodec/build/outputs/aar folder and paste it into my Unity project. Then I wrote an C# script to use my lib that contains this:
using System;
using System.Runtime.InteropServices;
using UnityEngine;
using System.Collections;
using System.Diagnostics;
public class ScreenRecorder {
private AndroidJavaObject unityActivity = null;
private AndroidJavaObject captureObject = null;
// Use this for initialization
public ScreenRecorder () {
try{
using (AndroidJavaClass unityPlayerActivityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
unityActivity = unityPlayerActivityClass.GetStatic<AndroidJavaObject>("currentActivity");
}
AndroidJavaClass captureClass = new AndroidJavaClass ("com.promineostudios.ffmpegcodec.FFMpegCodec");
if (captureClass != null) {
captureObject = captureClass.CallStatic<AndroidJavaObject>("instance");
captureObject.Call("setContext", unityActivity);
captureObject.Call("loadFFMpegBinary");
}
}catch(Exception ex){
UnityEngine.Debug.Log(ex);
}
}
}
The problems comes here. When I create a class instance using:
AndroidJavaClass captureClass = new AndroidJavaClass ("com.promineostudios.ffmpegcodec.FFMpegCodec");
It creates an instance correctly, and also when I create an object using:
captureObject = captureClass.CallStatic<AndroidJavaObject>("instance");
But when I try to get access to FFMpeg library methods like in "setContext" it fails and returns this:
UnityEngine.AndroidJavaException: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/github/hiteshsondhi88/libffmpeg/FFmpeg;
java.lang.NoClassDefFoundError: Failed resolution of: Lcom/github/hiteshsondhi88/libffmpeg/FFmpeg;
at com.promineostudios.ffmpegcodec.FFMpegCodec.loadFFMpegBinary(FFMpegAndroidCodec.java:39)
at com.unity3d.player.UnityPlayer.nativeRender(Native Method)
at com.unity3d.player.UnityPlayer.a(Unknown Source)
at com.unity3d.player.UnityPlayer$b.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.github.hiteshsondhi88.libffmpeg.FFmpeg" on path: DexPathList[[zip file "/data/app/com.promineostudios.ffmpegmodule-1/base.apk"],nativeLibraryDirectories=[/data/app/com.promineostudios.ffmpegmodule-1/lib/arm, /vendor/lib, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at com.promineostudios.ffmpegcodec.FFMpegCodec.loadFFMpegBinary(FFMpegAndroidCodec.java:39)
at com.unity3d.player.UnityPlayer.nativeRender(Native Method)
at com.unity3d.player.UnityPlayer.a(Unknown Source)
at com.unity3d.player.UnityPlayer$b.run(Unknown Source)
I think that the problem is in when I export my lib into Unity but I do not know what is going wrong. If anybody could help me I will be really appreciated.
Thanks and excuse me for my English.
I know you're no longer working on this project Nestoraj, however I have figured out a solution:
The error you're getting is caused by the UnityPlayerActivity not finding the source for com/github/hiteshsondhi88/libffmpeg/FFmpeg so in order to fix that you must put the 'com.writingminds:FFmpegAndroid:0.3.2' jar file inside your Unity project's Assets/Plugins/Android folder
After doing that and running the project, you're still going to get an error loading the ffmpeg binary file, this is because it's also not properly packaged in Unity, to fix this you need to export your project to Gradle/Android Studio, and then inside your src/main/jniLibs directory you have to place the ffmpeg .so binary files, according to each architecture so jniLibs/armeabi-v7a/libARM_ARCH.so jniLibs/armeabi/libARM_ARCH.so etc. Then you must also place the ffmpeg object files inside src/main/assets and again according to each architecture like so : src/main/assets/armeabi-v7a/ffmpeg etc.
Once those 2 steps are done you should be able to run your project in Android Studio and load the ffmpeg binary inside your Unity project
Related
I've finished an app and have been attempting to have that app log some data in a text file. I have a few questions about creating files and file paths with Android Studio. I'm not well versed in the editor so perhaps my questions are simple. I'm logging the data in an activity that extends MainActivity, declared as LoggerActivity.
public class LoggerActvitivty extends MainActivity{
public boolean bNewUser = true;
public String sFileName;
public void writeToFile(HashMap hmSaleToSave) {
if (bNewUser) {
System.out.println(hmSaleToSave);
sFileName = Environment.DIRECTORY_DOWNLOADS + "/Sales-" + LocalDateTime.now() + ".txt";
try {
if(!Files.exists(Paths.get(sFileName))) {
Files.createFile(Paths.get(sFileName));
Files.write(Paths.get(sFileName), sFileName.getBytes(StandardCharsets.UTF_8));
}
System.out.println("FILE WRITE EXECUTED");
System.out.println("Location" + sFileName);
} catch (IOException e) {
e.printStackTrace();
}
} else {
}
}
}
Nothing crash's, but I believe I'm providing a null directory resulting in this logcat error
W/System.err: java.nio.file.NoSuchFileException: Download/Sales-2021-08-16T07:44:36.321.txt
I would like the logs to go to my tablets download directory. My first question is should I be using a different method to get my directory rather than through Enviornment.DIRECTORY_DOWNLOADS? Additionally if the path doesn't exist Files.createFile(....) should create the path too? Or does that just create the file at the specified path (sFileName)?
I am quite new in cordova and android native code
I had created my own plugin by using plugman.
plugman create --name myCustomPlugin --plugin_id myCustomPlugin --plugin_version 1.0
plugman had generated all the needed files for me to get started.
js file www/myCustomPlugin.js
var exec = require('cordova/exec');
exports.coolMethod = function(arg0, success, error) {
exec(success, error, "myCustomPlugin", "coolMethod", [arg0]);
};
java file src/android/myCustomPlugin.java, method execute
#Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if (action.equals("coolMethod")) {
String message = args.getString(0);
this.coolMethod(message, callbackContext);
return true;
}
return false;
}
private void coolMethod(String message, CallbackContext callbackContext) {
if (message != null && message.length() > 0) {
callbackContext.success(message);
} else {
callbackContext.error("Expected one non-empty string argument.");
}
}
The question is:
On the cordova project js file. How do I call myCustomPlugin? and how do I know that it is working?
I am using angular in my cordova project, let say my controller is like this:
.controller('mainCtrl', function($scope){
// how to call myCustomPlugin here?
});
My motive is just to learn how to call and how it work only, maybe after click it will do a native alert or open native view or something.
Thanks in advance!
You can do:
www/myCustomPlugin.js
var MyCustomPlugin {
coolMethod : function(arg0, success, error) {
exec(success, error, "myCustomPlugin", "coolMethod", [arg0]);
}
}
module.exports = MyCustomPlugin
And in your js client just:
MyCustomPlugin.coolMethod("Arg1", function(){}, function(){});
Cordova will inject JavaScript file for you.
Hope it helps.
I am trying to implement Speech Recognition with the help of Pocket Sphinx Library.
What my approach was,
I just downloaded their demo project for Android. Imported it in Eclipse, did clean-build and run it on device. It ran successfully.
After this I copied the libs folder from demo project to my own project.
I copied the assets folder contents as it is in my own project.
Then I edited the digits.gram file contents as per this post.
Then implemented the Listener in my activity and added addKeywordSearch to it.
My Questions:
Is this approach correct to implement this library in our own projects? Can we just copy paste all the files, modify .gram files as per need and run it? Or we need to do some other steps to generate .gram files?
When I tried with the above mentioned approach, then project ran successfully on the device for a few times. But after that it started showing the following error.
E/cmusphinx(10470): ERROR: "kws_search.c", line 158: The word '/1e-20/' is missing in the dictionary
E/AndroidRuntime(10470): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.uwemo.myvdemo/com.uwemo.myvdemo.MainActivity}: java.lang.RuntimeException: Decoder_setSearch returned -1
However /1e-20/ exists in my digits.gram file.
digits.gram file contents:
up /1e-20/
down /1e-15/
left /1e-15/
right /1e-15/
here /1e-15/
I noticed one thing, when I do clean-build of demo project of Pocket Sphinx and run it, then it recreates digits.gram.md5 files. But when I copied pasted all in my own project and did its cleant-build then these files were not recreated. Is this normal?
Is it necessary to have all the files in our project as in the demo project? Or can I delete some unused files from my own project? Any list of necessary files (either in assets folder or libs or others) will be highly helpful.
Please let me know what approach should I have to successfully implement it in my own project.
My Activity Code
public class MainActivity extends Activity implements RecognitionListener{
private static final String DIGITS_SEARCH = "digits";
private SpeechRecognizer recognizer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try
{
Assets assets = new Assets(MainActivity.this);
File assetDir = assets.syncAssets();
setupRecognizer(assetDir);
Toast.makeText(getApplicationContext(), "Speech Recognizer Started", Toast.LENGTH_SHORT).show();
}
catch (IOException e){
e.printStackTrace();
}
}
#Override
public void onBeginningOfSpeech() {
// TODO Auto-generated method stub
}
#Override
public void onEndOfSpeech() {
// TODO Auto-generated method stub
reset();
}
#Override
public void onPartialResult(Hypothesis arg0) {
// TODO Auto-generated method stub
}
#Override
public void onResult(Hypothesis hypothesis) {
// TODO Auto-generated method stub
if (hypothesis != null){
String text = hypothesis.getHypstr();
Toast.makeText(getApplicationContext(), "you got ::"+text, Toast.LENGTH_SHORT).show();
}
}
private void setupRecognizer(File assetsDir){
File modelsDir = new File(assetsDir, "models");
recognizer = defaultSetup().setAcousticModel(new File(modelsDir, "hmm/en-us-semi"))
.setDictionary(new File(modelsDir, "dict/cmu07a.dic"))
.setRawLogDir(assetsDir).setKeywordThreshold(1e-20f)
.getRecognizer();
recognizer.addListener(this);
File digitsGrammar = new File(modelsDir, "grammar/digits.gram");
recognizer.addKeywordSearch(DIGITS_SEARCH, digitsGrammar);
reset();
}
private void reset(){
recognizer.stop();
recognizer.startListening(DIGITS_SEARCH);
}
}
My project structure is following
I'm trying to integrate InMobi into my unity game to show ads. I made a jar library and call needed method via JNI in unity but getting the following error:
Android Java Exception: java.lang.NoClassDefFoundError
My method is static because unity doesn't do Call() for me, so I can do only CallStatic().
public static void ShowInMobi(final Context context)
{
//com.google.ads.mediation.inmobi.InMobiAdapter.disableHardwareAcceleration();
//((Activity) context).runOnUiThread(new Runnable() {
// public void run() {
String CLASS_TO_LOAD ="com.inmobi.commons.InMobi";
try
{
Class<?> newClass = Class.forName(CLASS_TO_LOAD);
System.out.println("Class "+newClass+" found successfully!");
}
catch (ClassNotFoundException ex)
{
ex.printStackTrace();
System.out.println("Class "+CLASS_TO_LOAD+" not found!");
}
catch (Throwable any)
{
System.out.println("Unexpected error! "+any);
}
if(interstitialInMobi==null)
{
com.inmobi.commons.InMobi.initialize((Activity) context, "my_ad_unit_id");
interstitialInMobi = new IMInterstitial((Activity) context, "my_ad_unit_id");
interstitialInMobi.loadInterstitial();
}
else
{
if (interstitialInMobi.getState() == IMInterstitial.State.READY){
interstitialInMobi.show();
interstitialInMobi.loadInterstitial();
}
}
//}
//});
Sohan from InMobi here
Looks like you are trying to use the native SDK directly in Unity. InMobi has a Unity plugin that you can use instead. The plugin acts as the mediator between the native SDK and your app.
You can check this link for further integration details.
I did a mistake that apparently can be solved only by uninstalling and then installing my app again.
I delivered a message to the users, but no-one seems to uninstall it.
AFAIK, if I change the certificate file, the play store won't let me upload the application, and
obviously I don't want to upload a new app.
Is there a way to force uninstall in order to update?
Thanks!
There's no killswitch to remotely force uninstalls (that'd be a security nightmare). What you can do is publish a fixed version on Google Play, and wait for users to upgrade.
I don't know if this can help you but i had the same problem. The solution for me is that i check the app version every time the user opens it and compare it with a version code stored on apache server (in a checkversion.php file).
If versions doesn't match, i show a not cancelable dialog that ask the user to go to market and download the update.
Here is an example (keep in mind that i use Volley library to handle connections):
public class UpdateManager {
private Activity ac;
private HashMap<String,String> params;
public UpdateManager(Activity ac) {
this.ac = ac;
}
public void checkForUpdates() {
Log.d("UpdateManager","checkForUpdates() - Started...");
params = new HashMap<String,String>();
params.put("request","checkforupdates");
try {
params.put("versioncode", String.valueOf(ac.getPackageManager().getPackageInfo(ac.getPackageName(), 0).versionCode));
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (Helper.isInternetAvailable(ac)) { //this is a class i made to check internet connection availability
checkAppVersion();
} else { Log.d("UpdateManager","CheckForUpdates(): Impossible to update version due to lack of connection"); }
}
private void checkAppVersion() {
Log.d("UpdateManager","checkAppVersion() - Request started...");
JsonObjectRequest req = new JsonObjectRequest("http://yourserver/checkappversion.php", new JSONObject(params),
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
if (response != null && response.has("result")) {
try {
Log.d("UpdateManager","checkAppVersion() - Request finished - Response: "+response.getString("result"));
if (response.getString("result").matches("updaterequested")) { //Update requested. Show the relative dialog
Log.d("UpdateManager","Update requested");
askUserForUpdate();
}
else if (response.getString("result").matches("current")) { //Same version. Do nothing
Log.d("UpdateManager","Version is up to date");
}
else if (response.getString("result").matches("error")) { //You can return an error message if error occurred on server
Log.d("UpdateManager","checkappversion Error - "+response.getString("error"));
}
VolleyLog.v("Response:%n %s", response.toString(4));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("UpdateManager","Volley Error - "+error.getMessage());
}
});
req.setRetryPolicy(new DefaultRetryPolicy(60000,0,DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
ConnectionController.getInstance().addToRequestQueue(req);
}
public void askUserForUpdate() {
final Dialog diag = new Dialog(ac);
diag.requestWindowFeature(Window.FEATURE_NO_TITLE);
diag.setContentView(R.layout.updatemanager_requestupdate_dialog);
diag.setCancelable(false);
diag.getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
TextView t = (TextView)diag.findViewById(R.id.requestupdate_dialog_main_text);
ImageView im_ok = (ImageView)diag.findViewById(R.id.requestupdate_dialog_ok);
ImageView im_canc = (ImageView)diag.findViewById(R.id.requestupdate_dialog_canc);
t.setText(ac.getResources().getString(R.string.update_manager_askuserforupdate));
im_canc.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
diag.dismiss();
ac.finish();
}
});
im_ok.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://details?id="+ac.getPackageName()));
diag.dismiss();
ac.startActivity(intent);
ac.finish();
}
});
diag.show();
}
}
You can then use it when your main activity (or maybe login activity) starts like this:
UpdateManager updateManager = new UpdateManager(MainActivity.this); //i assume MainActicity as the calling activity
updateManager.checkForUpdates();
Obviously this has to be implemented into the application code so, the first time, you have to rely only on the user to manually upgrade it. But this can help if you have the same problem in the future.
This is an extract from my personal code so you have to rearrange it to your needings. Hope this helps someone.
Users should be able to go to Settings > Applications > Manage Applications and select the application to be removed. I've never seen a case where the application can't be removed this way, except in the case of built-in applications which require a rooted device to remove.