├── updatechecker.jar
├── UpdateChecker
├── res
│ ├── values
│ │ ├── strings.xml
│ │ └── styles.xml
│ ├── values-v11
│ │ └── styles.xml
│ ├── values-v14
│ │ └── styles.xml
│ ├── drawable-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_action_search.png
│ ├── drawable-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_action_search.png
│ └── drawable-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_action_search.png
├── .classpath
├── project.properties
├── AndroidManifest.xml
├── proguard-project.txt
├── .project
└── src
│ └── com
│ └── appaholics
│ └── updatechecker
│ ├── DownloadManager.java
│ └── UpdateChecker.java
├── .gitignore
├── changelog.txt
└── README.md
/updatechecker.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RaghavSood/AppaholicsUpdateChecker/HEAD/updatechecker.jar
--------------------------------------------------------------------------------
/UpdateChecker/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | UpdateChecker
3 |
4 |
--------------------------------------------------------------------------------
/UpdateChecker/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/UpdateChecker/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/UpdateChecker/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/UpdateChecker/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RaghavSood/AppaholicsUpdateChecker/HEAD/UpdateChecker/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/UpdateChecker/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RaghavSood/AppaholicsUpdateChecker/HEAD/UpdateChecker/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/UpdateChecker/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RaghavSood/AppaholicsUpdateChecker/HEAD/UpdateChecker/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/UpdateChecker/res/drawable-hdpi/ic_action_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RaghavSood/AppaholicsUpdateChecker/HEAD/UpdateChecker/res/drawable-hdpi/ic_action_search.png
--------------------------------------------------------------------------------
/UpdateChecker/res/drawable-mdpi/ic_action_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RaghavSood/AppaholicsUpdateChecker/HEAD/UpdateChecker/res/drawable-mdpi/ic_action_search.png
--------------------------------------------------------------------------------
/UpdateChecker/res/drawable-xhdpi/ic_action_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RaghavSood/AppaholicsUpdateChecker/HEAD/UpdateChecker/res/drawable-xhdpi/ic_action_search.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 | doc/
15 |
16 | # Local configuration file (sdk path, etc)
17 | local.properties
18 |
19 | #OS X specific
20 | .DS_STORE
--------------------------------------------------------------------------------
/changelog.txt:
--------------------------------------------------------------------------------
1 | API Version 1
2 |
3 | Initial features added
4 |
5 | Checks for updates and installs them if available
6 |
7 | API Version 2
8 |
9 | New features:
10 |
11 | Launch app's page on Google Play
12 | Install and download separately
13 | Optional toast notifications
14 | Better handling for when user is offline
--------------------------------------------------------------------------------
/UpdateChecker/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | AppaholicsUpdateChecker
2 | =======================
3 |
4 | Follow me on Twitter for updates: @Appaholics16
5 |
6 | [API 1 Integration Guide](https://github.com/RaghavSood/AppaholicsUpdateChecker/wiki/API-1-Integration-Guide)
7 |
8 | [API 2 Integration Guide](https://github.com/RaghavSood/AppaholicsUpdateChecker/wiki/API-2-Integration-Guide)
9 |
10 | [UpdateChecker Documentation](https://github.com/RaghavSood/AppaholicsUpdateChecker/wiki/UpdateChecker-Documentation)
11 |
12 | [DownloadManager Documentation](https://github.com/RaghavSood/AppaholicsUpdateChecker/wiki/DownloadManager-Documentation)
--------------------------------------------------------------------------------
/UpdateChecker/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-16
15 | android.library=true
16 |
--------------------------------------------------------------------------------
/UpdateChecker/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/UpdateChecker/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/UpdateChecker/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | UpdateChecker
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/UpdateChecker/src/com/appaholics/updatechecker/DownloadManager.java:
--------------------------------------------------------------------------------
1 | package com.appaholics.updatechecker;
2 |
3 | import java.io.BufferedInputStream;
4 | import java.io.File;
5 | import java.io.FileOutputStream;
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.io.OutputStream;
9 | import java.net.URL;
10 | import java.net.URLConnection;
11 |
12 | import android.app.ProgressDialog;
13 | import android.content.Context;
14 | import android.content.Intent;
15 | import android.net.ConnectivityManager;
16 | import android.net.Uri;
17 | import android.os.AsyncTask;
18 | import android.os.Environment;
19 | import android.util.Log;
20 |
21 | /**
22 | * DO NOT USE ON YOUR OWN. All calls are handled through UpdateChecker.
23 | *
24 | * @author Raghav Sood
25 | * @version API 2
26 | * @since API 1
27 | *
28 | */
29 | public class DownloadManager extends AsyncTask
30 | {
31 | private ProgressDialog progressDialog;
32 | private String TAG = "UpdateDownloadManager";
33 | private boolean installAfterDownload = true;
34 | private boolean downloaded = false;
35 | private Context mContext;
36 |
37 | /**
38 | * Constructor for the Download Manager. DO NOT USE ON YOUR OWN. All calls are through UpdateChecker
39 | *
40 | * @param context Activity context
41 | * @since API 1
42 | */
43 | public DownloadManager(Context context)
44 | {
45 | this(context, true);
46 | }
47 |
48 | /**
49 | * Constructor for the download manager. DO NOT USE ON YOUR OWN. All calls are handled through UpdateChecker.
50 | *
51 | * @param context Activity context
52 | * @param installAfterDownload true if you want to install immediately after download, false otherwise
53 | * @since API 2
54 | */
55 | public DownloadManager(Context context, boolean installAfterDownload)
56 | {
57 | mContext = context;
58 | this.installAfterDownload = installAfterDownload;
59 | }
60 |
61 | /**
62 | * Checks to see if we have an active internet connection
63 | * @return true if online, false otherwise
64 | * @since API 1
65 | */
66 | private boolean isOnline()
67 | {
68 | try
69 | {
70 | ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
71 | return cm.getActiveNetworkInfo().isConnectedOrConnecting();
72 | }
73 | catch (Exception e)
74 | {
75 | return false;
76 | }
77 | }
78 |
79 | /**
80 | * Downloads the update file in a background task
81 | *
82 | * @since API 1
83 | */
84 | @Override
85 | protected String doInBackground(String... sUrl) {
86 | if(isOnline())
87 | {
88 | try {
89 | URL url = new URL(sUrl[0]);
90 | URLConnection connection = url.openConnection();
91 | connection.connect();
92 | // this will be useful so that you can show a typical 0-100% progress bar
93 | int fileLength = connection.getContentLength();
94 |
95 | // download the file
96 | InputStream input = new BufferedInputStream(url.openStream());
97 | OutputStream output = new FileOutputStream(Environment.getExternalStorageDirectory().getPath() + "/appupdate.apk");
98 |
99 | byte data[] = new byte[1024];
100 | long total = 0;
101 | int count;
102 | while ((count = input.read(data)) != -1) {
103 | total += count;
104 | // publishing the progress....
105 | publishProgress((int) (total * 100 / fileLength));
106 | output.write(data, 0, count);
107 | }
108 |
109 | output.flush();
110 | output.close();
111 | input.close();
112 | } catch (IOException e) {
113 | Log.e(TAG, "There was an IOException when downloading the update file");
114 | }
115 | }
116 | return null;
117 | }
118 |
119 | /**
120 | * Updates our progress bar with the download information
121 | *
122 | * @since API 1
123 | */
124 | @Override
125 | protected void onProgressUpdate(Integer... changed)
126 | {
127 | progressDialog.setProgress(changed[0]);
128 | }
129 |
130 | /**
131 | * Sets up the progress dialog to notify user of download progress
132 | *
133 | * @since API 1
134 | */
135 | @Override
136 | protected void onPreExecute()
137 | {
138 | progressDialog = new ProgressDialog(mContext);
139 | progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
140 | progressDialog.setMessage("Downloading ...");
141 | progressDialog.setCancelable(false);
142 | progressDialog.show();
143 | }
144 |
145 | /**
146 | * Dismissed progress dialog, calls install() if installAfterDownload is true
147 | *
148 | * @since API 1
149 | */
150 | @Override
151 | protected void onPostExecute(String result)
152 | {
153 | progressDialog.dismiss();
154 | downloaded = true;
155 | if(installAfterDownload)
156 | {
157 | install();
158 | }
159 | }
160 |
161 | /**
162 | * Launches an Intent to install the apk update.
163 | *
164 | * @since API 2
165 | */
166 | public void install()
167 | {
168 | if(downloaded)
169 | {
170 | String filepath = Environment.getExternalStorageDirectory().getPath() + "/appupdate.apk";
171 | Uri fileLoc = Uri.fromFile(new File(filepath));
172 | Intent intent = new Intent(Intent.ACTION_VIEW);
173 | intent.setDataAndType(fileLoc, "application/vnd.android.package-archive");
174 | mContext.startActivity(intent);
175 | }
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/UpdateChecker/src/com/appaholics/updatechecker/UpdateChecker.java:
--------------------------------------------------------------------------------
1 | package com.appaholics.updatechecker;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.InputStreamReader;
7 | import java.net.MalformedURLException;
8 | import java.net.URL;
9 | import java.util.List;
10 |
11 | import android.annotation.SuppressLint;
12 | import android.content.Context;
13 | import android.content.Intent;
14 | import android.content.pm.PackageManager;
15 | import android.content.pm.PackageManager.NameNotFoundException;
16 | import android.content.pm.ResolveInfo;
17 | import android.net.ConnectivityManager;
18 | import android.net.Uri;
19 | import android.util.Log;
20 | import android.widget.Toast;
21 |
22 | /**
23 | * @author Raghav Sood
24 | * @version API 2
25 | * @since API 1
26 | */
27 |
28 | public class UpdateChecker {
29 |
30 | private String TAG = "UpdateChecker";
31 | private boolean updateAvailable = false;
32 | private Context mContext;
33 | private boolean haveValidContext = false;
34 | private boolean useToasts = false;
35 | private DownloadManager downloadManager;
36 |
37 | /**
38 | * Constructor that only takes the Activity context.
39 | *
40 | * This constructor sets the toast notification functionality to false. Example call:
41 | * UpdateChecker uupdateChecker = new UpdateChecker(this);
42 | *
43 | * @param context An instance of your Activity's context
44 | * @since API 1
45 | */
46 | public UpdateChecker(Context context)
47 | {
48 | this(context, false);
49 | }
50 |
51 | /**
52 | * Constructor for UpdateChecker
53 | *
54 | * Example call:
55 | * UpdateChecker uupdateChecker = new UpdateChecker(this, false);
56 | *
57 | * @param context An instance of your Activity's context
58 | * @param toasts True if you want toast notifications, false by default
59 | * @since API 2
60 | */
61 | public UpdateChecker(Context context, boolean toasts) {
62 | mContext = context;
63 | if (mContext != null) {
64 | haveValidContext = true;
65 | useToasts = toasts;
66 | }
67 | }
68 |
69 | /**
70 | * Checks for app update by version code.
71 | *
72 | * Example call:
73 | * updateChecker.checkForUpdateByVersionCode("http://www.example.com/version.txt");
74 | *
75 | * @param url URL at which the text file containing your latest version code is located.
76 | * @since API 1
77 | */
78 | public void checkForUpdateByVersionCode(String url) {
79 | if(isOnline())
80 | {
81 | if (haveValidContext) {
82 | int versionCode = getVersionCode();
83 | int readCode = 0;
84 | if (versionCode >= 0) {
85 | try {
86 | readCode = Integer.parseInt(readFile(url));
87 | // Check if update is available.
88 | if (readCode > versionCode) {
89 | updateAvailable = true; //We have an update available
90 | }
91 | } catch (NumberFormatException e) {
92 | Log.e(TAG, "Invalid number online"); //Something wrong with the file content
93 | }
94 |
95 | } else {
96 | Log.e(TAG, "Invalid version code in app"); //Invalid version code
97 | }
98 | } else {
99 | Log.e(TAG, "Context is null"); //Context was null
100 | }
101 | } else {
102 | if(useToasts)
103 | {
104 | makeToastFromString("App update check failed. No internet connection available").show();
105 | }
106 | }
107 | }
108 |
109 | /**
110 | * Get's the version code of your app by the context passed in the constructor
111 | *
112 | * @return The version code if successful, -1 if not
113 | * @since API 1
114 | */
115 | public int getVersionCode() {
116 | int code;
117 | try {
118 | code = mContext.getPackageManager().getPackageInfo(
119 | mContext.getPackageName(), 0).versionCode;
120 | return code; // Found the code!
121 | } catch (NameNotFoundException e) {
122 | Log.e(TAG, "Version Code not available"); // There was a problem with the code retrieval.
123 | } catch (NullPointerException e) {
124 | Log.e(TAG, "Context is null");
125 | }
126 |
127 | return -1; // There was a problem.
128 | }
129 |
130 | /**
131 | * Downloads and installs the update apk from the URL
132 | *
133 | * @param apkUrl URL at which the update is located
134 | * @since API 1
135 | */
136 | public void downloadAndInstall(String apkUrl)
137 | {
138 | if(isOnline())
139 | {
140 | downloadManager = new DownloadManager(mContext, true);
141 | downloadManager.execute(apkUrl);
142 | }
143 | else {
144 | if(useToasts)
145 | {
146 | makeToastFromString("App update failed. No internet connection available").show();
147 | }
148 | }
149 | }
150 |
151 | /**
152 | * Must be called only after download().
153 | * @since API 2
154 | * @throws NullPointerException Thrown when download() hasn't been called.
155 | */
156 | public void install()
157 | {
158 | downloadManager.install();
159 | }
160 |
161 | /**
162 | * Downloads the update apk, but does not install it
163 | *
164 | * @param apkUrl URL at which the update is located.
165 | * @since API 2
166 | */
167 | public void download(String apkUrl)
168 | {
169 | if(isOnline())
170 | {
171 | downloadManager = new DownloadManager(mContext, false);
172 | downloadManager.execute(apkUrl);
173 | } else {
174 | if(useToasts)
175 | {
176 | makeToastFromString("App update failed. No internet connection available").show();
177 | }
178 | }
179 | }
180 |
181 | /**
182 | * Should be called after checkForUpdateByVersionCode()
183 | * @return Returns true if an update is available, false if not.
184 | * @since API 1
185 | */
186 | public boolean isUpdateAvailable() {
187 | return updateAvailable;
188 | }
189 |
190 | /**
191 | * Checks to see if an Internet connection is available
192 | *
193 | * @return True if connected or connecting, false otherwise
194 | * @since API 2
195 | */
196 | public boolean isOnline()
197 | {
198 | if(haveValidContext)
199 | {
200 | try
201 | {
202 | ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
203 | return cm.getActiveNetworkInfo().isConnectedOrConnecting();
204 | }
205 | catch (Exception e)
206 | {
207 | return false;
208 | }
209 | }
210 | return false;
211 | }
212 |
213 | /**
214 | * Launches your app's page on Google Play if it exists.
215 | *
216 | * @since API 2
217 | */
218 | public void launchMarketDetails()
219 | {
220 | if(haveValidContext)
221 | {
222 | if(hasGooglePlayInstalled())
223 | {
224 | String marketPage = "market://details?id=" + mContext.getPackageName();
225 | Intent intent = new Intent(Intent.ACTION_VIEW);
226 | intent.setData(Uri.parse(marketPage));
227 | mContext.startActivity(intent);
228 | } else {
229 | if(useToasts)
230 | {
231 | makeToastFromString("Google Play isn't installed on your device.").show();
232 | }
233 | }
234 | }
235 | }
236 |
237 | /**
238 | * Checks to use if the user's device has Google Play installed
239 | * @return true if Google Play is installed, otherwise false
240 | * @since API 2
241 | */
242 | public boolean hasGooglePlayInstalled() {
243 | Intent market = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=dummy"));
244 | PackageManager manager = mContext.getPackageManager();
245 | List list = manager.queryIntentActivities(market, 0);
246 |
247 | if (list != null && list.size() > 0) {
248 | for (int i = 0; i < list.size(); i++) {
249 | if (list.get(i).activityInfo.packageName.startsWith("com.android.vending") == true) {
250 | return true;
251 | }
252 | }
253 | }
254 | return false;
255 | }
256 |
257 | /**
258 | * Makes a toast message with a short duration from the given text.
259 | * @param text The text to be displayed by the toast
260 | * @return The toast object.
261 | * @since API 2
262 | */
263 | @SuppressLint("ShowToast")
264 | public Toast makeToastFromString(String text)
265 | {
266 | Toast toast = Toast.makeText(mContext, text, Toast.LENGTH_SHORT);
267 | return toast;
268 | }
269 |
270 | /**
271 | * Reads a file at the given URL
272 | *
273 | * @param url The URL at which the file is located
274 | * @return Returns the content of the file if successful
275 | * @since API 1
276 | */
277 | public String readFile(String url)
278 | {
279 | String result;
280 | InputStream inputStream;
281 | try {
282 | inputStream = new URL(url).openStream();
283 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
284 | result = bufferedReader.readLine();
285 | return result;
286 | } catch (MalformedURLException e) {
287 | Log.e(TAG, "Invalid URL");
288 | } catch (IOException e) {
289 | Log.e(TAG, "There was an IO exception");
290 | }
291 |
292 | Log.e(TAG, "There was an error reading the file");
293 | return "Problem reading the file";
294 | }
295 | }
296 |
--------------------------------------------------------------------------------