Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App update notification #1608

Merged
merged 42 commits into from
Jan 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
75a44fb
Added HTTPS request to get version data. Added APK flaor for github a…
krtkush Aug 11, 2018
5e2aa51
Moved the asynctask to its own class.
krtkush Aug 11, 2018
f85e19c
Added notification channel and code to show notification.
krtkush Aug 12, 2018
7c875a8
Merge branch 'dev' of https://github.com/krtkush/NewPipe into 1520_ap…
krtkush Aug 12, 2018
06f20c6
Moved the new version check to the application class.
krtkush Aug 12, 2018
930c971
Added version check in the pop-up player
krtkush Aug 12, 2018
12b93d6
Added new icon for update notification.
krtkush Aug 12, 2018
af42e32
Code refactored and added comments.
krtkush Aug 12, 2018
2a18eac
More refactoring.
krtkush Aug 12, 2018
e7abeb5
Added version code check.
krtkush Aug 16, 2018
04e974b
Bug fix.
krtkush Aug 16, 2018
910c10f
Removed debug code
krtkush Aug 16, 2018
17197ad
Pull request changes begins here.
krtkush Sep 15, 2018
395c958
Conflict resolution.
krtkush Sep 15, 2018
cde5f7d
Merge branch 'dev' of https://github.com/krtkush/NewPipe into 1520_ap…
krtkush Sep 15, 2018
6417bd9
Pull request changes v1.
krtkush Sep 15, 2018
7124d9b
Removed flvor checks. Added update settings under main settings.
krtkush Sep 15, 2018
f1aa3d8
Merge branch 'dev' into 1520_app_update_notif
theScrabi Oct 6, 2018
e234136
Added check for SHA1 key.
krtkush Oct 14, 2018
54ac5e8
Merge branch '1520_app_update_notif' of https://github.com/krtkush/Ne…
krtkush Oct 14, 2018
8ef702f
Removed updates options from settings in case of non github apk.
krtkush Oct 18, 2018
506ffb9
Delete BuildConfig.java
krtkush Oct 18, 2018
2d5bc3a
Delete Manifest.java
krtkush Oct 18, 2018
d1a9033
Delete R.java
krtkush Oct 18, 2018
ec28e97
Delete BuildConfig.java
krtkush Oct 18, 2018
6ef25eb
Delete Manifest.java
krtkush Oct 18, 2018
3c6d27b
Delete R.java
krtkush Oct 18, 2018
fda9b59
Code review changes.
TobiGr Oct 22, 2018
96dac0f
Code review suggested changes.
krtkush Oct 22, 2018
c29b064
Merge branch '1520_app_update_notif' of https://github.com/krtkush/Ne…
krtkush Oct 22, 2018
23309e6
Pull request changes.
krtkush Nov 18, 2018
939cc56
Pull request changes v2.
krtkush Nov 18, 2018
ad5535a
Code refactoring, PR changes.
krtkush Nov 19, 2018
26e22f9
Conflict resolution
krtkush Nov 22, 2018
069654c
vector -> png
krtkush Nov 25, 2018
c864b15
Test code revert.
krtkush Nov 25, 2018
7ed460c
Merge branch 'dev' into 1520_app_update_notif
krtkush Dec 2, 2018
f62bfea
Merge branch 'dev' into 1520_app_update_notif
TobiGr Dec 3, 2018
bfda8dc
Merge branch 'dev' into 1520_app_update_notif
krtkush Dec 5, 2018
67499bd
Merge branch 'dev' into 1520_app_update_notif
krtkush Dec 9, 2018
b674006
Conflict resolution.
krtkush Dec 28, 2018
794c370
Merge branch '1520_app_update_notif' of https://github.com/krtkush/Ne…
krtkush Dec 28, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ android {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}

buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}

debug {
multiDexEnabled true
debuggable true
Expand All @@ -33,6 +35,7 @@ android {
// but continue the build even when errors are found:
abortOnError false
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
Expand Down
Binary file added app/src/main/ic_settings_update_white-web.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions app/src/main/java/org/schabi/newpipe/App.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.schabi.newpipe;

import android.annotation.TargetApi;
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
Expand Down Expand Up @@ -65,6 +66,7 @@
public class App extends Application {
protected static final String TAG = App.class.toString();
private RefWatcher refWatcher;
private static App app;

@SuppressWarnings("unchecked")
private static final Class<? extends ReportSenderFactory>[]
Expand All @@ -88,6 +90,8 @@ public void onCreate() {
}
refWatcher = installLeakCanary();

app = this;

// Initialize settings first because others inits can use its values
SettingsActivity.initSettings(this);

Expand All @@ -100,6 +104,9 @@ public void onCreate() {
ImageLoader.getInstance().init(getImageLoaderConfigurations(10, 50));

configureRxJavaErrorHandler();

// Check for new version
new CheckForNewAppVersionTask().execute();
}

protected Downloader getDownloader() {
Expand Down Expand Up @@ -211,6 +218,31 @@ public void initNotificationChannel() {
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.createNotificationChannel(mChannel);

setUpUpdateNotificationChannel(importance);
}

/**
* Set up notification channel for app update.
* @param importance
*/
@TargetApi(Build.VERSION_CODES.O)
private void setUpUpdateNotificationChannel(int importance) {

final String appUpdateId
= getString(R.string.app_update_notification_channel_id);
final CharSequence appUpdateName
= getString(R.string.app_update_notification_channel_name);
final String appUpdateDescription
= getString(R.string.app_update_notification_channel_description);

NotificationChannel appUpdateChannel
= new NotificationChannel(appUpdateId, appUpdateName, importance);
appUpdateChannel.setDescription(appUpdateDescription);

NotificationManager appUpdateNotificationManager
= (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
appUpdateNotificationManager.createNotificationChannel(appUpdateChannel);
}

@Nullable
Expand All @@ -226,4 +258,8 @@ protected RefWatcher installLeakCanary() {
protected boolean isDisposedRxExceptionsReported() {
return false;
}

public static App getApp() {
return app;
}
}
230 changes: 230 additions & 0 deletions app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
package org.schabi.newpipe;

import android.app.Application;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.net.Uri;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;

import org.json.JSONException;
import org.json.JSONObject;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
* AsyncTask to check if there is a newer version of the NewPipe github apk available or not.
* If there is a newer version we show a notification, informing the user. On tapping
* the notification, the user will be directed to the download link.
*/
public class CheckForNewAppVersionTask extends AsyncTask<Void, Void, String> {

private static final Application app = App.getApp();
private static final String GITHUB_APK_SHA1 = "B0:2E:90:7C:1C:D6:FC:57:C3:35:F0:88:D0:8F:50:5F:94:E4:D2:15";
private static final String newPipeApiUrl = "https://newpipe.schabi.org/api/data.json";
private static final int timeoutPeriod = 30;

private SharedPreferences mPrefs;
private OkHttpClient client;

@Override
protected void onPreExecute() {

mPrefs = PreferenceManager.getDefaultSharedPreferences(app);

// Check if user has enabled/ disabled update checking
// and if the current apk is a github one or not.
if (!mPrefs.getBoolean(app.getString(R.string.update_app_key), true)
|| !isGithubApk()) {
this.cancel(true);
}
Comment on lines +63 to +66
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking this before starting the task would be more efficient, at App#L108

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is 4 years old code, is this still the case?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Stypox,

I apologize for my previous comment. I didn't realize that the code was 4 years old, and I see that it has now been deleted. I was going through the code to explore and understand the codebase, but I should have been more careful about commenting on old code.

I'll be more mindful of the age of code in the future and avoid commenting on old code unless I'm sure it's still relevant.

Thanks for your patience and understanding.

}

@Override
protected String doInBackground(Void... voids) {

// Make a network request to get latest NewPipe data.
if (client == null) {

client = new OkHttpClient
.Builder()
.readTimeout(timeoutPeriod, TimeUnit.SECONDS)
.build();
}

Request request = new Request.Builder()
.url(newPipeApiUrl)
.build();

try {
Response response = client.newCall(request).execute();
return response.body().string();
} catch (IOException ex) {
krtkush marked this conversation as resolved.
Show resolved Hide resolved
ErrorActivity.reportError(app, ex, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"app update API fail", R.string.app_ui_crash));
}

return null;
}

@Override
protected void onPostExecute(String response) {

// Parse the json from the response.
if (response != null) {

try {
JSONObject mainObject = new JSONObject(response);
JSONObject flavoursObject = mainObject.getJSONObject("flavors");
JSONObject githubObject = flavoursObject.getJSONObject("github");
JSONObject githubStableObject = githubObject.getJSONObject("stable");

String versionName = githubStableObject.getString("version");
String versionCode = githubStableObject.getString("version_code");
String apkLocationUrl = githubStableObject.getString("apk");

compareAppVersionAndShowNotification(versionName, apkLocationUrl, versionCode);

} catch (JSONException ex) {
ErrorActivity.reportError(app, ex, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"could not parse app update JSON data", R.string.app_ui_crash));
}
}
}

/**
* Method to compare the current and latest available app version.
* If a newer version is available, we show the update notification.
* @param versionName
* @param apkLocationUrl
*/
private void compareAppVersionAndShowNotification(String versionName,
String apkLocationUrl,
String versionCode) {

int NOTIFICATION_ID = 2000;

if (BuildConfig.VERSION_CODE < Integer.valueOf(versionCode)) {

// A pending intent to open the apk location url in the browser.
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkLocationUrl));
PendingIntent pendingIntent
= PendingIntent.getActivity(app, 0, intent, 0);

NotificationCompat.Builder notificationBuilder = new NotificationCompat
.Builder(app, app.getString(R.string.app_update_notification_channel_id))
.setSmallIcon(R.drawable.ic_newpipe_update)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setContentTitle(app.getString(R.string.app_update_notification_content_title))
.setContentText(app.getString(R.string.app_update_notification_content_text)
+ " " + versionName);

NotificationManagerCompat notificationManager = NotificationManagerCompat.from(app);
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
}
}

/**
* Method to get the apk's SHA1 key.
* https://stackoverflow.com/questions/9293019/get-certificate-fingerprint-from-android-app#22506133
*/
private static String getCertificateSHA1Fingerprint() {

PackageManager pm = app.getPackageManager();
String packageName = app.getPackageName();
int flags = PackageManager.GET_SIGNATURES;
PackageInfo packageInfo = null;

try {
packageInfo = pm.getPackageInfo(packageName, flags);
} catch (PackageManager.NameNotFoundException ex) {
ErrorActivity.reportError(app, ex, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not find package info", R.string.app_ui_crash));
}

Signature[] signatures = packageInfo.signatures;
byte[] cert = signatures[0].toByteArray();
InputStream input = new ByteArrayInputStream(cert);

CertificateFactory cf = null;
X509Certificate c = null;

try {
cf = CertificateFactory.getInstance("X509");
c = (X509Certificate) cf.generateCertificate(input);
} catch (CertificateException ex) {
ErrorActivity.reportError(app, ex, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Certificate error", R.string.app_ui_crash));
}

String hexString = null;

try {
MessageDigest md = MessageDigest.getInstance("SHA1");
byte[] publicKey = md.digest(c.getEncoded());
hexString = byte2HexFormatted(publicKey);
} catch (NoSuchAlgorithmException ex1) {
ErrorActivity.reportError(app, ex1, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not retrieve SHA1 key", R.string.app_ui_crash));
} catch (CertificateEncodingException ex2) {
ErrorActivity.reportError(app, ex2, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not retrieve SHA1 key", R.string.app_ui_crash));
}

return hexString;
}

private static String byte2HexFormatted(byte[] arr) {

StringBuilder str = new StringBuilder(arr.length * 2);

for (int i = 0; i < arr.length; i++) {
String h = Integer.toHexString(arr[i]);
int l = h.length();
if (l == 1) h = "0" + h;
if (l > 2) h = h.substring(l - 2, l);
str.append(h.toUpperCase());
if (i < (arr.length - 1)) str.append(':');
}
return str.toString();
}

public static boolean isGithubApk() {

return getCertificateSHA1Fingerprint().equals(GITHUB_APK_SHA1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import android.support.v7.preference.Preference;

import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.CheckForNewAppVersionTask;
import org.schabi.newpipe.R;

public class MainSettingsFragment extends BasePreferenceFragment {
Expand All @@ -13,6 +14,13 @@ public class MainSettingsFragment extends BasePreferenceFragment {
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.main_settings);

if (!CheckForNewAppVersionTask.isGithubApk()) {
final Preference update = findPreference(getString(R.string.update_pref_screen_key));
getPreferenceScreen().removePreference(update);

defaultPreferences.edit().putBoolean(getString(R.string.update_app_key), false).apply();
}

if (!DEBUG) {
final Preference debug = findPreference(getString(R.string.debug_pref_screen_key));
getPreferenceScreen().removePreference(debug);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.schabi.newpipe.settings;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.preference.Preference;

import org.schabi.newpipe.CheckForNewAppVersionTask;
import org.schabi.newpipe.R;

public class UpdateSettingsFragment extends BasePreferenceFragment {

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

String updateToggleKey = getString(R.string.update_app_key);
findPreference(updateToggleKey).setOnPreferenceChangeListener(updatePreferenceChange);
}

@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.update_settings);
}

private Preference.OnPreferenceChangeListener updatePreferenceChange
= (preference, newValue) -> {

defaultPreferences.edit().putBoolean(getString(R.string.update_app_key),
(boolean) newValue).apply();

return true;
};
}
Binary file added app/src/main/res/drawable-hdpi/ic_newpipe_update.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-mdpi/ic_newpipe_update.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/src/main/res/values/attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<attr name="ic_add" format="reference"/>
<attr name="ic_restore_defaults" format="reference"/>
<attr name="ic_blank_page" format="reference"/>
<attr name="ic_settings_update" format="reference"/>

<!-- Can't refer to colors directly in drawable's xml-->
<attr name="toolbar_shadow_drawable" format="reference"/>
Expand Down
Loading