I'm pretty much new to android coding, so I'm sorry if I make some mistakes while asking the question.
I'm trying to implement in-app updates for a personal use application and not to be put up on the play store. I've searched my way through stack and found various methods through which that can be achieved. So I tried my own. But the thing is, it doesn't work sometimes, and sometimes it shows wrong information about the update being available. To be more specific, when I compare the curVersionCode to newVersionCode, where curVersionCode = newVersionCode, it still shows that the newer version is available.
Here's My Activity -
public class Test extends Activity {
private Handler mHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.front);
mHandler = new Handler();
checkUpdate.start();
}
}
/* This Thread checks for Updates in the Background */
private Thread checkUpdate = new Thread() {
public void run() {
try {
URL updateURL = new URL("URL TO THE TXT FILE");
BufferedReader in = new BufferedReader(new InputStreamReader(updateURL.openStream()));
String str;
while ((str = in.readLine()) != null) {
// str is one line of text; readLine() strips the newline character(s)
/* Get current Version Number */
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
int curVersion = packageInfo.versionCode;
int newVersion = Integer.valueOf(str);
/* Is a higher version than the current already out? */
if (newVersion > curVersion) {
/* Post a Handler for the UI to pick up and open the Dialog */
mHandler.post(showUpdate);
}
}
in.close();
} catch (Exception e) {
}
}
};
/* This Runnable creates a Dialog and asks the user to open the Update Link*/
private Runnable showUpdate = new Runnable(){
public void run(){
new AlertDialog.Builder(BrowserActivity.this)
.setIcon(R.drawable.ic_launcher)
.setTitle("Update Available")
.setMessage("An update for is available!\n\nOpen Update page and see the details?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked OK so do some stuff */
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("URL TO THE UPDATE FILE"));
startActivity(intent);
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked Cancel */
}
})
.show();
}
};
Been searching for a few days, still ain't able to find the answer. I'm sorry if this is a basic question but I'm pretty new to it.
Thanks.
EDIT : The code now works just fine. Turns out it requires some time to refresh the version code over the internet text file. Doesn't matter, got experience.
Related
I am facing one challenge in our application that, our customer groups are always keeping "Auto update" of App features off. so they are not even getting updates of my app on their device although it is on google play. I want to check programmatically that if any new version of my app is being uploaded on google play then user will get alerts like "new version is on google play, update it now" and when user clicks on "Ok" then new version will be installed on the device.
it would be good if you share the code snippet for this.
You can check manually for updates and notify user. Try the following:
public class Test extends Activity {
private Handler mHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.front);
mHandler = new Handler();
/* Get Last Update Time from Preferences */
SharedPreferences prefs = getPreferences(0);
lastUpdateTime = prefs.getLong("lastUpdateTime", 0);
/* Should Activity Check for Updates Now? */
if ((lastUpdateTime + (24 * 60 * 60 * 1000)) < System.currentTimeMillis()) {
/* Save current timestamp for next Check*/
lastUpdateTime = System.currentTimeMillis();
SharedPreferences.Editor editor = getPreferences(0).edit();
editor.putLong("lastUpdateTime", lastUpdateTime);
editor.commit();
/* Start Update */
checkUpdate.start();
}
}
/* This Thread checks for Updates in the Background */
private Thread checkUpdate = new Thread() {
public void run() {
try {
URL updateURL = new URL("http://my.company.com/update");
URLConnection conn = updateURL.openConnection();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
ByteArrayBuffer baf = new ByteArrayBuffer(50);
int current = 0;
while((current = bis.read()) != -1){
baf.append((byte)current);
}
/* Convert the Bytes read to a String. */
final String s = new String(baf.toByteArray());
/* Get current Version Number */
int curVersion = getPackageManager().getPackageInfo("your.app.id", 0).versionCode;
int newVersion = Integer.valueOf(s);
/* Is a higher version than the current already out? */
if (newVersion > curVersion) {
/* Post a Handler for the UI to pick up and open the Dialog */
mHandler.post(showUpdate);
}
} catch (Exception e) {
}
}
};
/* This Runnable creates a Dialog and asks the user to open the Market */
private Runnable showUpdate = new Runnable(){
public void run(){
new AlertDialog.Builder(Test.this)
.setIcon(R.drawable.icon)
.setTitle("Update Available")
.setMessage("An update for is available!\\n\\nOpen Android Market and see the details?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked OK so do some stuff */
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:your.app.id"));
startActivity(intent);
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked Cancel */
}
})
.show();
}
};
}
Source
I have no clue whether am I doing a correct implementation of LVL.
Please guide me with this issue.
I followed some of the answers like clearing the cache, uninstalling and reinsatlling.
Still no luck..
I tried the following steps before uploading to alpha testing.
I am using Eclipse. I created a keystore using the export signed application package option
Uploaded the APK from the keystore.
Following is my code, which I took from How to license my Android application?
public class Activity_LicenseCheck extends Activity {
private class MyLicenseCheckerCallback implements LicenseCheckerCallback{
#Override
public void allow(int reason) {
toast("Inside-Allow:" + reason);
if (isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
startMainActivity();
}
#Override
public void dontAllow(int reason) {
toast("dontAllow: " + reason);
if (isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
}
#Override
public void applicationError(int errorCode) {
if (isFinishing()) {
return;
}
toast("Errorffff: " + errorCode);
startMainActivity();
}
}
private static final String BASE64_PUBLIC_KEY = "mykey";
private static final byte[] SALT = new byte[] {11,34,56,36,3,45,-87,2,67,-98,32,-14,44,-58,39,-26,72,-19,86,23};
private LicenseChecker mChecker;
// A handler on the UI thread.
private LicenseCheckerCallback mLicenseCheckerCallback;
private void doCheck() {
mChecker.checkAccess(mLicenseCheckerCallback);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Try to use more data here. ANDROID_ID is a single point of attack.
String deviceId = Secure.getString(getContentResolver(),
Secure.ANDROID_ID);
// Library calls this when it's done.
mLicenseCheckerCallback = new MyLicenseCheckerCallback();
// Construct the LicenseChecker with a policy.
mChecker = new LicenseChecker(this, new ServerManagedPolicy(this,
new AESObfuscator(SALT, getPackageName(), deviceId)),
BASE64_PUBLIC_KEY);
doCheck();
}
#Override
protected Dialog onCreateDialog(int id) {
// We have only one dialog.
return new AlertDialog.Builder(this)
.setTitle("Application Not Licensed")
.setCancelable(false)
.setMessage(
"This application is not licensed. Please purchase it from Android Market")
.setPositiveButton("Buy App",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog,
int which) {
Intent marketIntent = new Intent(
Intent.ACTION_VIEW,
Uri.parse("http://market.android.com/details?id="
+ getPackageName()));
startActivity(marketIntent);
finish();
}
})
.setNegativeButton("Exit",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog,
int which) {
finish();
}
}).create();
}
#Override
protected void onDestroy() {
super.onDestroy();
mChecker.onDestroy();
}
private void startMainActivity() {
startActivity(new Intent(this, Activity_login.class));
finish();
}
public void toast(String string) {
Toast.makeText(this, string, Toast.LENGTH_SHORT).show();
}
}
Finally it worked the issue was the wrong entry of BASE64 PUBLIC KEY.I was completely clueless about the licencing concept google should come up with easy solution.
How it worked for me..
My first publish was ver 1.0 and i was getting error 561.(Not licenced)
the isuue was wrong BASE64 PUBLIC KEY entry then i replaced it with the correct one and changed application version to 2.0 in Androidmanifest.xml and regenerated keystore and finally uploaded apk to the developer console and disabled version 1 and published version 2 in the console. when downloaded new apk from the console still facing issue the app was throwing "Error retrieving information from server [RPC:S-7:AEC-0]" error. i googled and found the solution, rebooted the device and it worked
I am having trouble implementing Licensing using the licensing lib. My code alway seems to go to the "check network access" part or the "don't allow" part. I am testing using the method mentioned in the Setting Up for Licensing article (http://developer.android.com/google/play/licensing/setting-up.html). I have the licencing bits of my code below, let me know if you need to see anything else.
Note: most of this code is from the sample app that comes with the library.
public class Reminder_list extends ListActivity {
Global variables ....
private static final String BASE64_PUBLIC_KEY = "bla,bla,bla";
// Generate your own 20 random bytes, and put them here.
private static final byte[] SALT = new byte[] {
-46, 65, 30, -118, -103, -57, 74, -14, 51, 88, -95, -45, 77, -17, -36, -113, -11, 32, -64,
19
};
private LicenseCheckerCallback mLicenseCheckerCallback;
private LicenseChecker mChecker;
// A handler on the UI thread.
private Handler mHandler;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler();
String deviceId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
// LicencingLibrary calls this when it's done.
mLicenseCheckerCallback = new MyLicenseCheckerCallback();
mChecker = new LicenseChecker(
this, new ServerManagedPolicy(this,
new AESObfuscator(SALT, getPackageName(), deviceId)),
BASE64_PUBLIC_KEY);
doCheck();
...
the rest of the stuff in the onCreate.
...
}
// this dialog is for the licence check.
protected Dialog onCreateDialog(int id) {
final boolean bRetry = id == 1;
return new AlertDialog.Builder(this)
.setTitle(R.string.unlicensed_dialog_title)
.setMessage(bRetry ? R.string.unlicensed_dialog_retry_body : R.string.unlicensed_dialog_body)
.setPositiveButton(bRetry ? R.string.retry_button : R.string.buy_button, new DialogInterface.OnClickListener() {
boolean mRetry = bRetry;
public void onClick(DialogInterface dialog, int which) {
if ( mRetry ) {
doCheck();
} else {
Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(
"market:///details?id=" + getPackageName()));
startActivity(marketIntent);
}
}
})
.setNegativeButton(R.string.quit_button, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
}).create();
}
// this is the actual method that called for the licence check.
private void doCheck() {
mChecker.checkAccess(mLicenseCheckerCallback);
}
// this is used to display the result of he licence check
private void displayResult(final String result) {
mHandler.post(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), result ,Toast.LENGTH_SHORT).show();
}
});
}
// this shows the dialog if the licence check came back with a problem. Either the user did not pay for it or we did an error.
private void displayDialog(final boolean showRetry) {
mHandler.post(new Runnable() {
#SuppressWarnings("deprecation")
public void run() {
showDialog(showRetry ? 1 : 0);//calls the dialog
}
});
}
...
...
the rest of the class
The LicenseCheckerCallback class in the main class:
// this is an internal class that should be inside the main activity that is used for license checking. The licensing lib call this when
// it is done with checking the license
private class MyLicenseCheckerCallback implements LicenseCheckerCallback {
public void allow(int policyReason) {
if (isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
// Should allow user access.
displayResult(getString(R.string.allow));
}
public void dontAllow(int policyReason) {
if (isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
displayResult(getString(R.string.dont_allow));
// Should not allow access. In most cases, the app should assume
// the user has access unless it encounters this. If it does,
// the app should inform the user of their unlicensed ways
// and then either shut down the app or limit the user to a
// restricted set of features.
// In this example, we show a dialog that takes the user to Market.
// If the reason for the lack of license is that the service is
// unavailable or there is another problem, we display a
// retry button on the dialog and a different message.
displayDialog(policyReason == Policy.RETRY);
}
public void applicationError(int errorCode) {
if (isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
// This is a polite way of saying the developer made a mistake
// while setting up or calling the license checker library.
// Please examine the error code and fix the error.
String result = String.format(getString(R.string.application_error), errorCode);
displayResult(result);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
mChecker.onDestroy();
}
}
I am using the ServerManagedPolicy . So I have not changed anything in the library files(I don't have to do I?). And I do have the in the manifest.
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
I cant work out what I am doing wrong. All of this is entirely based on the sample app.
My question is : how do I get the Licence check to work properly?
Note : If you plan on downvoting , pls tell me why.
Thanks for taking the time to read this and for any help that you can give.
Cheers.
I want to create a dialogBuilder with a text field and a button on it. The idea is to make the program wait for any further actions until the text in the field is entered and the OK button is clicked. Below is the code:
private static final Object wait = new int[0];
private static String result = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Handler h = new Handler();
final Context context = MainActivity.this;
h.post(new Runnable() {
public void run() {
final Builder dialogBuilder = new AlertDialog.Builder(context);
dialogBuilder.setTitle(R.string.app_name);
final LinearLayout panel = new LinearLayout(context);
panel.setOrientation(LinearLayout.VERTICAL);
final TextView label = new TextView(context);
label.setId(1);
label.setText(R.string.app_name);
panel.addView(label);
final EditText input = new EditText(context);
input.setId(2);
input.setSingleLine();
input.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_VARIATION_URI
| InputType.TYPE_TEXT_VARIATION_PHONETIC);
final ScrollView view = new ScrollView(context);
panel.addView(input);
view.addView(panel);
dialogBuilder
.setCancelable(true)
.setPositiveButton(R.string.app_name,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int id) {
result = input.getText().toString();
synchronized (wait) {
wait.notifyAll();
}
dialog.dismiss();
}
}).setView(view);
dialogBuilder.setOnCancelListener(new OnCancelListener() {
public void onCancel(DialogInterface arg0) {
result = null;
synchronized (wait) {
wait.notifyAll();
}
}
});
dialogBuilder.create().show();
}
});
String localResult = null;
try {
synchronized (wait) {
Log.d("Waiting", "Waiting " + localResult);
wait.wait();
}
localResult = result;
result = null;
if (localResult == null) {
// user is requesting cancel
throw new RuntimeException("Cancelled by user");
}
Log.d("RESULT ", "RESULT " + localResult);
} catch (InterruptedException e) {
localResult = result;
result = null;
if (localResult == null) {
// user is requesting cancel
Log.d("CANCELED ", "CANCELED " + localResult);
throw new RuntimeException("Cancelled by user");
}
}
Log.d("RESULT AFTER THE DIALOG", "RESULT AFTER THE DIALOG " + result);
}
The program is going to Log.d("Waiting", "Waiting " + localResult); and after that just waiting. NO DIALOG BUILDER IS SHOWN on the activity window. I used the debug mode and saw that the program flow is not entering the run() method, but the value of the Handler.post() is true. And for this reason the dialog is not shown, and the program is waiting.
I have tried to remove the moment with waiting (remove the Handler.post()), just to see if the dialog will show, and it showed and all moved well, but the result was not I am needing - I want the program to wait the input from the dialog ... I am really out of ideas.
Would you please give me some suggestions as I am really out of ideas.
Thanks a lot!
Handlers don't run in a separate thread. So when you call wait() :
synchronized (wait) {
Log.d("Waiting", "Waiting " + localResult);
wait.wait();
}
It waits indefinitely since the handler runs on the same thread as the current thread. Your Runnable can only be executed after the onCreate() method finishes but this will never happen because you just called wait().
You should reconsider your idea and find a workaround (for example, show the dialog the usual way and disable the "OK" button as long as the user does not enter a valid text). But calling wait() on the UI thread cannot go well.
You should be running the display of the Dialog in the UI Thread, not a seperate thread.
An example would be something like this:
In the onCreate()
runOnUiThread(new Runnable() {
#Override
public void run() {
// Display progress dialog when loading contacts
dialog = new ProgressDialog(this);
// continue with config of Dialog
}
});
// Execute the Asynchronus Task
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
// code to execute in background
return null;
}
#Override
protected void onPostExecute(Void result) {
// Dismiss the dialog after inBackground is done
if (dialog != null)
dialog.dismiss();
super.onPostExecute(result);
}
}.execute((Void[]) null);
Specifically what is happening here is the Dialog is being displayed on the UI thread and then the AsyncTask is executing in the background while the Dialog is running. Then at the end of the execution we dismiss the dialog.
I am trying to start an activity after n seconds with a handler. The application was crashing on the startActivity call, so I put the handler code in my application's onCreate, and it is still crashing (which makes me think that the error comes from me not using startActivity well) :
#Override
public void onCreate(){
String roomName = this.getSettingValue(R.string.PREFERENCES_ROOM_NAME, "");
Room room;
try {
room = this.getRoomWithName(roomName);
} catch (ReservatorException ex) {
Toast err = Toast.makeText(this, ex.getMessage(),
Toast.LENGTH_LONG);
err.show();
return;
}
Intent i = new Intent(this, RoomActivity.class);
i.putExtra("room", room);
this.startActivity(i);
}
Strange thing is that this work when called from a view, by using exactly the same code, but different context :
Intent i = new Intent(getContext(), RoomActivity.class);
// ...
I am pretty new to Android ... so there may be information missing in that question, or I might even be trying to do something completely stupid who knows ?
EDIT
Link to the stacktrace : http://pastebin.com/vh2QC3xz
EDIT2
Here is the handler version of my code (so what I am trying to do in the end) :
public class ReservatorApplication extends Application {
private GoToFavouriteRoom goToFavouriteRoomRunable;
class GoToFavouriteRoom implements Runnable {
ReservatorApplication app;
public GoToFavouriteRoom(ReservatorApplication anApp){
app = anApp;
}
#Override
public void run() {
String roomName = app.getSettingValue(R.string.PREFERENCES_ROOM_NAME, "");
Room room;
try {
room = app.getDataProxy().getRoomWithName(roomName);
} catch (ReservatorException ex) {
Toast err = Toast.makeText(app, ex.getMessage(),
Toast.LENGTH_LONG);
err.show();
return;
}
RoomActivity.startWith(app, room);
}
}
private final ReservatorAppHandler handler = new ReservatorAppHandler();
class ReservatorAppHandler extends Handler{
#Override
public void handleMessage(Message msg){
return;
}
}
#Override
public void onCreate(){
String serverAddress = getSettingValue(R.string.PREFERENCES_SERVER_ADDRESS, "mail.futurice.com");// TODO: change to mail.futurice.com before delivery
proxy = new SoapDataProxy(serverAddress);
// proxy = new DummyDataProxy();
proxy = new CachedDataProxy(proxy);
addressBook = new FumAddressBook();
try {
addressBook.prefetchEntries();
} catch (ReservatorException e) {
// TODO: DIE!
}
goToFavouriteRoomRunable = new GoToFavouriteRoom(this);
handler.postDelayed(goToFavouriteRoomRunable, 20000);
}
Ok ... I finally solved my problem, mainly thanks to #Drax
Apparently, you just can't start an activity from an application ... you need an instance of an activity. So :
public class ReservatorApplication extends Application {
#Override
public void onCreate(){
Intent i = new Intent(this, RoomActivity.class);
this.startActivity(i);
}
}
Is just not valid, and causes a RunTimeException ...
As far as crashing is concern when you start activity in handler with "this". it will take handler's context. and when you do getContext() it will take activity context.
Intent i = new Intent(YourActivityName.this, RoomActivity.class);
or
Intent i = new Intent(getBaseContext(), RoomActivity.class);
It`s hard to answer without seeing the stack trace from logcat, but I found that sometimes you need to pass the application context to the a new Intent before starting an Activity.
Try this line:
Intent i = new Intent(getApplicationContext(), RoomActivity.class);