I'm using user's geolocation via phonegap.The exapmle is shown below. (ANDROID)
// onSuccess Callback
// This method accepts a Position object, which contains the
// current GPS coordinates
//
var onSuccess = function(position) {
var element = document.getElementById('geolocation');
element.innerHTML = 'Latitude: '+ position.coords.latitude +'<br/>' +
'Longitude: ' + position.coords.longitude +<br/>' +
'Altitude: ' + position.coords.altitude +'<br/>' +
'Accuracy: ' + position.coords.accuracy +<br/>' +
'Altitude Accuracy: ' + position.coords.altitudeAccuracy +'<br/>' +
'Heading: ' + position.coords.heading +<br/>' +
'timestamp: ' + position.timestamp +<br/>';
};
// onError Callback receives a PositionError object
//
function onError(error) {
alert('code: ' + error.code + '\n' +
'message: ' + error.message + '\n');
}
navigator.geolocation.getCurrentPosition(onSuccess, onError);
Is it possible (using phonegap) on error function to open dialog which will lead us to location settings (where user will be able to give me access to his location) instead of alert, as it's done in google maps android application (screenshot below) ?
I'd do just what you are saying, create the dialog, and send them to settings; I'm assuming you have managed to get the onError fcn called at the apropos time, so then
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(getString(R.string.loc_man));
builder.setMessage(getString(R.string.ask_for_gps));
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which)
{
Intent i = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(i);
}
});
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which)
{
Toast.makeText(getBaseContext(), getString(R.string.gps_no), Toast.LENGTH_SHORT).show();
finish();
}
});
builder.create().show();
what you also need to do is call this function from your javascript... Oh, this is fun, b/c you'll be using a lot of pretty cool java concepts all at once and I've only worked with Cordova, but this should all work the same =] Keep in mind that if you see some variable undefined in my code, you should probably assume it's a feild.
so let's say you are sure this code hits on error, let's make up an interface name; 'Android' is a pretty logical one
function onError(error) {
alert('code: ' + error.code + '\n' +
'message: ' + error.message + '\n');
Android.TurnOnTuneIn();
}
now back in your Java, grep around your phonegap app to find wherever it has 'WebView'; if it's anything like my cordova implementations, it'll have webview.loadUrl() in it somewhere. Open the file that defines this class; this is the one to edit/place all the java we are working on in this question. After something like 'public class PhoneGapActivity extends Activity', insert 'implements CordovaInterface' ( I think not PhoneGapInterface ); if your IDE gives you an error with the option to implement abstract methods, pound that.
Make sure the WebView is a feild in the class, not a variable in a method, then
//CordovaWebView should work, but maybe it needs to be PhoneGapWebView, you're smart, you'll figure it out =]
webView = (CordovaWebView) findViewById(R.id.data_window);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setBuiltInZoomControls(false);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); //this always seemed like a good idea to me
webView.addJavascriptInterface(new JSInterface(this), "Android"); //see the interface name? =]
//this line should already be in this class, just re-use it
webView.loadUrl("file:///"+getContext().getFilesDir().toString()+"/index.html");
now make that interface:
public class JSInterface {
// these fcns are exposed to the WebView
private Context context;
public JSInterface(Context context)
{
context = context;
}
#JavascriptInterface
public void doEchoTest(String echo)
{
//A useful test; I usually use it in a webViewClient that runs the function that calls this in my javascript with this:
//webView.loadUrl("javascript:echo('"echo!!!"');"); //this exact formatting
//but webViewClient is outside the scope of your question and would just confuse the
//issue; however, you may need to implement
//webViewClient with an delaying asynctask to seperatly run the js you loaded with the webpage,
//again, I used to do this ground-up, and phoneGap Might Just Work.
Toast.makeText(context, echo, Toast.LENGTH_SHORT).show();
}
#JavascriptInterface
public void TurnOnTuneIn
{
aMethodThatHasThatFirstBitOfCodeIPublished();
}
}
I love interfaces =]
The rest is fairly boilerplate for your purposes ( but fun to play with nevertheless! ) and you might not need much if any of it. If you notice the method I put up top is not returning the way you want it to, you can use the startActivityForResult method to do the same and I'll bet it'll work nicely; Note the 'super.' call that the Override makes; you only need the Intent ( like I showed you up top ) and some reference number for receiving the result... again, out of the scope of the question. ALso, I included support for the ResultCallback b/c a guy I was working with used it, but I don't use it myself.
#Override
public Activity getActivity(){
return this;
}
#Override
public void setActivityResultCallback(CordovaPlugin plugin) {
this.activityResultCallback = plugin;
}
public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode) {
this.activityResultCallback = command;
this.activityResultKeepRunning = this.keepRunning;
// If multitasking turned on, then disable it for activities that return results
if (command != null) {
this.keepRunning = false;
}
// Start activity
super.startActivityForResult(intent, requestCode);
}
#Override
public void cancelLoadUrl(){
// no op
}
#Override
public Object onMessage(String id, Object data) {
LOG.d("is", "onMessage(" + id + "," + data + ")");
if ("exit".equals(id)) {
super.finish();
}
return null;
}
#Override
public void onPause() {
Method pause = null; // Pauses the webview.
try {
pause = WebView.class.getMethod("onPause");
}
catch (SecurityException e) { }
catch (NoSuchMethodException e) { }
if (pause != null) {
try { pause.invoke(webView);
}
catch (InvocationTargetException e) { }
catch (IllegalAccessException e) { }
}
else {
// No such method. Stores the current URL.
suspendUrl = webView.getUrl(); // And loads a URL without any processing.
webView.loadUrl("file:///android_asset/nothing.html");
}
super.onPause();
}
#Override
public void onResume() {
super.onResume();
Method resume = null; // Resumes the webview.
try {
resume = WebView.class.getMethod("onResume");
}
catch (SecurityException e) { }
catch (NoSuchMethodException e) { }
if (resume != null) {
try {
resume.invoke(webView);
}
catch (InvocationTargetException e) { }
catch (IllegalAccessException e) { }
}
else if (webView != null) { // No such method. Restores the suspended URL.
if (suspendUrl == null) {
//this should be wherever you have your page; you can probably copy it from what is there now.
webView.loadUrl("file:///"+getContext().getFilesDir().toString()+"/index.html");
}
else {
webView.loadUrl(suspendUrl);
}
}
}
Hope that gets you further along; if you don't use it already, remember to use version control, so you can be reckless with your edits!
gl hf
I share with you, this phonegap plugin, that lets you do that. I added that functionality to that plugin as a contribuitor.
https://github.com/BastienL/GPSDetector
Related
I have a search functionality in my Android app. The searchView has callback onSearchTextChanged(String oldQuery, String newQuery). This callback function calls every time user enters any text to search field. I have a REST call in this callback which is triggering every time each word is being typed. Which leads to multiple calls for REST. I want to avoid that. I just want only one instance active at a time which is latest one and all other calls should be canceled instantly whenever the new letter is typed. Below are code details.
#Override
public void onSearchTextChanged(String oldQuery, String newQuery) {
floatingSearchView.showProgress();
moviesAPI.searchQuery(newQuery)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Search>() {
#Override
public void onCompleted() {
Log.d(TAG, "onCompleted() called");
}
#Override
public void onError(Throwable e) {
Log.d(TAG, "onError() called with: e = [" + e + "]");
}
#Override
public void onNext(Search search) {
floatingSearchView.hideProgress();
floatingSearchView.swapSuggestions(search.getSearchResults());
Log.d(TAG, "onNext() called with: search = [" + search + "]");
}
}
);
}
You probably want to use debounce to delay search requests while typing (and only after user stops typing for x duration).
Additionally, for canceling older requests when new request arrive you may transform the text changes events to stream of event (either manually or using library like RxBindings), and then switchMap each new text event to query.
switchMap operator will cancel previous request that still going, when new request arrive driven by new search text stream.
you can found various examples of implementing search suggestions for android with rxjava, like this one by Kaushik Gopal: instantauto-searching-text-listeners-using-subjects--debounce
code sample, this sample uses RxBinding library for easily getting textChanages events as Observable stream.
RxTextView.textChangeEvents(tv)
.debounce(300, TimeUnit.MILLISECONDS)
.switchMap(new Func1<TextViewTextChangeEvent, Observable<SearchResult>>() {
#Override
public Observable<SearchResult> call(
TextViewTextChangeEvent textViewTextChangeEvent) {
return moviesAPI.searchQuery(textViewTextChangeEvent.text().toString())
.subscribeOn(Schedulers.io());
}
})
.subscribe(new Subscriber<SearchResult>() {
#Override
public void onCompleted() {
Log.d(TAG, "onCompleted() called");
}
#Override
public void onError(Throwable e) {
floatingSearchView.hideProgress();
Log.d(TAG, "onError() called with: e = [" + e + "]");
}
#Override
public void onNext(SearchResult search) {
floatingSearchView.hideProgress();
floatingSearchView.swapSuggestions(search.getSearchResults());
Log.d(TAG, "onNext() called with: search = [" + search + "]");
}
});
Having set a system property in android using the setprop command (through adb) is there a way to listen to this change in my own service?
I tried with SystemProperties.addChangeCallback and was not notified. Was there something that I missed?
You can create a method in your service which should fetch any Systemproperty and that method should call Looper.loop(); so that that loop will poll for SystemProperty time to time
This implementation may not be optimized way of doing this but it is used in Android 4.4.2, you can see here http://androidxref.com/4.4.2_r2/xref/frameworks/base/services/java/com/android/server/SystemServer.java
you can see at above link:
boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
boolean disableMedia = SystemProperties.getBoolean("config.disable_media", false);
boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);
boolean disableTelephony = SystemProperties.getBoolean("config.disable_telephony", false);
boolean disableLocation = SystemProperties.getBoolean("config.disable_location", false);
boolean disableSystemUI = SystemProperties.getBoolean("config.disable_systemui", false);
boolean disableNonCoreServices = SystemProperties.getBoolean("config.disable_noncore", false);
boolean disableNetwork = SystemProperties.getBoolean("config.disable_network", false);
These boolean variables are being checked in initAndLoop() method with the help of Looper.loop(); here you can notify your other components on any change in even a single SystemProperty.
Another way is to create static callback and get call for any change in any of SystemProperty, see the master branch's code for SystemService here: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/os/SystemService.java
you can see in above link what following code is doing:
private static Object sPropertyLock = new Object();
static {
SystemProperties.addChangeCallback(new Runnable() {
#Override
public void run() {
synchronized (sPropertyLock) {
sPropertyLock.notifyAll();
}
}
});
}
/**
* Wait until given service has entered specific state.
*/
public static void waitForState(String service, State state, long timeoutMillis)
throws TimeoutException {
final long endMillis = SystemClock.elapsedRealtime() + timeoutMillis;
while (true) {
synchronized (sPropertyLock) {
final State currentState = getState(service);
if (state.equals(currentState)) {
return;
}
if (SystemClock.elapsedRealtime() >= endMillis) {
throw new TimeoutException("Service " + service + " currently " + currentState
+ "; waited " + timeoutMillis + "ms for " + state);
}
try {
sPropertyLock.wait(timeoutMillis);
} catch (InterruptedException e) {
}
}
}
}
/**
* Wait until any of given services enters {#link State#STOPPED}.
*/
public static void waitForAnyStopped(String... services) {
while (true) {
synchronized (sPropertyLock) {
for (String service : services) {
if (State.STOPPED.equals(getState(service))) {
return;
}
}
try {
sPropertyLock.wait();
} catch (InterruptedException e) {
}
}
}
}
This information originates from Shridutt Kothari. Check this google post about listening to single SystemProperty changes
Too long for comments, so adding as an answer:
The setprop tool does not appear to fire change callbacks. In my read of the OS source, it simply sets a property value in a hashmap (see: https://cs.android.com/android/platform/superproject/+/master:system/libbase/properties.cpp;bpv=1;bpt=1;l=133?q=setprop).
For the callback to happen, someone needs to call do_report_sysprop_change (https://cs.android.com/android/platform/superproject/+/master:system/core/libutils/misc.cpp;bpv=1;bpt=1;l=102?q=syspropchange&ss=android%2Fplatform%2Fsuperproject&gsn=do_report_sysprop_change&gs=kythe%3A%2F%2Fandroid.googlesource.com%2Fplatform%2Fsuperproject%3Flang%3Dc%252B%252B%3Fpath%3Dsystem%2Fcore%2Flibutils%2Fmisc.cpp%23tC5_BHx-Z-jKUw_rRXWlL0wtVyVh15Oh60E0YnrdfSg&gs=kythe%3A%2F%2Fandroid.googlesource.com%2Fplatform%2Fsuperproject%3Flang%3Dc%252B%252B%3Fpath%3Dsystem%2Fcore%2Flibutils%2Fmisc.cpp%234FfZ9jgPIUEg7IKCGYGLCaQ4-cj6enFs8AeI7SIRLBs) and this is not done via setprop. I do see it invoked in a variety of places in the OS since invocation of the set prop callback is a method in the IBase interface implemented by a variety of services in Android.
public String newUser = "false";
public double lat = 0.0, lon = 0.0;
I have the following function in my android app (called when a Button is clicked) which starts a thread:
public void SignUpFunction(View view) {
assignValues();
String filledAll = checkIfFilled();
if (filledAll.equals("true")) {
Log.d("LIFECYCLE", "calling thread..");
//my thread
new validateThread().start();
Log.d("After thread start","This log call does not occur");
if (newUser.equals("true")) {
Toast.makeText(this, "Please wait as we obtain your location", Toast.LENGTH_SHORT).show();
getMyLocationFunction();
} else {
return;
}
}
}
validateThread:
class validateThread extends Thread {
public void run() {
Log.d("LIFECYCLE", "validateThread entered...");
try {
newUser = "true";
Log.d("validateThread", "Validated and found new user");
} catch (Exception e) {
Log.d("validateThread", "Exception in validateThread: " + e.getMessage());
}
}
}
The thread runs correctly...but after the last line, it does not go back to its point of start. I don't understand why this is happening because I've used threads before and they all work correctly.
I know I can just give the getMyLocation function inside the thread but I really need it this way.
I've searched for similar questions but none helped.. What am I doing wrong here?
Thanks in advance.
It's a race. SignUpFunction should wait until validateThread decides whether or not to set newUser = "true". Even with the race your code may work sometimes, but that is by accident.
As recommended in the available documentation I decided to implement an automatic update whenever there is an update of the version of my application.
For doing that I have a service that is running in the background performing several operations appart from the GCM update. This service is calling a class that performs all operations related to GCM.
So, basically, this is the call to performed in the Service:
try {
PackageInfo info = manager.getPackageInfo(this.getPackageName(), 0);
currentVersion = info.versionCode;
} catch (NameNotFoundException e) {
//Handle exception
}
if (registeredVersion != currentVersion) {
Log.i(ApplicationData.APP_TAG, TAG + ": New version, updating");
GcmUpdater upGcm = new GcmUpdater(getApplicationContext());
Boolean update = upGcm.getAndUpdate();
//We update the current version
if (update) {
prefs.setAppPrevVersion(currentVersion);
} else {
Log.e(ApplicationData.APP_TAG, TAG + ": GCM not updated");
}
} else {
Log.i(ApplicationData.APP_TAG, TAG + ": Same version, no GCM needed");
}
Ok, I think the key point in the previous code is that I am initiating the class called GcmUpdater is initiated using the application context given by the service.
The constructor of my class GcmUpdater is the following:
public GcmUpdater(Context cont) {
context = cont;
TAG = getClass().getName();
prefs = new StorePreferences(context);
}
Nothing special, as you can see I am calling the method inside GcmUpdater called getAndUpdate(), this method is the following one
public Boolean getAndUpdate() {
String new_regid = giveRegId();
return updateGCM(new_regid);
}
Ok, the problem is coming now, is the public function giveRegId()
public String giveRegId() {
try{
return new RegisterGCM().execute().get();
}catch(Exception ex){
ex.printStackTrace();
return null;
}
}
Which calls to the asyncronous task RegisterGCM....
public class RegisterGCM extends AsyncTask<Void,Void,String>
{
#Override
protected String doInBackground(Void... arg0)
{
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(ApplicationData.SENDER_ID);
Log.i(ApplicationData.APP_TAG, TAG +":Device registered, registration ID=" + regid);
} catch (IOException ex) {
Log.e(ApplicationData.APP_TAG, TAG + ": " + ex.getMessage());
}
return regid;
}
protected void onPostExecute(Boolean result) {
return ;
}
}
The problem I am facing is that the variable regid obtained is null and according to similar problems like this one or this other one, I should include the ApplicationContext, however that is passed as parameter in the constructor.
Moreover, the class RegisterGCM is used by my main activity and works. So my guess has been always that the way to call to register the GCM code is the one that is creating the problem, but is not clear why.
What am I doing wrong? I have not been able to find any explication of this problem in google.
Your sender ID is equals to Google Console Project Id?
I want to make recharge process just by entering the recharge number and add up 401 at the beginning and # at last eg: *401*12387263736748# . but call goes like: *401*12387263736748
without "#" included.
RechargeRequestButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
RechargeCode = "*401*"
+ RechargeCodeView.getText().toString()+"#";
Intent RechargeingIntent = new Intent(Intent.ACTION_CALL, Uri
.parse("tel:"+RechargeCode));
try {
startActivity(RechargeingIntent);
finish();
} catch (Exception e) {
Log.e("Exception", e.getMessage());
Toast.makeText(getApplicationContext(), "Cannot make call",
Toast.LENGTH_SHORT).show();
}
}
});
Just replace "#" with Uri.encode("#") or "%23". But I will not recommend this, as this can cause some security issues.