Android apps throw out VM Aborting inside AsyncTask - android

I am writing Android apps using Eclipse + SDK 8, running at Android 2.2 emulator.
I create an AsyncTask to process lengthy task in background. Below is the code:
public class taskSavingFavourite extends AsyncTask<Void, Void, Integer>
{
#Override
protected Integer doInBackground(Void... arg0)
{
List<MainListClass> mList = new ArrayList<MainListClass>();
String s = null;
int addedCount= 0;
if (mainListSubTabSelectedTab == 0) mList = buildingList;
else mList = storeList;
if (mList == null) return 0;
try
{
for (int i = 0; i < mList.size(); i ++)
{
if (mList.get(i).checked == 1)
{
s = htFavourite.get(mList.get(i).id+"-"+mList.get(i).type);
if (s == null)
{
addedCount ++;
if (!favouriteList.equals(""))
favouriteList = favouriteList + ";";
favouriteList = favouriteList +
mList.get(i).id + "-" + mList.get(i).type + ":" +
mList.get(i).name;
htFavourite.put(mList.get(i).id + "-" + mList.get(i).type,
mList.get(i).name);
mList.get(i).favourite = 1;
}
}
publishProgress();
}
if (addedCount != 0)
{
SharedPreferences settings = getSharedPreferences("MGS_PRIVATE_DATA", 0);
SharedPreferences.Editor editor = settings.edit();
editor.putString("favouriteList", favouriteList);
//Commit the edits!
editor.commit();
}
}
catch (Exception e) {};
return addedCount;
}
}
The problem is: When the mList is large (1000+ items), current Activity will be force closed and back to launcher Activity. I quite confirm it is not crashed because the annoying "Unfortunately the ..." dialog is not coming out.
I check the logcat and when it happens, it always have this message:
12-10 14:10:28.772: D/dalvikvm(976): threadid=9: sending two SIGSTKFLTs to threadid=2 (tid=977) to cause debuggerd dump
12-10 14:10:30.842: D/dalvikvm(976): Sent, pausing to let debuggerd run
12-10 14:10:38.903: D/dalvikvm(976): Continuing 12-10 14:10:38.903: E/dalvikvm(976): **VM aborting***
I am not sure whether this is android bug or I am missing out something.

Alright, I think I found the reason: Never use tight loop inside AsyncTask:DoinBackground.
So I just put Thread.Sleep(1) inside the loop then everything works like a charm. Maybe this will just happen in emulator, not a real device (I just guess).

Related

android.renderscript.RSRuntimeException Fatal error 4097

I'm getting the following crash from renderscript on android:
100% of the crashes are on android 11 and 96% of the crashes are on Samsung devices. I removed all instances of renderscript, but this crash is still happening so maybe it's in one of my dependencies.
I found this got android.renderscript.RSRuntimeException on note4 which says that 4097 means a fatal driver error, but doesn't give any details on how to fix it.
Does anyone know how I can fix this crash?
UPDATE: when I search for "renderscript" in my app nothing comes up unless I put it on scope and then there's a bunch of references to it. I don't understand where they're coming from though
UPDATE: To be clear, I have removed all references to renderscript from my app but it seems like one or multiple of my dependencies are still using it. I need help isolating these dependencies. Something called android-30 is using renderscript. Is this the api 30 library or something? And then something called support/v8 is using renderscript. Is this the support library? (I do have support library enabled)
Migrate from RenderScript
RenderScript APIs are deprecated starting in Android 12. They will
continue to function, but we expect that device and component
manufacturers will stop providing hardware acceleration support over
time.
It seems to be a vendors issue (Driver issue)
Fatal error 4097: RS_ERROR_FATAL_UNKNOWN = 0x1000
static class MessageThread extends Thread {
RenderScript mRS;
boolean mRun = true;
int[] mAuxData = new int[2];
static final int RS_MESSAGE_TO_CLIENT_NONE = 0;
static final int RS_MESSAGE_TO_CLIENT_EXCEPTION = 1;
static final int RS_MESSAGE_TO_CLIENT_RESIZE = 2;
static final int RS_MESSAGE_TO_CLIENT_ERROR = 3;
static final int RS_MESSAGE_TO_CLIENT_USER = 4;
static final int RS_MESSAGE_TO_CLIENT_NEW_BUFFER = 5;
static final int RS_ERROR_FATAL_DEBUG = 0x0800;
static final int RS_ERROR_FATAL_UNKNOWN = 0x1000;
MessageThread(RenderScript rs) {
super("RSMessageThread");
mRS = rs;
}
public void run() {
// This function is a temporary solution. The final solution will
// used typed allocations where the message id is the type indicator.
int[] rbuf = new int[16];
mRS.nContextInitToClient(mRS.mContext);
while(mRun) {
rbuf[0] = 0;
int msg = mRS.nContextPeekMessage(mRS.mContext, mAuxData);
int size = mAuxData[1];
int subID = mAuxData[0];
if (msg == RS_MESSAGE_TO_CLIENT_USER) {
if ((size>>2) >= rbuf.length) {
rbuf = new int[(size + 3) >> 2];
}
if (mRS.nContextGetUserMessage(mRS.mContext, rbuf) !=
RS_MESSAGE_TO_CLIENT_USER) {
throw new RSDriverException("Error processing message from RenderScript.");
}
if(mRS.mMessageCallback != null) {
mRS.mMessageCallback.mData = rbuf;
mRS.mMessageCallback.mID = subID;
mRS.mMessageCallback.mLength = size;
mRS.mMessageCallback.run();
} else {
throw new RSInvalidStateException("Received a message from the script with no message handler installed.");
}
continue;
}
if (msg == RS_MESSAGE_TO_CLIENT_ERROR) {
String e = mRS.nContextGetErrorMessage(mRS.mContext);
// Throw RSRuntimeException under the following conditions:
//
// 1) It is an unknown fatal error.
// 2) It is a debug fatal error, and we are not in a
// debug context.
// 3) It is a debug fatal error, and we do not have an
// error callback.
if (subID >= RS_ERROR_FATAL_UNKNOWN ||
(subID >= RS_ERROR_FATAL_DEBUG &&
(mRS.mContextType != ContextType.DEBUG ||
mRS.mErrorCallback == null))) {
throw new RSRuntimeException("Fatal error " + subID + ", details: " + e);
}
if(mRS.mErrorCallback != null) {
mRS.mErrorCallback.mErrorMessage = e;
mRS.mErrorCallback.mErrorNum = subID;
mRS.mErrorCallback.run();
} else {
android.util.Log.e(LOG_TAG, "non fatal RS error, " + e);
// Do not throw here. In these cases, we do not have
// a fatal error.
}
continue;
}
if (msg == RS_MESSAGE_TO_CLIENT_NEW_BUFFER) {
if (mRS.nContextGetUserMessage(mRS.mContext, rbuf) !=
RS_MESSAGE_TO_CLIENT_NEW_BUFFER) {
throw new RSDriverException("Error processing message from RenderScript.");
}
long bufferID = ((long)rbuf[1] << 32L) + ((long)rbuf[0] & 0xffffffffL);
Allocation.sendBufferNotification(bufferID);
continue;
}
// 2: teardown.
// But we want to avoid starving other threads during
// teardown by yielding until the next line in the destructor
// can execute to set mRun = false
try {
sleep(1, 0);
} catch(InterruptedException e) {
}
}
//Log.d(LOG_TAG, "MessageThread exiting.");
}
}

PJSUA2 Android - Incoming calls drop after 32 seconds

I'm building a PJSUA2 (PJSIP 2.8) Android app and I have some issues: i.e. only on incoming call, call state remains in "PJSIP_INV_STATE_CONNECTING" and after 32 seconds the call drops.
I'm looking for the cause of the issue since several days, I googled a lot and all what I found is: in most situations this issue is related to NAT management or network issues related to NAT. In a few words: in most cases the called party does not receive the ACK after answering the call.
Finally I was able to log all SIP messages between my app and the SIP server and found that my app receives the ACK from the server, so I suppose it's not a network related issue.
I compiled PJSIP 2.8 with OpenSSL and SRTP support, but without video support (I don't need it at least at the moment). If it makes any difference, the app has a target version 28 and minimum SDK version 19.
I tried several apps on the market and they work fine enough with and without SRTP and with all signaling transports (UDP, TCP, TLS), WebRTC works fine too (tested with SipML5), so I would exclude a server misconfiguration. My app does the same (except SRTP with which I have some issues at the moment).
I tried with a SIP provider too (MessageNet) using UDP and the behaviour is always the same. I tried to use compact SIP messages and it behaves the same, with and without uri parameters, with and without STUN and or ICE and nothing changes. Mobile network and WiFi networks give the same results.
I tried to debug inside PJSIP library too, but without any success, then I tried to follow the code, to understand what I was doing wrong, but it doesn't seem to me there is something evidently wrong.
The following is the code (last version) which initializes PJSIP:
public class SipService extends Service {
private Looper serviceLooper;
private ServiceHandler serviceHandler;
private final Messenger mMessenger = new Messenger(new IncomingHandler());
private LocalBroadcastManager localBroadcast;
private LifecycleBroadcastReceiver lifecycleBroadcastReceiver;
private boolean lastCheckConnected;
private Endpoint endpoint;
private LogWriter logWriter;
private EpConfig epConfig;
private final List<ManagedSipAccount> accounts = new ArrayList<>();
private final Map<String, Messenger> eventRegistrations = new HashMap<>();
#TargetApi(Build.VERSION_CODES.N)
#Override
public void onCreate() {
super.onCreate();
String userAgent = "MyApp";
try {
PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
String appLabel = (pInfo.applicationInfo.labelRes == 0 ? pInfo.applicationInfo.nonLocalizedLabel.toString() : getString(pInfo.applicationInfo.labelRes));
userAgent = appLabel + "/" + pInfo.versionName;
} catch (PackageManager.NameNotFoundException e) {
Log.e("SipService", "Unable to get app version", e);
}
try {
endpoint = new MyAppEndpoint();
endpoint.libCreate();
epConfig = new EpConfig();
// Logging
logWriter = new PJSIPToAndroidLogWriter();
epConfig.getLogConfig().setWriter(logWriter);
epConfig.getLogConfig().setLevel(5);
// UA
epConfig.getUaConfig().setMaxCalls(4);
epConfig.getUaConfig().setUserAgent(userAgent);
// STUN
StringVector stunServer = new StringVector();
stunServer.add("stun.pjsip.org");
epConfig.getUaConfig().setStunServer(stunServer);
// General Media
epConfig.getMedConfig().setSndClockRate(16000);
endpoint.libInit(epConfig);
// UDP transport
TransportConfig udpCfg = new TransportConfig();
udpCfg.setQosType(pj_qos_type.PJ_QOS_TYPE_VOICE);
endpoint.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_UDP, udpCfg);
// TCP transport
TransportConfig tcpCfg = new TransportConfig();
//tcpCfg.setPort(5060);
endpoint.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_TCP, tcpCfg);
// TLS transport
TransportConfig tlsCfg = new TransportConfig();
endpoint.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_TLS, tlsCfg);
endpoint.libStart();
} catch (Exception e) {
throw new RuntimeException("Unable to initialize and start PJSIP", e);
}
ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
lastCheckConnected = activeNetwork != null && activeNetwork.isConnected();
updateForegroundNotification();
startForeground(MyAppConstants.N_FOREGROUND_NOTIFICATION_ID, buildForegroundNotification());
localBroadcast = LocalBroadcastManager.getInstance(this);
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
serviceLooper = thread.getLooper();
serviceHandler = new ServiceHandler(serviceLooper);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// Register LifeCycleBroadcastReceiver to receive network change notification
// It seems it's mandatory to do it programmatically since Android N (24)
lifecycleBroadcastReceiver = new LifecycleBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
registerReceiver(lifecycleBroadcastReceiver, intentFilter);
}
// Initialization
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (prefs != null) {
try {
CodecInfoVector codecs = endpoint.codecEnum();
SharedPreferences.Editor editor = prefs.edit();
for (int i = 0; i < codecs.size(); i++) {
CodecInfo codec = codecs.get(i);
int priority = prefs.getInt("codecs.audio{" + codec.getCodecId() + "}", 0);
try {
endpoint.codecSetPriority(codec.getCodecId(), (short) priority);
codec.setPriority((short) priority);
} catch (Exception e) {
Log.e("SipService", "Unexpected error setting codec priority for codec " + codec.getCodecId(), e);
}
}
} catch (Exception e) {
Log.e("SipService", "Unexpected error loading codecs priorities", e);
}
}
}
#Override
public void onDestroy() {
for (Account acc : accounts) {
acc.delete();
}
accounts.clear();
try {
endpoint.libDestroy();
} catch (Exception e) {
e.printStackTrace();
}
endpoint.delete();
endpoint = null;
epConfig = null;
if (lifecycleBroadcastReceiver != null) {
unregisterReceiver(lifecycleBroadcastReceiver);
}
super.onDestroy();
}
.......
}
And the following is my Account class with creation and registration code:
public class ManagedSipAccount extends Account {
public final String TAG;
private final VoipAccount account;
private final PhoneAccountHandle handle;
private final SipService service;
private final AccountStatus status;
private final Map<Integer, VoipCall> calls = new HashMap<>();
private final Map<String, VoipBuddy> buddies = new HashMap<>();
private AccountConfig acfg;
private List<SrtpCrypto> srtpCryptos = new ArrayList<>();
private AuthCredInfo authCredInfo;
public ManagedSipAccount(SipService service, VoipAccount account, PhoneAccountHandle handle) {
super();
TAG = "ManagedSipAccount/" + account.getId();
this.service = service;
this.account = account;
this.handle = handle;
this.status = new AccountStatus(account.getUserName() + "#" + account.getHost());
acfg = new AccountConfig();
}
public void register(Map<String, String> contactParameters) throws Exception {
StringBuilder contactBuilder = new StringBuilder();
for (Map.Entry<String, String> entry : contactParameters.entrySet()) {
contactBuilder.append(';');
contactBuilder.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
contactBuilder.append("=\"");
contactBuilder.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
contactBuilder.append("\"");
}
StringBuilder logBuilder = new StringBuilder();
logBuilder.append("Registering: ");
logBuilder.append(account.getProtocol().name());
/*logBuilder.append('(');
logBuilder.append(service.getTransport(account.getProtocol()));
logBuilder.append(')');*/
if (account.isEncryptionSRTP()) {
logBuilder.append(" SRTP");
}
if (account.isIce()) {
logBuilder.append(" ICE");
}
Log.d(TAG, logBuilder.toString());
String idUri = "sip:" + account.getUserName();
if (!"*".equals(account.getRealm())) {
idUri += "#" + account.getRealm();
}
else {
idUri += "#127.0.0.1" /*+ account.getHost()*/;
}
acfg.setIdUri(idUri);
acfg.getRegConfig().setRegistrarUri("sip:" + account.getHost() + ":" + account.getPort() + ";transport=" + account.getProtocol().name().toLowerCase());
acfg.getRegConfig().setRetryIntervalSec(account.getRetryInterval());
acfg.getRegConfig().setRegisterOnAdd(false);
acfg.getSipConfig().setContactUriParams(contactBuilder.toString());
// NAT management
acfg.getNatConfig().setSipStunUse(pjsua_stun_use.PJSUA_STUN_USE_DEFAULT);
if (account.isIce()) {
acfg.getNatConfig().setIceEnabled(true);
acfg.getNatConfig().setIceAlwaysUpdate(true);
acfg.getNatConfig().setIceAggressiveNomination(true);
}
else {
acfg.getNatConfig().setSdpNatRewriteUse(1);
}
acfg.getMediaConfig().getTransportConfig().setQosType(pj_qos_type.PJ_QOS_TYPE_VOICE);
if (account.isEncryptionSRTP()) {
acfg.getMediaConfig().setSrtpUse(pjmedia_srtp_use.PJMEDIA_SRTP_MANDATORY);
acfg.getMediaConfig().setSrtpSecureSignaling(0);
//acfg.getMediaConfig().getSrtpOpt().setKeyings(new IntVector(2));
acfg.getMediaConfig().getSrtpOpt().getKeyings().clear();
acfg.getMediaConfig().getSrtpOpt().getKeyings().add(pjmedia_srtp_keying_method.PJMEDIA_SRTP_KEYING_SDES.swigValue());
acfg.getMediaConfig().getSrtpOpt().getKeyings().add(pjmedia_srtp_keying_method.PJMEDIA_SRTP_KEYING_DTLS_SRTP.swigValue());
acfg.getMediaConfig().getSrtpOpt().getCryptos().clear();
StringVector cryptos = Endpoint.instance().srtpCryptoEnum();
for (int i = 0; i < cryptos.size(); i++) {
SrtpCrypto crypto = new SrtpCrypto();
crypto.setName(cryptos.get(i));
crypto.setFlags(0);
srtpCryptos.add(crypto);
acfg.getMediaConfig().getSrtpOpt().getCryptos().add(crypto);
}
}
else {
acfg.getMediaConfig().setSrtpUse(pjmedia_srtp_use.PJMEDIA_SRTP_DISABLED);
acfg.getMediaConfig().setSrtpSecureSignaling(0);
}
authCredInfo = new AuthCredInfo("digest",
account.getRealm(),
account.getAuthenticationId() != null && account.getAuthenticationId().trim().length() > 0 ? account.getAuthenticationId() : account.getUserName(),
0,
account.getPassword());
acfg.getSipConfig().getAuthCreds().add( authCredInfo );
acfg.getIpChangeConfig().setHangupCalls(false);
acfg.getIpChangeConfig().setShutdownTp(true);
create(acfg);
ConnectivityManager cm = (ConnectivityManager)service.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null && activeNetwork.isConnected();
if (isConnected) {
setRegistration(true);
}
}
#Override
public void onRegStarted(OnRegStartedParam prm) {
super.onRegStarted(prm);
Log.d(TAG, "Status: Registering...");
status.setStatus(AccountStatus.Status.REGISTERING);
service.updateStatus(this);
}
#Override
public void onRegState(OnRegStateParam prm) {
super.onRegState(prm);
try {
Log.d(TAG, "Registration state: " + prm.getCode().swigValue() + " " + prm.getReason());
AccountInfo ai = getInfo();
status.setStatus(ai.getRegIsActive() ? AccountStatus.Status.REGISTERED : AccountStatus.Status.UNREGISTERED);
Log.d(TAG, "Status: " + status.getStatus().name() + " " + super.getInfo().getUri());
service.updateStatus(this);
} catch (Exception e) {
e.printStackTrace();
}
}
.....
}
Finally, how I answer the code at the moment in a class which extends the PJSIP's Call class:
#Override
public void answerCall() {
Log.d(TAG, "Answering call...");
CallOpParam prm = new CallOpParam(true);
prm.setStatusCode(pjsip_status_code.PJSIP_SC_OK);
prm.getOpt().setAudioCount(1);
prm.getOpt().setVideoCount(0);
try {
this.answer(prm);
} catch (Exception e) {
e.printStackTrace();
}
}
I also tried with new CallOpParam(); with just the status code and nothing else, but nothing changes.
One note: I created the IdUri as sip:username#127.0.0.1 because without the host the resulting contact was and I thought that the missing user part may be the cause of the issue or part of it.
The following is the trace of the app <-> my Asterisk server communication during call (linked because of content length exceed).
https://gist.github.com/ivano85/a212ddc9a808f3cd991234725c2bdb45
The ServerIp is an internet public IP, while the MyIp[5.XXX.XXX.XXX] is my phone's public IP.
As you can see from the log, my app sends a 100 Trying, then a 180 Ringing when the phone rings, then the user answers and the app sends a 200 OK. The server replies with a ACK message (I would say it's not a NAT issue, because PJSIP receives the ACK). I see the same from Asterisk.
After this I would expect the call goes from PJSIP_INV_STATE_CONNECTING to PJSIP_INV_STATE_CONFIRMED, but it does not happen, so PJSIP continues to send a 200 OK and receive the ACK every about 2 seconds, until the call times out after 32 seconds and PJSIP disconnects the call (sending a BYE).
I'm starting to think that PJSIP just ignores ACK messages and just has a wrong behaviour. Please help me to understand what is happening here. I would appreciate it so much!
Obviously let me know if you think that more details are needed.

Google playstore app version check not working any more

For force update I used to call the app url and check the for the html tag value <softwareVersion><softwareVersion/> but suddenly it stopped working there is no softwareVersion tag in the page so getting null. Is there any google api available to check the google play app version.
Update -
I investigate in more details when I used to call the url the response was 200 but now I am getting 405
I come up with a solution. When ever I am pushing a new version in the playstore I will add the version in the what's new, like this -
WHAT'S NEW
Version - 2.11.0
- New changes 1
- New changes 2
And I look for this Version -
So Full code looks like this -
class VersionCheckTask extends AsyncTask<String, Void, String> {
protected String doInBackground(String... urls) {
String mVer = "";
String mData = "";
try {
URL mUrl = new URL(urls[0]);
BufferedReader in = new BufferedReader(new InputStreamReader(mUrl.openStream()));
String inputLine;
while ((inputLine = in.readLine()) != null){
if (inputLine == null)
break;
mData += inputLine;
}
in.close();
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
/*
* we are looking for this tag <div itemprop="description"><content>Version - 2.11.0<br>
* We need to make sure every time we release a new version we should add the line in what's new -
*
* Version - 2.11.1
*
* - New changes 1
* - New changes 2
*/
String startToken = "Version - ";
String endToken = "<";
int index = mData.indexOf(startToken);
if (index == -1) {
mVer = null;
} else {
mVer = mData.substring(index + startToken.length(), index
+ startToken.length() + 100);
mVer = mVer.substring(0, mVer.indexOf(endToken)).trim();
}
return mVer;
}
protected void onPostExecute(String store_version) {
String currentVersion = "";
try {
PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
currentVersion = pInfo.versionName;
} catch (PackageManager.NameNotFoundException e) {
Log.v(TAG, "Recv NameNotFoundException. Msg:" + e.getMessage());
}
Log.d(TAG, "store_version: " + store_version);
Log.d(TAG, "device_version: " + currentVersion);
if (store_version != null) {
if (versionCompare(store_version, currentVersion) > 0) {
dialog.setMessage(String.format(getResources().getString(R.string.update_message), getResources().getString(R.string.app_name), store_version));
dialog.show();
} else {
showDisclaimer();
}
}
}
}
public int versionCompare(String storeVersion, String currentVersion) {
String[] vals1 = storeVersion.split("\\.");
String[] vals2 = currentVersion.split("\\.");
int i = 0;
// set index to first non-equal ordinal or length of shortest version string
while (i < vals1.length && i < vals2.length && vals1[i].equals(vals2[i])) {
i++;
}
// compare first non-equal ordinal number
if (i < vals1.length && i < vals2.length) {
int diff = Integer.valueOf(vals1[i]).compareTo(Integer.valueOf(vals2[i]));
return Integer.signum(diff);
}
// the strings are equal or one string is a substring of the other
// e.g. "1.2.3" = "1.2.3" or "1.2.3" < "1.2.3.4"
return Integer.signum(vals1.length - vals2.length);
}
Maybe, at the end of march, Google changed Play store's HTML Code.
The structure of additional information has also changed.
Some developers, including me, use Jsoup to check for the latest version in the Play Store.
Perhaps you were using code like this:
Document doc = Jsoup.connect
("https://play.google.com/store/apps/details?id=name.package.your").get();
Elements Version = doc.select(".content");
for (Element v : Version) {
if (v.attr("itemprop").equals("softwareVersion")) {
VersionMarket = v.text();
}
}
but, after play store's change, your code return null.
because, "itemprop" and "sofrwareVersion" is gone, like that.
enter image description here
So, you need a new way to parse the version of your app in Google Play store's ADDITION INFORMATION with Jsoup.
try {
Document doc = Jsoup
.connect(
"https://play.google.com/store/apps/details?id=name.package.your")
.get();
Elements Version = doc.select(".htlgb ");
for (int i = 0; i < 5 ; i++) {
VersionMarket = Version.get(i).text();
if (Pattern.matches("^[0-9]{1}.[0-9]{1}.[0-9]{1}$", VersionMarket)) {
break;
}
}
The above code works as follows.
Parsing play store's your app page.
Selecting all "htlgb" contents.
like in image, "3 March 2018", "1,000+" "2.0.4", "4.4 and up, etc."
In [for Loop], [Regex] finds a value matching your version pattern (like 2.0.4) and stops.
VersionMarket is your "app version" and you can use it.
//2018-08-04 Add Comment
For some reason, the code above returns information about "Installs" instead of "version information".
Therefore, if you modify the code as shown below, you can return "version information" again.
try {
Document doc = Jsoup
.connect(
"https://play.google.com/store/apps/details?id=name.package.your")
.get();
Elements Version = doc.select(".htlgb ");
for (int i = 0; i < 10 ; i++) {
VersionMarket = Version.get(i).text();
if (Pattern.matches("^[0-9]{1}.[0-9]{1}.[0-9]{1}$", VersionMarket)) {
break;
}
}
The above code changed the number in "for break" from 5 to 10.
Because the number of "htlgb" codes has changed in Google Play Store's HTML Code.
No, no API exists for checking your app version on Play. Instead, you could implement a solution using Firebase Remote Config, then you have much more control over the minimum version your users see.

Read content of another app through my app

I have been searching the same problem for days. But unable to get any hint for that.
I need to create an app like voodoo app, which shows its custom layout only on specific pages of different apps like flipkart,etc.
Now, till this time, i have found options of using AccessebilityService and MediaProjection classes for the same. But i am stuck, how can i know programmatically, that Flipkart's Product Detail Page is visible so that i can display my app's custom view over it like Voodoo app does.
Any suggestions?
What you want to do is the following.
Using accessibility services track incoming events. Then you want to track TYPE_WINDOW_CONTENT_CHANGED events, and detect when the window content matches what you'd expect.
#Override
public void onAccessibilityEvent(AccessibilityEvent e) {
switch (e.getEventType()) {
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
if (isFlipkartProdcutDetailPage(getRootInActiveWindow()) {
doStuff()
}
}
}
}
public boolean isFlipkartProductDetailPage(AccessibilityNodeInfo nodeInfo) {
//Use the node info tree to identify the proper content.
//For now we'll just log it to logcat.
Log.w("TAG", toStringHierarchy(nodeInfo, 0));
}
private String toStringHierarchy(AccessibilityNodeInfo info, int depth) {
if (info == null) return "";
String result = "|";
for (int i = 0; i < depth; i++) {
result += " ";
}
result += info.toString();
for (int i = 0; i < info.getChildCount(); i++) {
result += "\n" + toStringHierarchy(info.getChild(i), depth + 1);
}
return result;
}

is it safe to use ThreadPoolExecutor inside AsyncTask doInBackground

I have to download a Json with a list of files, and then parallel download the files in the list. I would like to update periodically the ProgressDialog, so I implemented in this way
I create and show the dialog
I start an AsyncTask
onProgressUpdate receives 2 Integers, current progress and max progress, and updates the progress bar
doInBackground
downloads the json file and obtains the list of files to download
creates a ThreadPoolExecutor (tpe), with a LinkedBlockingQueue<Runnable>
submit a runnable for each file, that download the file to disk using Apache commons-io FileUtils.copyURLToFile
exec shutdown
in a while cycle. tpe.awaitTermination(1, TimeUnit.SECONDS) invokes periodically publishProgress( (int) tpe.getCompletedTaskCount(), tot), to update the progress bar
onPostExecute hides and dismisses the progres bar, and manages the files downloades
is there any problem in using ThreadPoolExecutor inside an AsynTask?
I am discussing with a colleague who claims that there could be problems in the threads management, that could deadlock, and that might give us problems on future versions
that's the code
public static void syncFiles(...)
{
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
sWakelock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
sWakelock.acquire();
sProgress = new ProgressDialog(context);
sProgress.setCancelable(false);
sProgress.setTitle("MyTitle");
sProgress.setMessage("Sincronizzazione in corso");
sProgress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
sProgress.setIndeterminate(false);
sProgress.show();
sCurrentTask = new AsyncTask<Void, Integer, Manifest>()
{
#Override
protected void onCancelled()
{
if ((sProgress != null) && sProgress.isShowing())
sProgress.dismiss();
if ((sWakelock != null) && sWakelock.isHeld())
sWakelock.release();
};
#Override
protected Manifest doInBackground(Void... params)
{
ArrayList files = getFiles(....)// download the jsonfile, and return the list of files
final String baseurl = ... // get the remote base url
final String baselocal = ... //get the local base path ;
int tot = m.size();
publishProgress(0, tot);
final int MAX_THREADS = Runtime.getRuntime().availableProcessors(); * 4;
ThreadPoolExecutor tpe = new ThreadPoolExecutor(
MAX_THREADS,
MAX_THREADS,
1,
TimeUnit.MINUTES,
new LinkedBlockingQueue<Runnable>()
);
for (final String s: files)
{
tpe.submit(new Runnable()
{
#Override
public void run()
{
try
{
URL remoteUrl = new URL(baseurl + s);
File localUrl = new File(baselocal, s);
FileUtils.copyURLToFile(remoteUrl, localUrl, 60000, 60000);
Log.w(TAG, "Downloaded " + localUrl.getAbsolutePath() + " in " + remoteUrl);
} catch (Exception e)
{
e.printStackTrace();
Log.e(TAG, "download error " + e);
// error management logic
}
}
});
}
tpe.shutdown();
int num = 0;
publishProgress(num, tot);
try
{
while (!tpe.awaitTermination(1, TimeUnit.SECONDS))
{
int n = (int) tpe.getCompletedTaskCount();
Log.w(TAG, "COUTN: " + n + "/" + tot);
if (n != num)
{
num = n;
publishProgress(num, tot);
}
}
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return m;
}
protected void onProgressUpdate(Integer... prog)
{
if (sProgress.getMax() != prog[1]) {
sProgress.setMax(prog[1]);
}
sProgress.setProgress(prog[0]);
}
#Override
protected void onPostExecute(Manifest result)
{
sWakelock.release();
sProgress.hide();
sProgress.dismiss();
// manage results
}
}.execute();
}
If you'll checkout the implementation of AsyncTask then youi can find that AsyncTask itself has ThreadPool so it will start the task on separate thread. Acutually when we can the .execute() to start the background task this method is typically used with THREAD_POOL_EXECUTOR to allow multiple tasks to run in parallel on a pool of threads managed by AsyncTask. So why you need to implement another.
Update
Read about executeOnExecutor in this may be this can help you... It clearly says that if you are allowing multiple tasks to run in parallel from a thread pool is generally not what one wants, because the order of their operation is not defined....but here you want to download the files so I don't think the order is important so in my view you can use it and it'll not create any issue.

Categories

Resources