Cordova phonegap android application does not exit - android

I am New in phonegap cordova development.i make an demo apllication with help of google in cordova.here i take a button named exitapp and called function navigator.app.exitApp() function.here it is working fine but if I add some methods in mainActivity,java file then navigator.app.exitApp() function is not working.
I used
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
in html page.
my activity code is.
package com.example.hello;
import com.acl.paychamp.util.DecryptData;
import com.example.hello.R;
public class MainActivity extends CordovaActivity implements
CordovaInterface
{
CordovaWebView cwv;
private final ExecutorService threadPool = Executors.newCachedThreadPool();
private CordovaPlugin activityResultCallback;
public static final String PASSKEY = "s407iejl";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.init();
setContentView(R.layout.main);
cwv = (CordovaWebView) findViewById(R.id.view);
cwv.loadUrl(this.launchUrl);
}
#Override
public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode) {
this.activityResultCallback = command;
// Start activity
super.startActivityForResult(intent, requestCode);
}
#Override
public void setActivityResultCallback(CordovaPlugin plugin) {
this.activityResultCallback = plugin;
}
#Override
public Activity getActivity() {
return this;
}
#Override
public Object onMessage(String id, Object data) {
try {
URI uri = new URI(data.toString());
if (uri.toString().contains("http://abcd.com/pr?
param=")) {
String encryptedText = uri.getQuery().split("=")[1];
JSONObject jObj = DecryptData.getDecryptedJson(encryptedText,
PASSKEY);
Log.i("CordovaApp", jObj.get("msg").toString());
Log.i("CordovaApp", jObj.get("trxid").toString());
cwv.loadUrl("file:///android_asset/www/status.htm?
resparam="+encryptedText);
}
} catch (Exception use) {
}
return null;
}
#Override
public ExecutorService getThreadPool() {
return threadPool;
}
#Override
protected void onActivityResult(final int requestCode, final int
resultCode, final Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
CordovaPlugin callback = this.activityResultCallback;
if (callback != null) {
callback.onActivityResult(requestCode, resultCode, intent);
}
}
}

In case you mean by minimizing that app that the application goes to the background (is not the active application but it is still in the list of running applications). In case that is what you mean, please notice that this is the normal behavior in Android and many Mobile OS.
Terminating (exiting if you prefer) the application is not under the control of the application developer. It is completely managed by the OS (Android in this case). So, what you have is the normal behavior for apps under Mobile OS.
SOURCE : navigator.app.exitapp() not working in android device

Related

Android Your app(s) are vulnerable to Intent Redirection

I got an email from google play support saying "Intent Redirection Your app(s) are vulnerable to Intent Redirection. To address this issue, follow the steps in this Google Help Center article."
After reading through the article, I'm guessing the key is my app should not call startActivity, startService, sendBroadcast, or setResult on untrusted Intents (intents used by external apps to invoke my app for example) without validating or sanitizing these Intents.
However, solution 1 in the article doesn't work in my case because my component needs to receive Intents from other apps.
Solution 2 is not applicable to my case because I don't know in advance which app would invoke my app, so I don't know what would getCallingActivity returns.
Solution 3 seems to be the most promising one, I tried to removeFlags of intents, however, when I resubmit my app, Google Play again alerts this vulnerability. I am about to try checking whether an Intent grants a URI permission using methods like getFlags and submit my app again to see the result. Does anyone know how do Google check this vulnerability anyway, and could someone spot the vulnerability in my source code and suggest a way to resolve it?
The exact message from Google Play is
Intent Redirection
Your app(s) are vulnerable to Intent Redirection.
To address this issue, follow the steps in this Google Help Center article.
com.mydomain.somepackage.a->a
And the following is the simplified source code.
// MainActivity.java
public class MainActivity extends CordovaActivity
{
SpecialUtil specialUtil;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
specialUtil = new specialUtil(MainActivity.this);
}
#Override
public void onResume() {
super.onResume();
specialUtil.verifyServerIfNeeded(MainActivity.this);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == this.specialUtil.CERT_INVALID_POPUP_REQUEST_CODE) {
// the user clicked the return button in the alert dialog within WhiteScreen activity
this.specialUtil.declareAsFailure();
}
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
}
// com/mydomain/somepackage/SpecialUtil.java
public class SpecialUtil {
private SharedPreferences mSharedPreferences;
private SharedPreferences.Editor mSharedPreferencesEditor;
private SharedPreferences.OnSharedPreferenceChangeListener listener;
private Activity activity;
private boolean shownCertInvalidPopup = false;
public final int CERT_INVALID_POPUP_REQUEST_CODE = 1000;
public SpecialUtil(Activity activity) {
this.activity = activity;
this.mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
this.mSharedPreferencesEditor = mSharedPreferences.edit();
this.listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals("SOME_RESULT")) {
String result = mSharedPreferences.getString("SOME_RESULT", "");
if (result.equals("RESULT_OK")) {
SpecialUtil.this.declareAsSuccess();
} else if (result.equals("RESULT_CANCELED")) {
SpecialUtil.this.declareAsFailure();
}
}
}
};
this.mSharedPreferences.registerOnSharedPreferenceChangeListener(listener);
}
public void verifyServerIfNeeded(Activity activity) {
Intent intent = activity.getIntent();
if (this.isFlowA(intent)) {
this.removePermission(intent);
String url = intent.getStringExtra("url");
this.verifyServer(url);
} else if (this.isFlowB(intent)) {
this.removePermission(intent);
String payment_request_object_url = intent.getData().getQueryParameter("pay_req_obj");
String callback_url = intent.getData().getQueryParameter("callback");
this.verifyServer(payment_request_object_url);
}
}
public boolean isFlowA(Intent intent) {
if (intent.getAction().equals("someAction")) {
return true;
}
return false;
}
public boolean isFlowB(Intent intent) {
if (intent.getData() != null) {
String path = intent.getData().getPath();
if (path.equals("something")) {
return true;
}
}
return false;
}
public void verifyServer(final String httpsURL) {
new Thread(new Runnable() {
#Override
public void run() {
try {
boolean isCertValid = SpecialUtil.this.verify(httpsURL);
if (isCertValid) {
// do somthing
} else {
// show a white screen with an alert msg
SpecialUtil.this.activity.runOnUiThread(new Runnable() {
public void run() {
if (!shownCertInvalidPopup) {
shownCertInvalidPopup = true;
Intent intent = new Intent(SpecialUtil.this.activity, WhiteScreen.class);
SpecialUtil.this.activity.startActivityForResult(intent, CERT_INVALID_POPUP_REQUEST_CODE);
}
}
});
}
} catch (IOException e) {
SpecialUtil.this.declareAsFailure();
}
}
}).start();
}
private void declareAsSuccess() {
this.activity.setResult(Activity.RESULT_OK, SpecialUtil.this.activity.getIntent());
this.activity.finishAndRemoveTask();
}
public void declareAsFailure() {
this.activity.setResult(Activity.RESULT_CANCELED, this.activity.getIntent());
this.activity.finishAndRemoveTask();
}
private void removePermission(Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.removeFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.removeFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
}
// com/mydomain/somepackage/WhiteScreen.java
public class WhiteScreen extends Activity {
SpecialUtil specialUtil;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
specialUtil = new SpecialUtil(WhiteScreen.this);
String title = "someTitle";
final AlertDialog.Builder builder = new AlertDialog.Builder(WhiteScreen.this)
.setTitle(title)
.setPositiveButton(btn_text, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Don't start the process, quit App immediately
WhiteScreen.this.setResult(Activity.RESULT_CANCELED, WhiteScreen.this.getIntent());
WhiteScreen.this.finishAndRemoveTask();
}
});
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
}

Android - duplicate "open with" system dialog instance after orientation change

I have simple activity with one button. When button is clicked, I'm firing intent to pick image from gallery. Something strange is going on when I fire intent and then rotate the screen. Here are the steps:
Click button. "Open with" system dialog appears.
Rotate screen. Activity gets recreated, dialog is still shown. Note - I don't call startActivityForResult(Intent, int) again on my activity recreate.
Tap back button. "Open with" dialog disappear, but there is another one beneath it.
So it seems even though I don't call startActivityForResult(Intent, int), new instance of dialog gets created every orientation change, and old instance don't getting destroyed.
Does anyone facing this issue? How to get rid of these duplicate dialogs?
Update 1:
So, here is some sample code:
public class MainActivity extends AppCompatActivity
{
private boolean mIsStarted = false;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null)
{
mIsStarted = savedInstanceState.getBoolean("key");
}
if (!mIsStarted)
{
mIsStarted = true;
Intent intent = new Intent(Intent.ACTION_PICK).setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MimeType.IMAGE);
startActivityForResult(intent, 1);
}
}
#Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putBoolean("key", mIsStarted);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
mIsStarted = false;
}
}
I also tried to set android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc" and still every time I rotate the screen, new copy of dialog (actually this is not a dialog, this is ResolverActivity) being showed on top of previous one. Is this some Android bug or it's just me doing something wrong?
Update 2: so I tried another approach - call finishActivity(int) inside my Activity.onStop(). Result is pretty strange - now I've got only 2 copies of ChooserActivity. After second copy is created, it starts rotating fine.
Here is the code:
public class MainActivity extends AppCompatActivity
{
private static final String LOG_TAG = MainActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState)
{
Log.d(LOG_TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.make_photo).setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
Intent intent = new Intent(Intent.ACTION_PICK).setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MimeType.IMAGE);
startActivityForResult(intent, 1);
}
});
}
#Override
protected void onStop()
{
Log.d(LOG_TAG, "onStop");
super.onStop();
finishActivity(1);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
Log.d(LOG_TAG, String.format("onActivityResult[requestCode:%d, resultCode:%d, data:%s]",
requestCode, resultCode, data != null ? data.toString() : "NULL"));
}
}
Still wonder why second copy is being created.
Add this to the activity declatation in your androidmanifest.xml
android:configChanges="orientation|screenSize"
This will prevent the activity from getting recreated and resolve the issue of duplicate dialog.
Find out this was silly mistake in my base Activity class. I end up with deriving all my Activities which works with intents from this base IntentProcessingActivity class.
/**
* Base activity for all activities which process intents. This activity saves processing state
* during recreation, so derived activities can get rid of this. This is useful for not showing
* "Open with" dialogs multiple times.
* <p />
* Derived activities can check if some intent is currently processing with {#link
* #isProcessingIntent()} function.
* <p />
* Created by Maksimov Stanislav (s.maks04#gmail.com) on 25.01.16
*/
public class IntentProcessingActivity extends AppCompatActivity
{
private static final String KEY_IS_PROCESSING_INTENT = "IsProcessingIntent";
private boolean mIsProcessingIntent;
#CallSuper
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
{
mIsProcessingIntent = savedInstanceState.getBoolean(KEY_IS_PROCESSING_INTENT, false);
}
}
#CallSuper
#Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_IS_PROCESSING_INTENT, mIsProcessingIntent);
}
#CallSuper
#Override
public void startActivityForResult(Intent intent, int requestCode, Bundle options)
{
mIsProcessingIntent = true;
super.startActivityForResult(intent, requestCode, options);
}
#CallSuper
#Override
public void startActivityForResult(Intent intent, int requestCode)
{
mIsProcessingIntent = true;
super.startActivityForResult(intent, requestCode);
}
#CallSuper
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
mIsProcessingIntent = false;
super.onActivityResult(requestCode, resultCode, data);
}
protected final boolean isProcessingIntent()
{
return mIsProcessingIntent;
}
}
On child Activities I just check
if (!isProcessingIntent())
{
startActivityForResult(...);
}

Cordova CLI Phone Gap Application navigator.app.exit Function not working

I am New Cordova phoneGap Development .i am impliment Simple Cordova Helloworld example it is working fine and also backbutton click Navigator.app.exit working fine.i include some code in My MainActivity After That Navigator.app.exit funtion not call on button click as well back press.
Please Give Me Some Suggestion Thank You.
public class CordovaApp extends CordovaActivity implements
CordovaInterface {
CordovaWebView cwv;
private final ExecutorService threadPool =
Executors.newCachedThreadPool();
private CordovaPlugin activityResultCallback;
public static final String PASSKEY = "s407iejl";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.init();
//super.appView.getSettings().setJavaScriptEnabled(true);
MyClass mc = new MyClass(this, appView);
appView.addJavascriptInterface(mc, "MyCls");
setContentView(R.layout.main);
cwv = (CordovaWebView) findViewById(R.id.buyView);
cwv.loadUrl(this.launchUrl);
}
#Override
public void startActivityForResult(CordovaPlugin command, Intent intent,
int requestCode) {
this.activityResultCallback = command;
// Start activity
super.startActivityForResult(intent, requestCode);
}
#Override
public void setActivityResultCallback(CordovaPlugin plugin) {
this.activityResultCallback = plugin;
}
#Override
public Activity getActivity() {
return this;
}
#Override
public Object onMessage(String id, Object data) {
try {
URI uri = new URI(data.toString());
if (uri.toString().contains("http://asdf.com/proces?
param=")) {
String encryptedText = uri.getQuery().split("=")[1];
JSONObject jObj = DecryptData.getDecryptedJson(encryptedText,
PASSKEY);
Log.i("CordovaApp", jObj.get("msg").toString());
Log.i("CordovaApp", jObj.get("trxid").toString());
cwv.loadUrl("file:///android_asset/www/payment_status.htm?
resparam="+encryptedText);
}
} catch (Exception use) {
}
return null;
}
#Override
public ExecutorService getThreadPool() {
return threadPool;
}
#Override
protected void onActivityResult(final int requestCode, final int
resultCode, final Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
CordovaPlugin callback = this.activityResultCallback;
if (callback != null) {
callback.onActivityResult(requestCode, resultCode, intent);
}
}
}

How to work with CordovaActivity class?

I'm using cordova in my project and I want to get access to some urls.
I have a class which extends CordovaActivity class. in Myclass, onCreate() function will be added to myClass automatically when cordova build the project.This class will be generated whenever cordova build the broject.
My problem is that my new code will be always disappear in that class when I run cordova build. I also reffer to this class in my create Notificatio function in GCMIntentService class.
Any idea about where I should add my new code to get access to urls?
here is my code:
public class Myclass extends CordovaActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.init();
super.loadUrl(Config.getStartUrl());
}
// here is my new code that intend to loud a specific page
#Override
public void onResume() {
super.onResume();
String url = Config.getStartUrl();
Intent intent = getIntent();
if (intent != null) {
String s = intent.getStringExtra("HTML_PAGE");
if (s != null) url = s;
}
super.loadUrl(url);
}
// here is my new code
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
}

Android - Call method from main class within JavaScriptInterface

I'm relatively new to android development and I'm trying to make a WebView that will allow me to launch the android camera app. How can I go about calling a method in the main class from with my JavaScriptInterface?
Thanks.
public class MainActivity extends Activity {
public static final int MEDIA_TYPE_IMAGE = 1888;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView mainWebView = (WebView) findViewById(R.id.mainWebView);
mainWebView.addJavascriptInterface(new JavaScriptInterface(this), "Android");
WebSettings webSettings = mainWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mainWebView.setWebViewClient(new MyCustomWebViewClient());
//mainWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
mainWebView.loadUrl("file:///mnt/sdcard/page.html");
}
private class MyCustomWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
public void takePicture() {
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, MEDIA_TYPE_IMAGE);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == MEDIA_TYPE_IMAGE) {
Bitmap photo = (Bitmap) data.getExtras().get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
photo.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
}}
}
package com.cargowise.view;
import android.content.Context;
import android.os.Handler;
import android.widget.Toast;
public class JavaScriptInterface {
Context mContext;
/** Instantiate the interface and set the context */
JavaScriptInterface(Context c) {
mContext = c;
}
public void takePic() {
MainActivity.takePicture();
}
public void showToast(String toast) {
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
}
You have stored the context which you should be able to use to access your MainActivity.
Change this:
public void takePic() {
MainActivity.takePicture();
}
To this:
public void takePic() {
((MainActivity)mContext).takePicture();
}
Note: you might want to add some type-checking or limit the type of the context that is given to a MainActivity to enforce correct behavior.
The suggestion of calling ((MainActivity)mContext).takePicture() is (probably) wrong, at least for me it failed. The reason is that the call will be not on the main/UI thread, but rather on some another thread.
Also, you MUST put the #JavascriptInterface annotation before each interface method.
The right way to place calls to main UI thread, which in turn will be able to do everything as usual on that thread, is this:
...inside JavaScriptInterface class
public void takePic() {
((MainActivity)mContext).post(new Runnable() {
#Override
public void run() {
((MainActivity)mContext).takePicture();
}
});
}
In Kotlin it would look much simpler:
#JavascriptInterface
fun takePic() {
(mContext as MainActivity).mainWebView?.post {
(mContext as MainActivity).takePicture()
}
}
Full example of integration of WebView with Android backend, with calls bak and forth between JS and Android, is here: https://github.com/latitov/Android_WebViewApp_FullScreen

Categories

Resources