Android JUnit Tests Hang When Run Together - java

I'll apologize in advance for posting alot of code, this issue has really got me!
I have two Android JUnit tests that are causing me problems. Run each individually and they work fine, but when run together in one go (PasswordEntryActivityTests and then CryptoKeystoreTests) CryptoKeystoreTests hangs indefinitely.
I know it's not just the emulator being slow because each individually finishes in less than a second but it can hang for more than 20 minutes. I also tested it on a real device (Droid Razr) and it does the same thing.
The problematic code is the PasswordEntryActivity.launchNewPasswordActivity(). Removing that function makes everything work fine.
Pausing the function in the debugger while it's hanging says it's in:
MessageQueue.nativePollOnce(int, int) line: not available [native method]
What's going on?
I've copied below:
PasswordEntryActivity
PasswordEntryActivityTests
CryptoManagerKeystoreTests
Please let me know to post any other code you'd like to see.
Thanks!
public class PasswordEntryActivity extends Activity
{
...
private void launchNewPasswordActivity()
{
Intent launchNewPasswordIntent = new Intent(this, NewPasswordActivity.class);
startActivity(launchNewPasswordIntent);
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.password_entry_layout);
...
//this code should be LAST in onCreate because it exits the Activity
//CryptoManager.passwordIsRight returns 0 if no password has been set
passwordExists = CryptoManager.passwordIsRight("x", this) != 0;
if(!passwordExists)
launchNewPasswordActivity();
}
}
That Activity's test:
//supposed to make sure the application responds correctly when no password is set
public class PasswordEntryActivityTests extends android.test.ActivityInstrumentationTestCase2< crypnote.controller.main.PasswordEntryActivity>{
protected void setUp() throws Exception
{
passwordEntryActivity = getActivity();
//delete the database if it exists
File file = passwordEntryActivity.getFileStreamPath(DBInterface.Constants.DatabaseName);
if(file.exists())
assertTrue(file.delete());
file = passwordEntryActivity.getFileStreamPath(CryptoManager.Constants.KEYSTORE_PATH);
if(file.exists())
assertTrue(file.delete());
}
//allows us to access the interface
#UiThreadTest
public void testNoPassword() throws Exception
{
passwordEntryActivity = getActivity();
EditText passwordEntryEditText =
(EditText) passwordEntryActivity.findViewById(
crypnote.controller.main.R.id.passwordentrylayout_passwordedittext);
Button unlockButton = (Button) passwordEntryActivity.findViewById(
crypnote.controller.main.R.id.passwordentrylayout_unlockbutton);
int passwordResult = CryptoManager.passwordIsRight("x", getActivity());
assertTrue(passwordResult == 0);
//pass a wrong password to the edittext and click the unlock button
passwordEntryEditText.setText("x");
assertTrue(unlockButton.performClick());
//get the foreground activity class name
ActivityManager am = (ActivityManager) passwordEntryActivity.
getSystemService(Context.ACTIVITY_SERVICE);
// get the info from the currently running task
List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(1);
ComponentName componentInfo = taskInfo.get(0).topActivity;
String foregroundClassName = componentInfo.getShortClassName();
//don't forget the leading '.'
assertTrue(!foregroundClassName.equals(".PasswordEntryActivity"));
}
}
The CryptoKeystoreTests:
public class CryptoKeystoreTests extends android.test.ActivityInstrumentationTestCase2<
crypnote.controller.main.PasswordEntryActivity>
{
public void testKeystore() throws Exception
{
Context context = getActivity();
//delete the database if it exists
File file = context.getFileStreamPath(DBInterface.Constants.DatabaseName);
if(file.exists())
assertTrue(file.delete());
file = context.getFileStreamPath(CryptoManager.Constants.KEYSTORE_PATH);
if(file.exists())
assertTrue(file.delete());
CryptoManager cryptoManager=null;
String password = CryptoManager.Constants.DEBUG_PASSWORD;
FileInputStream fis=null;
//the cryptomanager will generate a new key and keystore
cryptoManager = new CryptoManager(password, context);
Key CRYPTOKEY = cryptoManager.getKey();
cryptoManager.close();
//initialize KeyStore
KeyStore keystore = KeyStore.getInstance(Constants.KEYSTORE_INSTANCE_TYPE);
fis = context.openFileInput(CryptoManager.Constants.KEYSTORE_PATH);
keystore.load(fis, password.toCharArray());
assertTrue(keystore.containsAlias(Constants.APP_ALIAS));
assertTrue(keystore.isKeyEntry(Constants.APP_ALIAS));
Key key = keystore.getKey(CryptoManager.Constants.APP_ALIAS,
password.toCharArray());
assertTrue(key.getAlgorithm().equals(CryptoManager.Constants.PROVIDER_NAME));
assertTrue(key.getAlgorithm().equals(CRYPTOKEY.getAlgorithm()));
assertTrue(key.getFormat().equals(CRYPTOKEY.getFormat()));
if(fis != null)
fis.close();
}
}
EDIT: NewPasswordActivity.onCreate:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.new_password_layout);
}

It hangs because PasswordEntryActivityTests does not release/finish the resources/UI events that has been addressed/created by itself during its own running lifecycle, more specifically, then newly opened NewPasswordActivity.
PasswordEntryActivityTests starts by testing a creation of PasswordEntryActivity, i.e. getActivity(), which in consequence, based on the condition, launch a second NewPasswordActivity, the newly opened NewPasswordActivity occupy the foreground window and stay forever, it is developer's responsibility to release it properly after you have done your testing.
In instrumentation test, the correct way of detecting/monitoring second activity startup from current activity is to use ActivityMonitor, see the pseudo code below:
// No password result starting a second activity.
public void testNoPassword() {
// register NewPasswordActivity that need to be monitored.
ActivityMonitor activityMonitor = getInstrumentation().addMonitor(NewPasswordActivity.class.getName(), null, false);
// Get current activity, it will start NewPasswordActivity in consequence.
PasswordEntryActivity currentActivity = getActivity();
NewPasswordActivity nextActivity = getInstrumentation().waitForMonitorWithTimeout(activityMonitor, 5);
// NewPasswordActivity is opened and captured.
assertNotNull(nextActivity);
// Don't forget to release/finish NewPasswordActivity after test finish.
nextActivity.finish();
}

Related

Workmanager launching each time at launch without doing anything

First time using Stackoverflow !
I have an issue with my Workmanager and I'm asking for help:
When I run my app, its executing without switching my switch to ON. It's happening every time when I install (run) my app, my notification appears without doing anything. (it still works when I'm using my switch after launching )
MyWorker.java
public class MyWorker extends Worker {
private Workmate workmate;
private String messageBody;
public MyWorker(#NonNull Context context, #NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
#NonNull
#Override
public Result doWork() {
retrievesWorkmateData();
return Result.success();
}
SettingsActivity.java
#Override
public int getLayout() {
return R.layout.activity_settings;
}
#Override
protected void onConfigureDesign() {
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
configureToolbar();
spinnerLanguage();
final SharedPreferences.Editor editor = mSharedPreferences.edit();
boolean notificationBoolean = mSharedPreferences.getBoolean(BOOLEAN, false);
final OneTimeWorkRequest simpleRequest = new OneTimeWorkRequest.Builder(MyWorker.class)
.build();
UUID workId = simpleRequest.getId();
if (notificationBoolean) {
mSwitch.setChecked(true);
}
mSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (mSwitch.isChecked()) {
editor.putBoolean(BOOLEAN, true);
WorkManager.getInstance().enqueue(simpleRequest);
Toast.makeText(this, getResources().getString(R.string.Alarm_manager_start), Toast.LENGTH_SHORT).show();
} else {
editor.putBoolean(BOOLEAN, false);
WorkManager.getInstance().cancelWorkById(workId);
Toast.makeText(this, getResources().getString(R.string.Alarm_manager_cancel), Toast.LENGTH_SHORT).show();
}
editor.apply();
});
}
Have a nice day.
inside onConfigureDesign you are creating OneTimeWorkRequest an obtain its UUID workId = simpleRequest.getId(). this id is used inside OnCheckedChangeListener for starting or canceling work for WorkManager. consider this scenario: app doesn't have any pending work to do and user navigates to this switch and schedule work with given UUID. then user quits app, even kill it, and then got back to app and navigates again to this switch. onConfigureDesign is called again, new OneTimeWorkRequest is created and it have new UUID. this id won't cancel your previously set request, because it had/have another id... still switch is checked, as its checked/unchecked state is basing just on some boolean in SharedPreferences
solution would be to store in SharedPreferences this UUID (String in fact, use toString() and fromString(str)) when work is scheduled and remove it from there when canceling or doWork() gets called. initial state of switch should be also set basing on presence of this (any) id in shared prefs
welcome on SO :)

Checking Azure connected Database onClick for login

So Azure spit the following code for me to insert into an activity (Android Studio is what I'm using)
Add the following line to the top of the .java file containing your launcher activity:
import com.microsoft.windowsazure.mobileservices.*;
Inside your activity, add a private variable
private MobileServiceClient mClient;
Add the following code the onCreate method of the activity:
mClient = new MobileServiceClient("https://pbbingo.azurewebsites.net", this);
Add a sample item class to your project::
public class ToDoItem{ public String id; public String Text;}
In the same activity where you defined mClient, add the following code:
ToDoItem item = new ToDoItem();
item.Text = "Don't text and drive";
mClient.getTable(ToDoItem.class).insert(item, new TableOperationCallback<item>(){
public void onCompleted(ToDoItem entity, Exception exception, ServiceFilter response)
{
if(exception == null){
//Insert Succeeded
} else {
//Insert Failed
}
}});
My goal is to create a login page. I understand that the above was probably offered up more with a ToList in mind. I just want to get the syntax correct today. The problem I think, is my basic class structure. I have created an OnClick Listener within my on create that gets the ID from a button in my layout. I don't need it checking for anything in the database until the button has been actually clicked to either login or register.
public class LoginClass extends AppCompatActivity{
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.MyLoginLayout);
MobileServiceClient mClient = null;
try {
mClient = new MobileServiceClient ("myAzureWebsite", "AzureKey", this);
} catch (MalformedURLException e) {
e.printStackTrace();
}
Button Attempt = (Button) findViewById (R.id.mySubmitButton);
final MobileServiceClient finalMClient = mClient; // finalized so I can use it later.
Attempt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick (View v) {
final View thisView = v;
final MyToDoItemClass item = new MyToDoItemClass();
In MyToDoItemClass I have two variables (Both String) Just left over from
the example of a ToDoList (they are String ID and String Text)
item.Text = "Filler";
item.ID = "Fill";
finalMClient.getTable(MyToDoItemClass.class).insert(new Table OperationCallback<item>() { //<--- I'm getting an error that the variable, item
is from an unknown class...
public void onCompleted (Item entity, Exception exception, ServiceFilterResponse response){
if(exception == null) {
Intent i = new Intent (LoginClass.this, MainActivity.class);
startActivity(i);
}else{
Toast.makeText(thisView.getContext(), "Failed", Toast.LENGTH_LONG).show();
}}
});
}
});
}}
The problem is with that the TableOperationCallback is saying that the item from MyToDoItemClass class is from an unknown class.
There are many issues in your code, as below.
According to the javadoc for class MobileServiceClient, there is not a method insert(TableOperationCallback<E> callback), so the code finalMClient.getTable(MyToDoItemClass.class).insert(new Table OperationCallback<item>() {...} is invalid.
The generics E in Table OperationCallback<E> means that you need to write a POJO class name instead of E, not an object variable name like item, so the correct code should be new Table OperationCallback<MyToDoItemClass>, please see the Oracle tutorial for Generics to know more details.
The figure below shows all methods insert of class MobileServiceClient. The bold word Deprecated under the method name means that you should not use it for developing on new project, it‘s only compatible for old project on the new version of Java SDK.
Please follow the offical tutorial to develop your app. Any concern, please feel free to let me know.

Executing thread only in one Android Activity

I have three java files in my Android project. Two are activities (MainActivity and GeoActivity) and one is a plain java file (PostHttp -> sends data to server via the HTTP POST)
I switch over to GeoActivity via a simple button on-click method. GeoActivity returns the co-ordinates of the current location in a TextView AND sends them to a remote server via the HTTP POST.
I have a Handler.class which executes sends the Post Message after a delay of 50s. Something like this below. The problem i have is that when i click the back button and switch over to MainActivity i can still see in LogCat the echoes receiving from the server that the data is still being sent. How can i stop that?
GeoActivity.class
public class GeoActivity extends Activity {
Location location;
private Handler mHandler = new Handler();
protected void onCreate(Bundle savedInstanceState) {
....
if(location != null){
mHandler.postDelayed(updateTask,0);
}
...
}
...
public Runnable updateTask = new Runnable(){
public void run(){
mlocListener.onLocationChanged(location);
//send coordinates with a delay of 50s
new PostHttp(getUDID(),latitude,longitude).execute();
mHandler.postDelayed(updateTask, 50000);
}
Try acting on the activity's life cycle.
For example:
#Override
protected void onStop() {
super.onStop(); // Always call the superclass method first
// Save the note's current draft, because the activity is stopping
// and we want to be sure the current note progress isn't lost.
ContentValues values = new ContentValues();
values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());
getContentResolver().update(
mUri, // The URI for the note to update.
values, // The map of column names and new values to apply to them.
null, // No SELECT criteria are used.
null // No WHERE columns are used.
);
}
This doesn't destroy the activity, it will reside in memory. However, you can always resume when needed.
Source:
Stopping and Restarting Android Activities

Maximum length of Intent putExtra method? (Force close)

I need some help with debugging my application. First of all: In emulator and on some other devices my app is running fine. On my device I got a force close (without a force close message).
The "crash" happens if the Activity of the app is changed.
Here is some code of the MainActivity class. It just reads html content from a web page over webview. And no, it is NOT possible to do this over HttpRequest because I was not able to simulate the post request.
public class MainActivity extends Activity {
public final static String EXTRA_HTML = "com.example.com.test.HTML";
private WebView mWebView;
private ProgressDialog mDialog;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.webView1);
CookieSyncManager.createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
mWebView.setBackgroundColor(0);
mWebView.setWebChromeClient(new WebChromeClient() {
public boolean onConsoleMessage(ConsoleMessage cmsg) {
if (cmsg.message().startsWith("MAGIC")) {
mDialog.cancel();
/*HashMap<String, String> message = new HashMap<String, String>();*/
String msg = cmsg.message().substring(5);
Intent intent = new Intent(MainActivity.this,
ReadDataActivity.class);
/*message.put("message", msg);*/
/*intent.putExtra(EXTRA_HTML, message);*/
intent.putExtra(EXTRA_HTML, msg);
startActivity(intent);
}
return false;
}
});
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setPluginState(PluginState.OFF);
mWebView.getSettings().setLoadsImagesAutomatically(false);
mWebView.getSettings().setBlockNetworkImage(true);
mWebView.getSettings().setAppCacheEnabled(true);
mWebView.getSettings().setSavePassword(true);
mWebView.getSettings()
.setCacheMode(WebSettings.LOAD_NORMAL);
mWebView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String address) {
if (address.indexOf("mySession") != -1) {
view.loadUrl("javascript:console.log('MAGIC'+document.getElementsByTagName('html')[0].innerHTML);");
}
});
mWebView.loadUrl("http://www.myurl.de");
}
So, in the onConsoleMessage() method I just pass the html code to another Activity class which read, parse and display the content.
The problem is now that at this point when the ReadDataActivity class should be loaded the application just close and go back to the home screen without any message or user dialog.
Is it possible that the html code which is passed as a string to the ReadDataActivity is to big? I also try to add the html code as a string in a HashMap but the problem is the same.
Some ideas what I can do to debug the problem? Maybe I should try to create a Parcelable object?
In the emulator everything is working fine.
As per my experience (sometime ago), you are able to parcel up to 1MB of data in a Bundle for IPC. This limit can be reduced if a lot of transactions are happening at a given time. Further information here.
In order to overcome this issue, I would suggest you to save your content on a temp file and pass the path/URI of your temp file to your second activity. Then in your second activity, read the contents out from file, perform your desired operations and finally delete that file.
If you want, you may also incorporate Shared_Preferences for this task - if you think handling files is cumbersome.
I did some research on the maximum amount of data you can transfer using an Intent. And it seems that the limit is nowhere near 1MB or 90KB, it's more like 500KB (tested on API 10, 16, 19 and 23).
I wrote a blog post about this topic, you can find it here: http://web.archive.org/web/20200217153215/http://neotechsoftware.com/blog/android-intent-size-limit
The size limit of Intent is still pretty low in Jelly Bean, which is somewhat lower than 1MB (around 90K), so you should always be cautious about your data length, even if your application targets only latest Android versions.
I have seen that by writing and reading from a file consists of less performance .
Then I have seen this solution : . So I am using this solution :
public class ExtendedDataHolder {
private static ExtendedDataHolder ourInstance = new ExtendedDataHolder();
private final Map<String, Object> extras = new HashMap<>();
private ExtendedDataHolder() {
}
public static ExtendedDataHolder getInstance() {
return ourInstance;
}
public void putExtra(String name, Object object) {
extras.put(name, object);
}
public Object getExtra(String name) {
return extras.get(name);
}
public boolean hasExtra(String name) {
return extras.containsKey(name);
}
public void clear() {
extras.clear();
}
}
Then in MainActivity I have called it like the following :
ExtendedDataHolder extras = ExtendedDataHolder.getInstance();
extras.putExtra("extra", new byte[1024 * 1024]);
extras.putExtra("other", "hello world");
startActivity(new Intent(MainActivity.this, DetailActivity.class));
and in DetailActivity
ExtendedDataHolder extras = ExtendedDataHolder.getInstance();
if (extras.hasExtra("other")) {
String other = (String) extras.getExtra("other");
}
The fixed size of 1MB is not only limited to intents. As Intents, Content Providers, Messenger, all system services like Telephone, Vibrator etc. utilize IPC infrastructure provider by Binder. Moreover the activity lifecycle callbacks also use this infrastructure.
1MB is the overall limit on all the binder transactions executed in the system at a particular moment.
In case there are lot of transactions happening when the intent is sent,it may fail even though extra data is not large.
http://codetheory.in/an-overview-of-android-binder-framework/
A little late to the game, but I just ran up against the same issue. Writing the data to file didn't really make sense performance-wise in my case, but I came across this in my search for answers:
http://developer.android.com/guide/faq/framework.html#3
Using a singleton is better for me as there's no need for disk IO. Better performance if the data doesn't need to be persisted.
Here's an example implementation:
public class DataResult {
private static DataResult instance;
private List<SomeObject> data = null;
protected DataResult() {
}
public static DataResult getInstance() {
if (instance == null) {
instance = new DataResult();
}
return instance;
}
public List<SomeObject> getData() { return data; }
public void setData(List<SomeObject> data) { this.data = data; }
}
Then you can set using this in one activity:
DataResult.getInstance().setData(data);
And get it in the other activity like this:
List<SomeObject> data = DataResult.getInstance().getData();
The Binder transaction buffer has a limited fixed size - 1Mb.
But the problem is that buffer shared by all transactions in progress for the process.
So try to keep your intent's data as small as possible every time.
The use of static String variable is good. If there is a need for the user to go back & forth between different pieces of HTML, you can also use LruCache like this:
static LruCache<String, String> mMemoryCache;
final int kiloByte = 1024;
.
.
final int maxMemoryKB = (int) (Runtime.getRuntime().maxMemory() / kiloByte);
// then choose how much you want to allocate for cache
final int cacheSizeKB = maxMemoryKB / 8;
.
.
mMemoryCache = new LruCache<String, String>(cacheSizeKB) {
//#Override
protected int sizeOf(String key, String value) {
try {
byte[] bytesUtf8 = value.getBytes("UTF-8");
return bytesUtf8.length / kiloByte;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return -1;
}
};
.
.
String cacheKey = generateUniqueString(key);
if (mMemoryCache.get(key) == null) {
mMemoryCache.put(cacheKey, yourContent);
}
Intent intent = new Intent(getApplicationContext(), ReadDataActivity.class);
intent.putExtra(EXTRA_HTML, cacheKey);
startActivity(intent);
Then on the ReadDataActivity side
Intent intent = getIntent();
String cacheKey = intent.getStringExtra(EXTRA_HTML);
String contentString = MainActivity.mMemoryCache.get(cacheKey);
doSomethingWith(contentString);
This idea came from here.
An alternative solution for passing large data between activities is to use a static field. In your case add this line to ReadDataActivity class
public static String msg;
Then you can use the static field msg within MainActivity class as follows
ReadDataActivity.msg = cmsg.message().substring(5);
And finally start your activity without extra put
Intent intent = new Intent(MainActivity.this, ReadDataActivity.class);
startActivity(intent);

android java context explanation

I have the following question:
I have to use the following function from BT printer SDK:
StarIOPort port = null;
byte[] texttoprint = new byte[]{0x1b, 0x40, 0x1b,0x74,0x0D,(byte) 0x91,(byte) 0x92,(byte) 0x93,(byte) 0x94,(byte) 0x95,(byte) 0x96,(byte) 0x97,(byte) 0x98,(byte) 0x99,0x0A,0x0A,0x0A,0x0A,0x0A};
try
{
port = StarIOPort.getPort(portName, portSettings, 10000, context);
port.writePort(textToPrint, 0, textToPrint.length);
port.writePort(new byte[] {0x0a}, 0, 1);
}
catch (StarIOPortException e)
{
Builder dialog = new AlertDialog.Builder(context);
dialog.setNegativeButton("Ok", null);
AlertDialog alert = dialog.create();
alert.setTitle("Failure");
alert.setMessage("Failed to connect to printer");
alert.show();
}
I have understand everything except of context.
The manufacturer mention that
* #param context - Activity for displaying messages to the user
How can I use the above function because in the way I use it I do not receive any error neither any alert message.
To display Alert (or any other UI component) you need Activity context, that's right. If you do not have any Activity running at the moment, you can't display Alert.
But you can display Toast, using static method of class Toast:
public static Toast makeText(Context context, CharSequence text, int duration);
passing to it Application Context as first parameter.
Application context is always available while your app is running, even if there are no UI running at the moment. You can get it by calling getApplicationContext() method from your context. If you don't have any context at all, you can always use YourAppClass (public class YourAppClass extends Application), defined in your manifest under xml tag.
Most common practice is to make YourAppClass a singleton, and it always be available at any point of code within your app.
The context is your Activity.
private Context context;
context = this;
Here is a complete sample activity.
package com.example.helloworld;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import <my_star_io_library>;
public class HelloWorld extends Activity
{
private Context context;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//Save context
context = this;
StarIOPort port = null;
byte[] texttoprint = new byte[]{0x1b, 0x40, 0x1b,0x74,0x0D,(byte) 0x91,(byte) 0x92,(byte) 0x93,(byte) 0x94,(byte) 0x95,(byte) 0x96,(byte) 0x97,(byte) 0x98,(byte) 0x99,0x0A,0x0A,0x0A,0x0A,0x0A};
try
{
port = StarIOPort.getPort(portName, portSettings, 10000, context);
port.writePort(textToPrint, 0, textToPrint.length);
port.writePort(new byte[] {0x0a}, 0, 1);
}
catch (StarIOPortException e)
{
Builder dialog = new AlertDialog.Builder(context);
dialog.setNegativeButton("Ok", null);
AlertDialog alert = dialog.create();
alert.setTitle("Failure");
alert.setMessage("Failed to connect to printer");
alert.show();
}
}
}
Context is a class related with an Activity,that you use when you have to show and AlertDialog, Toast, get system services... It is related with the arquitecture MVC,which is a bit longer to explain. About using it, there are two ways. One is explained by droidhot and another way is, for example, MainActivity.this, if you are using the AlertDialog in the Main Activity (MainActivity.java file), so the Main Activity will be the one that launches the Alert Dialog. If it is launched from another class which is not an Activity, you have to put the context as a parameter (for example, new Class(MainActivity.this)) and inside the class,it would be, for example, public class( Context context) ant the parameter context is the one you have to use.
Activity is subclass of Context so if you printing code is part of your Activity class, then simply provide this as required context to fulfill SDK requirements:
port = StarIOPort.getPort(portName, portSettings, 10000, this);
and later
Builder dialog = new AlertDialog.Builder(this);
I have posted answer here:
Android Phonegap Plugin different result on virtual and real device (Looper.prepare() ERROR)
I had the same problem which appeared on some devices. Successfully one smart boy, Toby, helped me. So, solution is the next:
- before you call any StarIOPort's methods you have to check if looper exist:
if (Looper.myLooper() == null) {
Looper.prepare();
}
in your case it's will looks like this:
try
{
if (Looper.myLooper() == null) {
Looper.prepare();
}
port = StarIOPort.getPort("BT:", "mini", 10000, null);
try
{
Thread.sleep(500);
}
catch(InterruptedException e) {}
port.writePort(texttoprint, 0, texttoprint.length);
try
{
Thread.sleep(3000);
}
catch(InterruptedException e) {}
resultType = "success";
}
catch (StarIOPortException e)
{
resultType = "error";
}
One more advise:
instead
port = StarIOPort.getPort("BT:", "mini", 10000, null);
use just
port = StarIOPort.getPort("BT:", "mini", 10000);
in plugin you will not use Context
Good luck.

Categories

Resources