EDIT: Using simpler code.
I have a blackberry app that until recently (I upgraded to 6.0.0.600 on my torch) could be deleted. Now I can't even if I use javaloader -u erase (module in use) and the UI context menu or physical menu has no option for delete. The code below just requires an app descriptor with an alternate entry point on startup with parameters "startVibrate". The entry points code is below.
package mypackage;
import net.rim.device.api.system.Alert;
import net.rim.device.api.system.ApplicationDescriptor;
import net.rim.device.api.system.ApplicationManager;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.UiApplication;
/**
* This class extends the UiApplication class, providing a
* graphical user interface.
*/
public class ScheduleAppDemo extends UiApplication
{
public static void main(String[] args)
{
if (args != null && args.length > 0 && "startVibrate".equals(args[0]))
{
scheduleVibrate();
}
else
{
ScheduleAppDemo app = new ScheduleAppDemo();
app.enterEventDispatcher();
}
}
public ScheduleAppDemo()
{
ScheduleAppDemoScreen screen = new ScheduleAppDemoScreen();
pushScreen(screen);
}
public class ScheduleAppDemoScreen extends MainScreen
{
public ScheduleAppDemoScreen()
{
setTitle("Schedule app demo");
}
}
private static void scheduleVibrate()
{
Alert.startVibrate(2550);
ApplicationDescriptor current = ApplicationDescriptor.
currentApplicationDescriptor();
current.setPowerOnBehavior(ApplicationDescriptor.DO_NOT_POWER_ON);
ApplicationManager manager = ApplicationManager.getApplicationManager();
manager.scheduleApplication(current, System.currentTimeMillis()
+ 60000, true);
}
}
Basically what this shows you is that it vibrates every minute. Unfortunately evidence says it appears the Application Manager keeps the background process running during the time and then just calls it again when it's time to run occurs again. This is the sample app from RIM. Deleting all Alternate Entry Points in the Application descriptor.xml allows the app to be deleted. Other than modifying that, reloading the cods, and then deleting it; how can I delete the app.
The behavior is 'correct', in as far as this is exactly how an application with an alternate entry point would behave in the field.
When an application has an alternate entry point, deleting it only marks it for deletion. The actual deletion occurs when the device is restarted and the .cods are reloaded.
This is why, when a user tries to delete an application that has an alternate entry point with a running process in the background, a dialog box always pops up letting the user know that the application will be removed on restart, and asking whether they would like to reboot now.
Until the device is rebooted, the background process will continue to run.
Related
I am trying to create a Xamarin.Forms app that will run on both iOS and Android. Eventually I need instances of the app to communicate with each other via Bluetooth, but I'm stuck on getting the iOS side to do anything with Bluetooth. I originally tried to work with Plugin.BluetoothLE and Plugin.BLE, but after a week and a half I was not able to get advertising or scanning to work on either OS with either plugin, so I decided to try implementing simple Bluetooth interaction using the .NET wrappers of the platform APIs, which at least are well documented. I did get scanning to work fine on the Android side. With iOS, though, what I have right now builds just fine, and runs on my iPad without errors, but the DiscoveredPeripheral handler is never called, even though the iPad is just a few inches from the Android tablet and presumably should be able to see the same devices. I have verified this by setting a breakpoint in that method, which is never reached; and when I open the Bluetooth Settings on the iPad to make it discoverable the app version on the Android tablet can see it, so I don't think it's an iPad hardware issue.
It seems obvious that there is simply some part of the process I don't know to do, but it's not obvious (to me) where else to look to find out what it is. Here is the code for the class that interacts with the CBCentralManager (as far as I understand from what I've read, this should include everything necessary to return a list of peripherals):
using MyBluetoothApp.Shared; // for the interfaces and constants
using CoreBluetooth;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;
[assembly: Dependency(typeof(MyBluetoothApp.iOS.PeripheralScanner))]
namespace MyBluetoothApp.iOS
{
public class PeripheralScanner : IPeripheralScanner
{
private readonly CBCentralManager manager;
private List<IPeripheral> foundPeripherals;
public PeripheralScanner()
{
this.foundPeripherals = new List<IPeripheral>();
this.manager = new CBCentralManager();
this.manager.DiscoveredPeripheral += this.DiscoveredPeripheral;
this.manager.UpdatedState += this.UpdatedState;
}
public async Task<List<IPeripheral>> ScanForService(string serviceUuid)
{
return await this.ScanForService(serviceUuid, BluetoothConstants.DEFAULT_SCAN_TIMEOUT);
}
public async Task<List<IPeripheral>> ScanForService(string serviceUuid, int duration)
{
CBUUID uuid = CBUUID.FromString(serviceUuid);
//this.manager.ScanForPeripherals(uuid);
this.manager.ScanForPeripherals((CBUUID)null); // For now I'd be happy to see ANY peripherals
await Task.Delay(duration);
this.manager.StopScan();
return this.foundPeripherals;
}
private void DiscoveredPeripheral(object sender, CBDiscoveredPeripheralEventArgs args)
{
this.foundPeripherals.Add(new CPeripheral(args.Peripheral));
}
private void UpdatedState(object sender, EventArgs args)
{
CBCentralManagerState state = ((CBCentralManager)sender).State;
if (CBCentralManagerState.PoweredOn != state)
{
throw new Exception(state.ToString());
}
}
}
}
Can anyone point me in the direction of understanding what I'm missing?
EDIT: O...K, I've discovered quite by accident that if I do this in the shared code:
IPeripheralScanner scanner = DependencyService.Get<IPeripheralScanner>();
List<IPeripheral> foundPeripherals = await scanner.ScanForService(BluetoothConstants.VITL_SERVICE_UUID);
twice in a row, it works the second time. I feel both more hopeful and much more confused.
The underlying problem was that in the first instantiation of PeripheralScanner, ScanForService was being called before State was updated. I tried many ways of waiting for that event to be raised so I could be sure the state was PoweredOn, but nothing seemed to work; polling loops simply never reached the desired state, but if I threw an Exception in the UpdatedState handler it was thrown within milliseconds of launch and the state at that time was always PoweredOn. (Breakpoints in that handler caused the debugging to freeze with the output Resolved pending breakpoint, which not even the VS team seems to be able to explain).
Reading some of the Apple developer blogs I found that this situation is most often avoided by having the desired action occur within the UpdatedState handler. It finally soaked into my thick head that I was never seeing any effects from that handler running because the event was being raised and handled on a different thread. I really need to pass the service UUID to the scanning logic, and to interact with a generic List that I can return from ScanForService, so just moving it all to the handler didn't seem like a promising direction. So I created a singleton for flagging the state:
internal sealed class ManagerState // .NET makes singletons easy - Lazy<T> FTW
{
private static readonly Lazy<ManagerState> lazy = new Lazy<ManagerState>(() => new ManagerState());
internal static ManagerState Instance { get { return ManagerState.lazy.Value; } }
internal bool IsPoweredOn { get; set; }
private ManagerState()
{
this.IsPoweredOn = false;
}
}
and update it in the handler:
private void updatedState(object sender, EventArgs args)
{
ManagerState.Instance.IsPoweredOn = CBCentralManagerState.PoweredOn == ((CBCentralManager) sender).State;
}
then poll that at the beginning of ScanForService (in a separate thread each time because, again, I will not see the updates in my base thread):
while (false == await Task.Run(() => ManagerState.Instance.IsPoweredOn)) { }
I'm not at all sure this is the best solution, but it does work, at least in my case. I guess I could move the logic to the handler and create a fancier singleton class for moving all the state back and forth, but that doesn't feel as good to me.
According to AppCenter's instructions when adding crash reporting and analytics to a Xamarin Android app:
Inside your app’s MainActivity.cs, add the following using statements.
using Microsoft.AppCenter;
using Microsoft.AppCenter.Analytics;
using Microsoft.AppCenter.Crashes;
In the same file, add the following in the OnCreate() method.
AppCenter.Start("xxxx-xxxx-xxxx-xxxx-xxxx",
typeof(Analytics), typeof(Crashes));
However, I have a splash activity that runs before MainActivity that is prone to crashing - and the crashes won't get reported if the splash activity crashes before MainActivity gets the change to start and call AppCenter.Start.
So I also added AppCenter.Start to the start of SplashActivity. Does this mean I should remove AppCenter.Start from MainActivity in case I am starting multiple instances? Or are AppCenter instances separate to each activity and I need to add AppCenter.Start to every activity in the project (e.g. including my settings page activity)?
Add a new class and inherit it from the Application class something like below:
#if DEBUG
[Application(Debuggable=true)]
#else
[Application(Debuggable = false)]
#endif
public class MainApp : Application
{
public MainApp(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public override void OnCreate()
{
base.OnCreate();
}
}
Override the OnCreate method, now this method will be executed every time an activities OnCreate method is executed.
So you can simply add the crash analytics code here something like:
public override void OnCreate()
{
base.OnCreate();
AppCenter.Start("xxxx-xxxx-xxxx-xxxx-xxxx",
typeof(Analytics), typeof(Crashes));
}
I have an adobe air application - AS3 for iOs and Android.
Whenever the user clicks the home button, and thus the application is now in the background, the application automatically stops, which is the expected behavior. Now, if the user is in the application, and he double clicks his home button, showing all the multiple windows, the application continues running, which is not what i want. How can i access that state ( Not Background, not foreground )? If i can access it, i would then put my pausing code into that state, but how can i access that particular state?
When the user clicks the home button the app is moved to the background and suspended. The app isn't closed. The OS can close the app to free memory. If your app is a memory hog you'll see this happening.
You use events dispatched by the NativeApplication object. Below is example code to listen and handle these events.
import flash.events.Event;
import flash.desktop.NativeApplication;
import flash.desktop.SystemIdleMode;
// create listeners to NativeApplication
private var naApplication: NativeApplication;
naApplication = NativeApplication.nativeApplication;
naApplication.addEventListener(Event.ACTIVATE, eActivate);
naApplication.addEventListener(Event.DEACTIVATE, eDeactivate);
naApplication.addEventListener(Event.EXITING, eExiting);
private function eActivate(e: Event): void {
// app has opened or resumed
application.systemIdleMode = SystemIdleMode.KEEP_AWAKE;
}
private function eDeactivate(e: Event): void {
// app is going to be moved to background
application.systemIdleMode = SystemIdleMode.NORMAL;
}
private function eExiting(e: Event): void {
// app is going to be closed by user or by the OS (usually to free up memory)
// do whatever exit code here then remove all listeners (to be clean don't rely on OS to close them)
application.removeEventListener(Event.ACTIVATE, eActivate);
application.removeEventListener(Event.DEACTIVATE, eDeactivate);
application.removeEventListener(Event.EXITING, eExiting);
application.systemIdleMode = SystemIdleMode.NORMAL;
removeEventListener(Event.ENTER_FRAME, eMainTimer);
}
The systemIdleMode and ENTER_FRAME are just examples of typical code. Let me know of any questions.
I'm fairly new to writing BlackBerry applications, so maybe this is a stupid thing I'm overlooking. I have to use JDE 5 (client requirement) to support the older BlackBerry Curve 8520 phones.
What I am experiencing is that as soon as I place a DateField on my interface, the application slows down considerably, causing the UI to stutter. Even a simple layout that only has a single DateField and a button has the same effect. Then, as soon as I move on to the next layout, everything is fine again.
One of the layouts are created as follows (please comment if this is the incorrect way of doing it):
public void displaySomeLayout() {
final ButtonField okButton = new ButtonField("OK");
final DateField dobField = new DateField("Birthday", System.currentTimeMillis(), DateField.DATE);
/* some other non-ui code */
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
applicationFieldManager.addAll(new Field[] {
dobField,
okButton
});
}
});
}
The application then just slows down a lot. Sometimes, after a minute of so it starts responding normally again, sometimes not.
The displaySomeLayout() method is called from the contructor of the Screen extending class. And then applicationFieldManager is a private VerticalFieldManager which is instantiated during class construction.
I'm not sure the problem is in the code that you've shown us. I think it's somewhere else.
However, here are a couple recommendations to improve the code you've shown:
Threading
First of all, the code you show essentially is being run in the Screen subclass constructor. There is almost no difference between this code:
public MyScreen() {
Field f = new ButtonField("Hello", ButtonField.CONSUME_CLICK);
add(f);
}
and this:
public MyScreen() {
addField();
}
private void addField() {
Field f = new ButtonField("Hello", ButtonField.CONSUME_CLICK);
add(f);
}
So, because your code is being run in the screen class's constructor, it should already be running on the UI thread. Therefore, there's no reason to use UiApplication.getUiApplication().invokeLater() here. Instead, just use this:
public void displaySomeLayout() {
final ButtonField okButton = new ButtonField("OK");
final DateField dobField = new DateField("Birthday", System.currentTimeMillis(), DateField.DATE);
/* some other non-ui code */
applicationFieldManager.add(dobField);
applicationFieldManager.add(okButton);
}
Sometimes, you do need to use invokeLater() to run UI code, even when you're already on the UI thread. For example, if your code is inside the Manager#sublayout() method, which runs on the UI thread, adding new fields directly will trigger sublayout() to be called recursively, until you get a stack overflow. Using invokeLater() can help there, by deferring the running of a block of code until sublayout() has completed. But, from the constructor of your screen class, you don't need to do that.
ObjectChoiceField
I'm also worried about the ObjectChoiceField you said you were using with 250 choices. You might try testing this field with only 10 or 20 choices, and see if that makes a difference.
But, even if the 250 choice ObjectChoiceField isn't the cause of your performance problems, I would still suggest a different UI.
On BlackBerry Java, you can use the AutoCompleteField. This field can be given all the country choices that you are now using. The user starts typing the first couple letters of a country, and quickly, the list narrows to just those which match. I personally think this is a better way to get through a very large list of choices.
I need to add GPS functionality to an existing Blackberry Application that I've written. I write a stand alone class called CurrentLocation, and include a method to set the various location variables I care about by using the blackberry GPS in conjunction with google's reverse geocoding webservices. Everything is working beautifully, that is, until I try to instantiate my new class in my main application.
No matter what I do, I get a LocationException! .getLocation() doesn't work!
It really confuses me, because if I instantiate my class in a test hello world app, it works just fine.
Are there limits to where you can instantiate classes? I've not encountered any with previous classes I've written. In this case, I'm instantiating my CurrentLocation class in a listener (so the user only makes the lengthy gps and web calls when they want to). I've tried instantiating it in screens, as well. I've tried just gutting the class and using the method call, but that doesn't work either.
Is there something I'm missing entirely here?
http://pastie.org/639545
There's a link to the class I'm making,
And here's the listener I"m trying to instantiate from. I'm in an event thread because I thought it might help (but I get the same exception whether or not I do this).
FieldChangeListener listenerGPS = new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
CurrentLocation loc = new CurrentLocation();
if (loc != null){
country = loc.getCountry();
city = loc.getCity();
state = loc.getState();
road = loc.getRoad();
zip = loc.getZip();
}
}
});
}
};
What am I missing here?
Okay, I got it. Apparently you can't call getLocation() in the eventThread (not just invokeLater, but any listener). So now what I'm doing is getting the coordinates in a thread outside of the event, and worrying about google separately.