Currently most applications are distributed via google play store, but for some reason that you do not use this distribution and still want to be able to update the app when needed, then you have nothing worry because we can completely build an update mechanism for such apps.
Basically updating the app when not using the google pay store will include the steps
- Upload the new apk version to the server
- Notice of new version
- App check with new version will display the update screen with the content related to the update
- App download apk and install
Whenever you have a new version of the app, you will need to follow the steps above.
And now let’s go in step by step
1. Upload the new apk version to the server
This part, depending on you can build the server to save the file or as myself will choose to upload the apk file to github quickly.
The photo below is an example:
https://github.com/dangquanuet/wifi-hunter-public/blob/develop/apk/wifi-hunter-code-2-name-1-0-1.apk
Note: with github, you need to download the apk to get the exact link containing the file and provide it to the app,
In the above example, the link to download the file is
1 2 | https://raw.githubusercontent.com/dangquanuet/wifi-hunter-public/develop/apk/wifi-hunter-code-2-name-1-0-1.apk |
2. Notice of new version
There are many ways to do this like
- Building a server with api returns new version information
- Use Firebase remote config to return new version information
Information about the new version should include some basic fields such as
- version code
- version name
- apk url
- release notes
3. App check with new version will display update screen with content related to the update
In the app you will need to implement the new version check, maybe doing it every time you open the app will check the current version of the app compared to the version code app update on the server.
If the current app code version is smaller than the server code version, you can proceed to the force update app screen.
The force update app screen should display the most basic information about the new verson and button so that users can decide whether to update the app right away or not.
4. App download apk and install
When users click update app, you will do the following
- Config AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token operator"><</span> uses <span class="token operator">-</span> permission android <span class="token operator">:</span> name <span class="token operator">=</span> <span class="token string">"android.permission.INTERNET"</span> <span class="token operator">/</span> <span class="token operator">></span> <span class="token operator"><</span> uses <span class="token operator">-</span> permission android <span class="token operator">:</span> name <span class="token operator">=</span> <span class="token string">"android.permission.WRITE_EXTERNAL_STORAGE"</span> <span class="token operator">/</span> <span class="token operator">></span> <span class="token operator"><</span> uses <span class="token operator">-</span> permission android <span class="token operator">:</span> name <span class="token operator">=</span> <span class="token string">"android.permission.REQUEST_INSTALL_PACKAGES"</span> <span class="token operator">/</span> <span class="token operator">></span> <span class="token operator"><</span> application <span class="token operator">></span> <span class="token operator">..</span> <span class="token punctuation">.</span> <span class="token operator"><</span> provider android <span class="token operator">:</span> name <span class="token operator">=</span> <span class="token string">"androidx.core.content.FileProvider"</span> android <span class="token operator">:</span> authorities <span class="token operator">=</span> <span class="token string">" <span class="token interpolation"><span class="token delimiter variable">${</span> applicationId <span class="token delimiter variable">}</span></span> .provider"</span> android <span class="token operator">:</span> exported <span class="token operator">=</span> <span class="token string">"false"</span> android <span class="token operator">:</span> grantUriPermissions <span class="token operator">=</span> <span class="token string">"true"</span> <span class="token operator">></span> <span class="token operator"><</span> meta <span class="token operator">-</span> <span class="token keyword">data</span> android <span class="token operator">:</span> name <span class="token operator">=</span> <span class="token string">"android.support.FILE_PROVIDER_PATHS"</span> android <span class="token operator">:</span> resource <span class="token operator">=</span> <span class="token string">"@xml/provider_paths"</span> <span class="token operator">/</span> <span class="token operator">></span> <span class="token operator"><</span> <span class="token operator">/</span> provider <span class="token operator">></span> <span class="token operator"><</span> <span class="token operator">/</span> application <span class="token operator">></span> |
You need to ensure runtime permission for devices running Android 6 and above, can refer to how to quickly request runtime permission here
- Download new apk from url to device
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">downLoadFileFromUrl</span> <span class="token punctuation">(</span> url <span class="token operator">:</span> String <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> context <span class="token operator">=</span> context <span class="token operator">?:</span> <span class="token keyword">return</span> <span class="token keyword">val</span> downloadManager <span class="token operator">=</span> context <span class="token punctuation">.</span> applicationContext <span class="token operator">?</span> <span class="token punctuation">.</span> <span class="token function">getSystemService</span> <span class="token punctuation">(</span> Context <span class="token punctuation">.</span> DOWNLOAD_SERVICE <span class="token punctuation">)</span> <span class="token keyword">as</span> DownloadManager <span class="token keyword">val</span> uri <span class="token operator">=</span> Uri <span class="token punctuation">.</span> <span class="token function">parse</span> <span class="token punctuation">(</span> url <span class="token punctuation">)</span> <span class="token comment">// check if apk file exists</span> <span class="token keyword">val</span> oldFile <span class="token operator">=</span> <span class="token function">File</span> <span class="token punctuation">(</span> filePath <span class="token operator">+</span> filename <span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> oldFile <span class="token punctuation">.</span> <span class="token function">exists</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">openApkFile</span> <span class="token punctuation">(</span> filePath <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// download location is Download folder</span> <span class="token keyword">val</span> dir <span class="token operator">=</span> Environment <span class="token punctuation">.</span> <span class="token function">getExternalStoragePublicDirectory</span> <span class="token punctuation">(</span> Environment <span class="token punctuation">.</span> DIRECTORY_DOWNLOADS <span class="token punctuation">)</span> <span class="token punctuation">;</span> dir <span class="token punctuation">.</span> <span class="token function">mkdirs</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">val</span> downloadLocation <span class="token operator">=</span> Uri <span class="token punctuation">.</span> <span class="token function">fromFile</span> <span class="token punctuation">(</span> <span class="token function">File</span> <span class="token punctuation">(</span> dir <span class="token punctuation">,</span> filename <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token comment">// start download manager</span> <span class="token keyword">val</span> request <span class="token operator">=</span> DownloadManager <span class="token punctuation">.</span> <span class="token function">Request</span> <span class="token punctuation">(</span> uri <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setTitle</span> <span class="token punctuation">(</span> filename <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setDescription</span> <span class="token punctuation">(</span> <span class="token function">getString</span> <span class="token punctuation">(</span> R <span class="token punctuation">.</span> string <span class="token punctuation">.</span> downloading <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setAllowedNetworkTypes</span> <span class="token punctuation">(</span> DownloadManager <span class="token punctuation">.</span> Request <span class="token punctuation">.</span> NETWORK_MOBILE <span class="token operator">or</span> DownloadManager <span class="token punctuation">.</span> Request <span class="token punctuation">.</span> NETWORK_WIFI <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setNotificationVisibility</span> <span class="token punctuation">(</span> DownloadManager <span class="token punctuation">.</span> Request <span class="token punctuation">.</span> VISIBILITY_VISIBLE_NOTIFY_COMPLETED <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setVisibleInDownloadsUi</span> <span class="token punctuation">(</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setDestinationUri</span> <span class="token punctuation">(</span> downloadLocation <span class="token punctuation">)</span> request <span class="token punctuation">.</span> <span class="token function">allowScanningByMediaScanner</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> downloadID <span class="token operator">=</span> downloadManager <span class="token punctuation">.</span> <span class="token function">enqueue</span> <span class="token punctuation">(</span> request <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Listen when the download is done via the receiver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span> <span class="token punctuation">(</span> savedInstanceState <span class="token operator">:</span> Bundle <span class="token operator">?</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">onCreate</span> <span class="token punctuation">(</span> savedInstanceState <span class="token punctuation">)</span> context <span class="token operator">?</span> <span class="token punctuation">.</span> <span class="token function">registerReceiver</span> <span class="token punctuation">(</span> onDownloadComplete <span class="token punctuation">,</span> <span class="token function">IntentFilter</span> <span class="token punctuation">(</span> DownloadManager <span class="token punctuation">.</span> ACTION_DOWNLOAD_COMPLETE <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onDestroy</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> context <span class="token operator">?</span> <span class="token punctuation">.</span> <span class="token function">safeUnregisterBroadCastReceiver</span> <span class="token punctuation">(</span> onDownloadComplete <span class="token punctuation">)</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">onDestroy</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">val</span> onDownloadComplete <span class="token operator">=</span> <span class="token keyword">object</span> <span class="token operator">:</span> <span class="token function">BroadcastReceiver</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onReceive</span> <span class="token punctuation">(</span> context <span class="token operator">:</span> Context <span class="token punctuation">,</span> intent <span class="token operator">:</span> Intent <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> id <span class="token operator">=</span> intent <span class="token punctuation">.</span> <span class="token function">getLongExtra</span> <span class="token punctuation">(</span> DownloadManager <span class="token punctuation">.</span> EXTRA_DOWNLOAD_ID <span class="token punctuation">,</span> <span class="token operator">-</span> <span class="token number">1</span> <span class="token punctuation">)</span> <span class="token comment">// Checking if the received broadcast is for our enqueued download by matching download id</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> downloadID <span class="token operator">==</span> id <span class="token punctuation">)</span> <span class="token punctuation">{</span> Toast <span class="token punctuation">.</span> <span class="token function">makeText</span> <span class="token punctuation">(</span> context <span class="token punctuation">,</span> <span class="token function">getString</span> <span class="token punctuation">(</span> R <span class="token punctuation">.</span> string <span class="token punctuation">.</span> download_completed <span class="token punctuation">)</span> <span class="token punctuation">,</span> Toast <span class="token punctuation">.</span> LENGTH_SHORT <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">show</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token function">openApkFile</span> <span class="token punctuation">(</span> filePath <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Display pop up app settings:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">openApkFile</span> <span class="token punctuation">(</span> location <span class="token operator">:</span> String <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> intent <span class="token operator">=</span> <span class="token function">Intent</span> <span class="token punctuation">(</span> Intent <span class="token punctuation">.</span> ACTION_VIEW <span class="token punctuation">)</span> intent <span class="token punctuation">.</span> <span class="token function">setDataAndType</span> <span class="token punctuation">(</span> <span class="token function">getUriFromFile</span> <span class="token punctuation">(</span> location <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token string">"application/vnd.android.package-archive"</span> <span class="token punctuation">)</span> intent <span class="token punctuation">.</span> flags <span class="token operator">=</span> Intent <span class="token punctuation">.</span> FLAG_ACTIVITY_NEW_TASK <span class="token operator">or</span> Intent <span class="token punctuation">.</span> FLAG_ACTIVITY_CLEAR_TASK intent <span class="token punctuation">.</span> <span class="token function">addFlags</span> <span class="token punctuation">(</span> Intent <span class="token punctuation">.</span> FLAG_GRANT_READ_URI_PERMISSION <span class="token punctuation">)</span> context <span class="token operator">?</span> <span class="token punctuation">.</span> <span class="token function">startActivity</span> <span class="token punctuation">(</span> intent <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">getUriFromFile</span> <span class="token punctuation">(</span> location <span class="token operator">:</span> String <span class="token punctuation">)</span> <span class="token operator">:</span> Uri <span class="token operator">?</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> Build <span class="token punctuation">.</span> VERSION <span class="token punctuation">.</span> SDK_INT <span class="token operator"><</span> <span class="token number">24</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> Uri <span class="token punctuation">.</span> <span class="token function">fromFile</span> <span class="token punctuation">(</span> <span class="token function">File</span> <span class="token punctuation">(</span> location <span class="token operator">+</span> filename <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> FileProvider <span class="token punctuation">.</span> <span class="token function">getUriForFile</span> <span class="token punctuation">(</span> context <span class="token operator">?:</span> <span class="token keyword">return</span> <span class="token keyword">null</span> <span class="token punctuation">,</span> context <span class="token operator">?</span> <span class="token punctuation">.</span> applicationContext <span class="token operator">?</span> <span class="token punctuation">.</span> packageName <span class="token operator">+</span> <span class="token string">".provider"</span> <span class="token punctuation">,</span> <span class="token function">File</span> <span class="token punctuation">(</span> location <span class="token operator">+</span> filename <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Note : To be able to install apps from apk, you need to make sure the user has enabled setting to allow installing apps from unknown sources:
- Go to your phone’s Settings
- Go to Security & privac y> More settings
- Tap on Install apps from external sources
- Select the browser you want to download APK files from
- Toggle Allow app installs ON
So you’ve installed a mechanism to update the app without going to the google play store
See you in the next posts.