From 029b8b57ae72975110fa988aa3606ded831e5998 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 4 Nov 2020 18:39:45 +0100 Subject: [PATCH] push notifications --- app/build.gradle | 5 + app/src/acad/res/values/strings.xml | 34 +- app/src/full/res/values/strings.xml | 32 +- app/src/main/AndroidManifest.xml | 6 + .../app/fedilab/fedilabtube/FedilabTube.java | 23 ++ .../fedilab/fedilabtube/LoginActivity.java | 90 +++-- .../fedilabtube/MyAccountActivity.java | 285 +++++++++++++--- .../fedilabtube/PeertubeRegisterActivity.java | 116 +++---- .../fedilabtube/client/PeertubeService.java | 5 + .../client/RetrofitPeertubeAPI.java | 31 ++ .../client/entities/UserSettings.java | 9 + .../DisplayNotificationsFragment.java | 26 +- .../fedilab/fedilabtube/helper/Helper.java | 2 + .../helper/NotificationHelper.java | 87 +++++ .../worker/NotificationsWorker.java | 322 ++++++++++++++++++ .../fedilabtube/worker/WorkHelper.java | 51 +++ app/src/main/res/layout/activity_login.xml | 62 ++-- .../res/layout/activity_register_peertube.xml | 138 ++++---- app/src/main/res/menu/main_my_account.xml | 9 + 19 files changed, 1061 insertions(+), 272 deletions(-) create mode 100644 app/src/main/java/app/fedilab/fedilabtube/helper/NotificationHelper.java create mode 100644 app/src/main/java/app/fedilab/fedilabtube/worker/NotificationsWorker.java create mode 100644 app/src/main/java/app/fedilab/fedilabtube/worker/WorkHelper.java create mode 100644 app/src/main/res/menu/main_my_account.xml diff --git a/app/build.gradle b/app/build.gradle index 62394ec..059cde8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -130,5 +130,10 @@ dependencies { implementation "io.github.kobakei:ratethisapp:1.2.0" implementation 'com.github.HITGIF:TextFieldBoxes:1.4.5' implementation 'com.github.vkay94:DoubleTapPlayerView:1.0.0' + implementation "ss.anoop:awesome-textinput-layout:1.0.3" + + implementation "androidx.work:work-runtime:2.4.0" + implementation "androidx.work:work-runtime-ktx:2.4.0" + } \ No newline at end of file diff --git a/app/src/acad/res/values/strings.xml b/app/src/acad/res/values/strings.xml index 6455633..70d536d 100644 --- a/app/src/acad/res/values/strings.xml +++ b/app/src/acad/res/values/strings.xml @@ -11,11 +11,29 @@ set_theme_choice set_fullscreen_choice set_autoplay_next_video_choice - set_store_in_history + set_store_in_history Modifier la photo de profil Le compte a été mis à jour ! + + Nouvelle vidéo + New blacklist info + Your video is published + Error when publishing your video + New comment + New follow + + Nouvelle vidéo depuis vos souscriptions + Nouveau commentaire sur votre vidéo + Une de vos vidéos est bloquée/débloquée + Vidéo publiée (après transcodage / mise à jour programmée) + Import de vidéo terminé + Vous ou votre chaîne avez/a un·e nouvel·le abonné·e + Quelqu\'un vous a mentionné dans les commentaires d\'une vidéo + Un signalement d\'abus a reçu un nouveau message + Un de vos rapports d\'abus a été accepté ou rejeté par les modérateurs + Enregistrer Lire automatiquement la vidéo suivante Quand une vidéo est terminée, lire la prochaine vidéo suggérée. @@ -35,6 +53,16 @@ Automatique + Mettre à jour toutes les : + + Jamais + 15 minutes + 30 minutes + 1 heure + 2 heures + 6 heures + 12 heures + %d réponse @@ -206,7 +234,9 @@ Répondre publiquement Envoyer un commentaire Tout - + Activité + App + Mise à jour des notifications %1$s a été accepté]]> Répondre diff --git a/app/src/full/res/values/strings.xml b/app/src/full/res/values/strings.xml index d1cfad0..aca83d5 100644 --- a/app/src/full/res/values/strings.xml +++ b/app/src/full/res/values/strings.xml @@ -7,7 +7,7 @@ set_video_quality_choice set_video_cache_choice set_autoplay_choice - set_store_in_history + set_store_in_history set_autoplay_next_video_choice set_theme_choice set_fullscreen_choice @@ -24,6 +24,17 @@ When a video ends, follow up with the next suggested video. Add a public reply + Activity + App + New video from your subscriptions + New comment on your video + One of your video is blocked/unblocked + Video published (after transcoding/scheduled update) + Video import finished + You or your channel(s) has a new follower + Someone mentioned you in video comments + An abuse report received a new message + One of your abuse reports has been accepted or rejected by moderators %d reply @@ -59,10 +70,28 @@ Image preview Select the file to upload + New video + New blacklist info + Your video is published + Error when publishing your video + New comment + New follow + Channel Videos Channels + Fetch every: + + Never + 15 minutes + 30 minutes + 1 hour + 2 hours + 6 hours + 12 hours + + Yes No Cancel @@ -352,6 +381,7 @@ Pick categories Pick languages Update information + Fetch notifications Add an account List of accounts diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 606b842..f63e144 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -150,6 +150,12 @@ + + \ No newline at end of file diff --git a/app/src/main/java/app/fedilab/fedilabtube/FedilabTube.java b/app/src/main/java/app/fedilab/fedilabtube/FedilabTube.java index f5c3517..ff2be2c 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/FedilabTube.java +++ b/app/src/main/java/app/fedilab/fedilabtube/FedilabTube.java @@ -19,13 +19,34 @@ import android.content.SharedPreferences; import androidx.multidex.MultiDex; import androidx.multidex.MultiDexApplication; +import androidx.work.Configuration; +import androidx.work.WorkManager; import net.gotev.uploadservice.UploadService; import app.fedilab.fedilabtube.helper.Helper; import app.fedilab.fedilabtube.helper.ThemeHelper; +import app.fedilab.fedilabtube.worker.WorkHelper; public class FedilabTube extends MultiDexApplication { + + + @Override + public void onCreate() { + super.onCreate(); + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + int interval = sharedpreferences.getInt(Helper.NOTIFICATION_INTERVAL, 60); + + Configuration myConfig = new Configuration.Builder() + .setMinimumLoggingLevel(android.util.Log.INFO) + .build(); + WorkManager.initialize(FedilabTube.this, myConfig); + if( interval >= 15 ) { + WorkHelper.fetchNotifications(this, interval); + } + } + + @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); @@ -36,6 +57,8 @@ public class FedilabTube extends MultiDexApplication { SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); int themePref = sharedpreferences.getInt(Helper.SET_THEME, Helper.DEFAULT_MODE); ThemeHelper.switchTo(themePref); + + } diff --git a/app/src/main/java/app/fedilab/fedilabtube/LoginActivity.java b/app/src/main/java/app/fedilab/fedilabtube/LoginActivity.java index 7b63c97..d03c039 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/LoginActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/LoginActivity.java @@ -22,20 +22,16 @@ import android.text.SpannableString; import android.text.Spanned; import android.text.style.ForegroundColorSpan; import android.text.style.UnderlineSpan; +import android.util.Patterns; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; -import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; -import com.google.android.material.textfield.TextInputEditText; -import com.google.android.material.textfield.TextInputLayout; - import java.util.Arrays; import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; @@ -43,6 +39,7 @@ import app.fedilab.fedilabtube.client.entities.Error; import app.fedilab.fedilabtube.client.entities.Oauth; import app.fedilab.fedilabtube.client.entities.OauthParams; import app.fedilab.fedilabtube.client.entities.Token; +import app.fedilab.fedilabtube.databinding.ActivityLoginBinding; import app.fedilab.fedilabtube.helper.Helper; import es.dmoral.toasty.Toasty; @@ -54,29 +51,26 @@ public class LoginActivity extends AppCompatActivity { private static String client_id; private static String client_secret; - private EditText login_uid; - private EditText login_passwd; - private Button connectionButton; - private TextInputEditText login_instance; + private ActivityLoginBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + binding = ActivityLoginBinding.inflate(getLayoutInflater()); + View view = binding.getRoot(); + setContentView(view); - - setContentView(R.layout.activity_login); if (getSupportActionBar() != null) getSupportActionBar().setDisplayHomeAsUpEnabled(true); - TextView create_an_account_peertube = findViewById(R.id.create_an_account_peertube); SpannableString content_create = new SpannableString(getString(R.string.join_peertube)); content_create.setSpan(new UnderlineSpan(), 0, content_create.length(), 0); content_create.setSpan(new ForegroundColorSpan(ContextCompat.getColor(LoginActivity.this, Helper.getColorAccent())), 0, content_create.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - create_an_account_peertube.setText(content_create); + binding.createAnAccountPeertube.setText(content_create, TextView.BufferType.SPANNABLE); - create_an_account_peertube.setOnClickListener(v -> { + binding.createAnAccountPeertube.setOnClickListener(v -> { Intent mainActivity = new Intent(LoginActivity.this, PeertubeRegisterActivity.class); Bundle b = new Bundle(); mainActivity.putExtras(b); @@ -84,42 +78,35 @@ public class LoginActivity extends AppCompatActivity { }); - TextInputLayout login_instance_container = findViewById(R.id.login_instance_container); - login_instance = findViewById(R.id.login_instance); if (BuildConfig.full_instances) { - login_instance_container.setVisibility(View.VISIBLE); + binding.loginInstanceContainer.setVisibility(View.VISIBLE); } - login_uid = findViewById(R.id.login_uid); - login_passwd = findViewById(R.id.login_passwd); if (Helper.isTablet(LoginActivity.this)) { - ViewGroup.LayoutParams layoutParamsI = login_instance_container.getLayoutParams(); + ViewGroup.LayoutParams layoutParamsI = binding.loginInstanceContainer.getLayoutParams(); layoutParamsI.width = (int) Helper.convertDpToPixel(300, LoginActivity.this); - login_instance_container.setLayoutParams(layoutParamsI); + binding.loginInstanceContainer.setLayoutParams(layoutParamsI); - TextInputLayout login_uid_container = findViewById(R.id.login_uid_container); - ViewGroup.LayoutParams layoutParamsU = login_uid_container.getLayoutParams(); + ViewGroup.LayoutParams layoutParamsU = binding.loginUidContainer.getLayoutParams(); layoutParamsU.width = (int) Helper.convertDpToPixel(300, LoginActivity.this); - login_uid_container.setLayoutParams(layoutParamsU); + binding.loginUidContainer.setLayoutParams(layoutParamsU); - TextInputLayout login_passwd_container = findViewById(R.id.login_passwd_container); - ViewGroup.LayoutParams layoutParamsP = login_passwd_container.getLayoutParams(); + ViewGroup.LayoutParams layoutParamsP = binding.loginPasswdContainer.getLayoutParams(); layoutParamsP.width = (int) Helper.convertDpToPixel(300, LoginActivity.this); - login_passwd_container.setLayoutParams(layoutParamsP); + binding.loginPasswdContainer.setLayoutParams(layoutParamsP); } - connectionButton = findViewById(R.id.login_button); if (!BuildConfig.full_instances) { - login_uid.setOnFocusChangeListener((v, hasFocus) -> { + binding.loginUid.setOnFocusChangeListener((v, hasFocus) -> { if (!hasFocus) { - if (android.util.Patterns.EMAIL_ADDRESS.matcher(login_uid.getText().toString().trim()).matches()) { - String[] emailArray = login_uid.getText().toString().split("@"); + if (binding.loginUid.getText() != null && android.util.Patterns.EMAIL_ADDRESS.matcher(binding.loginUid.getText().toString().trim()).matches()) { + String[] emailArray = binding.loginUid.getText().toString().split("@"); if (emailArray.length > 1 && Arrays.asList(Helper.openid).contains(emailArray[1])) { - connectionButton.callOnClick(); + binding.loginButton.callOnClick(); } } } @@ -127,30 +114,35 @@ public class LoginActivity extends AppCompatActivity { } - connectionButton.setOnClickListener(v -> { + binding.loginButton.setOnClickListener(v -> { - if (login_uid.getText().toString().contains("@") && !android.util.Patterns.EMAIL_ADDRESS.matcher(login_uid.getText().toString().trim()).matches()) { + if (binding.loginUid.getText() != null && binding.loginUid.getText().toString().contains("@") && !android.util.Patterns.EMAIL_ADDRESS.matcher(binding.loginUid.getText().toString().trim()).matches()) { Toasty.error(LoginActivity.this, getString(R.string.email_error)).show(); return; } - connectionButton.setEnabled(false); + binding.loginButton.setEnabled(false); String instance, host; if (!BuildConfig.full_instances) { - String[] emailArray = login_uid.getText().toString().split("@"); + String[] emailArray = binding.loginUid.getText().toString().split("@"); if (emailArray.length > 1 && !Arrays.asList(Helper.valideEmails).contains(emailArray[1])) { Toasty.error(LoginActivity.this, getString(R.string.email_error_domain, emailArray[1])).show(); - connectionButton.setEnabled(true); + binding.loginButton.setEnabled(true); return; } host = emailArray[1]; instance = Helper.getPeertubeUrl(host); } else { - if (login_instance == null || login_instance.getText() == null || login_instance.getText().toString().trim().length() == 0) { + if (binding.loginInstance.getText() == null || binding.loginInstance.getText().toString().trim().length() == 0) { Toasty.error(LoginActivity.this, getString(R.string.not_valide_instance)).show(); - connectionButton.setEnabled(true); + binding.loginButton.setEnabled(true); return; } - instance = host = login_instance.getText().toString().trim().toLowerCase(); + instance = host = binding.loginInstance.getText().toString().trim().toLowerCase(); + } + if (!Patterns.WEB_URL.matcher("https://"+instance).matches()) { + Toasty.error(LoginActivity.this, getString(R.string.not_valide_instance)).show(); + binding.loginButton.setEnabled(true); + return; } if (Arrays.asList(Helper.openid).contains(host) && !BuildConfig.full_instances) { new Thread(() -> { @@ -158,7 +150,7 @@ public class LoginActivity extends AppCompatActivity { Oauth oauth = new RetrofitPeertubeAPI(LoginActivity.this, instance, null).oauthClient(null, null, null, null); if (oauth == null) { runOnUiThread(() -> { - connectionButton.setEnabled(true); + binding.loginButton.setEnabled(true); Toasty.error(LoginActivity.this, getString(R.string.client_error), Toast.LENGTH_LONG).show(); }); return; @@ -179,7 +171,7 @@ public class LoginActivity extends AppCompatActivity { } catch (Exception e) { e.printStackTrace(); runOnUiThread(() -> { - connectionButton.setEnabled(true); + binding.loginButton.setEnabled(true); Toasty.error(LoginActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); }); } @@ -190,7 +182,7 @@ public class LoginActivity extends AppCompatActivity { Oauth oauth = new RetrofitPeertubeAPI(LoginActivity.this, instance, null).oauthClient(Helper.CLIENT_NAME_VALUE, Helper.WEBSITE_VALUE, Helper.OAUTH_SCOPES_PEERTUBE, Helper.WEBSITE_VALUE); if (oauth == null) { runOnUiThread(() -> { - connectionButton.setEnabled(true); + binding.loginButton.setEnabled(true); Toasty.error(LoginActivity.this, getString(R.string.client_error), Toast.LENGTH_LONG).show(); }); return; @@ -209,20 +201,22 @@ public class LoginActivity extends AppCompatActivity { oauthParams.setClient_secret(client_secret); oauthParams.setGrant_type("password"); oauthParams.setScope("user"); - oauthParams.setUsername(login_uid.getText().toString().trim()); - oauthParams.setPassword(login_passwd.getText().toString()); + oauthParams.setUsername(binding.loginUid.getText().toString().trim()); + if( binding.loginPasswd.getText() != null) { + oauthParams.setPassword(binding.loginPasswd.getText().toString()); + } try { Token token = new RetrofitPeertubeAPI(LoginActivity.this, instance, null).manageToken(oauthParams); proceedLogin(token, host); } catch (final Exception | Error e) { - oauthParams.setUsername(login_uid.getText().toString().toLowerCase().trim()); + oauthParams.setUsername(binding.loginUid.getText().toString().toLowerCase().trim()); try { Token token = new RetrofitPeertubeAPI(LoginActivity.this, instance, null).manageToken(oauthParams); proceedLogin(token, host); } catch (Error error) { Error.displayError(LoginActivity.this, error); error.printStackTrace(); - runOnUiThread(() -> connectionButton.setEnabled(true)); + runOnUiThread(() -> binding.loginButton.setEnabled(true)); } } }).start(); @@ -243,7 +237,7 @@ public class LoginActivity extends AppCompatActivity { //Update the account with the token; updateCredential(LoginActivity.this, token.getAccess_token(), client_id, client_secret, token.getRefresh_token(), host); } else { - connectionButton.setEnabled(true); + binding.loginButton.setEnabled(true); } }); } diff --git a/app/src/main/java/app/fedilab/fedilabtube/MyAccountActivity.java b/app/src/main/java/app/fedilab/fedilabtube/MyAccountActivity.java index a88fe8e..39f49f6 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/MyAccountActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/MyAccountActivity.java @@ -1,37 +1,4 @@ package app.fedilab.fedilabtube; - -import android.Manifest; -import android.app.Activity; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.view.MenuItem; -import android.view.View; -import android.widget.Toast; - -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; -import androidx.documentfile.provider.DocumentFile; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.resource.bitmap.CenterCrop; -import com.bumptech.glide.load.resource.bitmap.RoundedCorners; -import com.bumptech.glide.request.RequestOptions; - -import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; -import app.fedilab.fedilabtube.client.entities.Error; -import app.fedilab.fedilabtube.client.entities.UserMe; -import app.fedilab.fedilabtube.client.entities.UserSettings; -import app.fedilab.fedilabtube.databinding.ActivityMyAccountSettingsBinding; -import app.fedilab.fedilabtube.helper.Helper; -import es.dmoral.toasty.Toasty; - -import static app.fedilab.fedilabtube.PeertubeUploadActivity.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE; - /* Copyright 2020 Thomas Schneider * * This file is a part of TubeLab @@ -46,6 +13,49 @@ import static app.fedilab.fedilabtube.PeertubeUploadActivity.MY_PERMISSIONS_REQU * * You should have received a copy of the GNU General Public License along with TubeLab; if not, * see . */ +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.SwitchCompat; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.documentfile.provider.DocumentFile; +import androidx.work.WorkManager; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.resource.bitmap.CenterCrop; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.bumptech.glide.request.RequestOptions; + +import org.jetbrains.annotations.NotNull; + +import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; +import app.fedilab.fedilabtube.client.entities.Error; +import app.fedilab.fedilabtube.client.entities.NotificationSettings; +import app.fedilab.fedilabtube.client.entities.UserMe; +import app.fedilab.fedilabtube.client.entities.UserSettings; +import app.fedilab.fedilabtube.databinding.ActivityMyAccountSettingsBinding; +import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.worker.WorkHelper; +import es.dmoral.toasty.Toasty; + +import static app.fedilab.fedilabtube.PeertubeUploadActivity.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE; +import static app.fedilab.fedilabtube.worker.WorkHelper.NOTIFICATION_WORKER; public class MyAccountActivity extends AppCompatActivity { @@ -53,6 +63,7 @@ public class MyAccountActivity extends AppCompatActivity { private static final int PICK_IMAGE = 466; private Uri inputData; private String fileName; + private NotificationSettings notificationSettings; @Override protected void onCreate(Bundle savedInstanceState) { @@ -71,8 +82,93 @@ public class MyAccountActivity extends AppCompatActivity { binding.displayname.setText(MainActivity.userMe.getAccount().getDisplayName()); binding.description.setText(MainActivity.userMe.getAccount().getDescription()); + notificationSettings = MainActivity.userMe.getNotificationSettings(); + initializeValues(notificationSettings.getAbuseStateChange(), binding.notifAbuseAcceptedApp, binding.notifAbuseAcceptedMail); + initializeValues(notificationSettings.getAbuseNewMessage(), binding.notifAbuseReceivedApp, binding.notifAbuseReceivedMail); + initializeValues(notificationSettings.getCommentMention(), binding.notifVideoMentionApp, binding.notifVideoMentionMail); + initializeValues(notificationSettings.getNewFollow(), binding.notifNewFollowersApp, binding.notifNewFollowersMail); + initializeValues(notificationSettings.getMyVideoImportFinished(), binding.notifVideoImportedApp, binding.notifVideoImportedMail); + initializeValues(notificationSettings.getMyVideoPublished(), binding.notifVideoPublishedApp, binding.notifVideoPublishedMail); + initializeValues(notificationSettings.getBlacklistOnMyVideo(), binding.notifBlockedApp, binding.notifBlockedMail); + initializeValues(notificationSettings.getNewCommentOnMyVideo(), binding.notifNewCommentApp, binding.notifNewCommentMail); + initializeValues(notificationSettings.getNewVideoFromSubscription(), binding.notifNewVideoApp, binding.notifNewVideoMail); + Helper.loadGiF(MyAccountActivity.this, MainActivity.userMe.getAccount().getAvatar()!=null?MainActivity.userMe.getAccount().getAvatar().getPath():null, binding.profilePicture); + String[] refresh_array = getResources().getStringArray(R.array.refresh_time); + ArrayAdapter refreshArray = new ArrayAdapter<>(MyAccountActivity.this, + android.R.layout.simple_spinner_dropdown_item, refresh_array); + binding.refreshTime.setAdapter(refreshArray); + + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + int interval = sharedpreferences.getInt(Helper.NOTIFICATION_INTERVAL, 60); + binding.refreshTime.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + SharedPreferences.Editor editor = sharedpreferences.edit(); + int time; + switch (position) { + case 1: + time = 15; + break; + case 2: + time = 30; + break; + case 3: + time = 60; + break; + case 4: + time = 120; + break; + case 5: + time = 360; + break; + case 6: + time = 720; + break; + default: + time = 0; + } + editor.putInt(Helper.NOTIFICATION_INTERVAL, time); + editor.apply(); + WorkManager.getInstance(getApplicationContext()).cancelAllWorkByTag(NOTIFICATION_WORKER); + if( time > 0 ) { + WorkHelper.fetchNotifications(getApplication(), time); + + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } + }); + int position = 0; + switch (interval) { + case 0: + position = 0; + break; + case 15: + position = 1; + break; + case 30: + position = 2; + break; + case 60: + position = 3; + break; + case 120: + position = 4; + break; + case 360: + position = 5; + break; + case 720: + position = 6; + break; + } + binding.refreshTime.setSelection(position, false); + binding.selectFile.setOnClickListener(v->{ if (ContextCompat.checkSelfPermission(MyAccountActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { @@ -90,11 +186,24 @@ public class MyAccountActivity extends AppCompatActivity { startActivityForResult(intent, PICK_IMAGE); }); + } - binding.save.setOnClickListener(v -> { - binding.save.setEnabled(false); + @Override + public boolean onCreateOptionsMenu(@NotNull Menu menu) { + getMenuInflater().inflate(R.menu.main_my_account, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + finish(); + return true; + } else if (item.getItemId() == R.id.action_validate) { + item.setEnabled(false); new Thread(() -> { UserSettings userSettings = new UserSettings(); + userSettings.setNotificationSettings(notificationSettings); if( binding.displayname.getText() != null) { userSettings.setDisplayName(binding.displayname.getText().toString().trim()); } @@ -113,30 +222,22 @@ public class MyAccountActivity extends AppCompatActivity { if( avatarResponse != null && avatarResponse.getAvatar() != null ) { MainActivity.userMe.getAccount().setAvatar(avatarResponse.getAvatar()); } + Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> { Toasty.info(MyAccountActivity.this, getString(R.string.account_updated), Toasty.LENGTH_LONG).show(); - binding.save.setEnabled(true); + item.setEnabled(true); }; mainHandler.post(myRunnable); } catch (Exception | Error e) { Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> { Toasty.error(MyAccountActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show(); - binding.save.setEnabled(true); + item.setEnabled(true); }; mainHandler.post(myRunnable); - } }).start(); - }); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home) { - finish(); - return true; } return super.onOptionsItemSelected(item); } @@ -163,4 +264,96 @@ public class MyAccountActivity extends AppCompatActivity { } } + + private void initializeValues(int value, SwitchCompat app, SwitchCompat email) { + switch (value) { + case 1: + app.setChecked(true); + email.setChecked(false); + break; + case 2: + app.setChecked(false); + email.setChecked(true); + break; + case 3: + app.setChecked(true); + email.setChecked(true); + break; + default: + app.setChecked(false); + email.setChecked(false); + } + app.setOnCheckedChangeListener((compoundButton, checked) -> { + int id = app.getId(); + if (id == R.id.notif_new_video_app) { + notificationSettings.setNewVideoFromSubscription(getNewAppCheckedValue(checked, email)); + } else if (id == R.id.notif_new_comment_app) { + notificationSettings.setNewCommentOnMyVideo(getNewAppCheckedValue(checked, email)); + } else if (id == R.id.notif_blocked_app) { + notificationSettings.setBlacklistOnMyVideo(getNewAppCheckedValue(checked, email)); + } else if (id == R.id.notif_video_published_app) { + notificationSettings.setMyVideoPublished(getNewAppCheckedValue(checked, email)); + } else if (id == R.id.notif_video_imported_app) { + notificationSettings.setMyVideoImportFinished(getNewAppCheckedValue(checked, email)); + } else if (id == R.id.notif_new_followers_app) { + notificationSettings.setNewFollow(getNewAppCheckedValue(checked, email)); + } else if (id == R.id.notif_video_mention_app) { + notificationSettings.setCommentMention(getNewAppCheckedValue(checked, email)); + } else if (id == R.id.notif_abuse_received_app) { + notificationSettings.setAbuseNewMessage(getNewAppCheckedValue(checked, email)); + } else if (id == R.id.notif_abuse_accepted_app) { + notificationSettings.setAbuseStateChange(getNewAppCheckedValue(checked, email)); + } + }); + email.setOnCheckedChangeListener((compoundButtonMail, checkedMail) -> { + int id = email.getId(); + if (id == R.id.notif_new_video_mail) { + notificationSettings.setNewVideoFromSubscription(getNewMailCheckedValue(checkedMail, app)); + } else if (id == R.id.notif_new_comment_mail) { + notificationSettings.setNewCommentOnMyVideo(getNewMailCheckedValue(checkedMail, app)); + } else if (id == R.id.notif_blocked_mail) { + notificationSettings.setBlacklistOnMyVideo(getNewMailCheckedValue(checkedMail, app)); + } else if (id == R.id.notif_video_published_mail) { + notificationSettings.setMyVideoPublished(getNewMailCheckedValue(checkedMail, app)); + } else if (id == R.id.notif_video_imported_mail) { + notificationSettings.setMyVideoImportFinished(getNewMailCheckedValue(checkedMail, app)); + } else if (id == R.id.notif_new_followers_mail) { + notificationSettings.setNewFollow(getNewMailCheckedValue(checkedMail, app)); + } else if (id == R.id.notif_video_mention_mail) { + notificationSettings.setCommentMention(getNewMailCheckedValue(checkedMail, app)); + } else if (id == R.id.notif_abuse_received_mail) { + notificationSettings.setAbuseNewMessage(getNewMailCheckedValue(checkedMail, app)); + } else if (id == R.id.notif_abuse_accepted_mail) { + notificationSettings.setAbuseStateChange(getNewMailCheckedValue(checkedMail, app)); + } + }); + } + + private int getNewAppCheckedValue(boolean checked, SwitchCompat email){ + int newValue; + if( checked && email.isChecked()) { + newValue = 3; + }else if( !checked && email.isChecked()) { + newValue = 2; + } else if( checked && !email.isChecked()) { + newValue = 1; + }else { + newValue = 0; + } + return newValue; + } + + private int getNewMailCheckedValue(boolean checked, SwitchCompat app){ + int newValue; + if( checked && app.isChecked()) { + newValue = 3; + }else if( !checked && app.isChecked()) { + newValue = 1; + } else if( checked && !app.isChecked()) { + newValue = 2; + }else { + newValue = 0; + } + return newValue; + } } diff --git a/app/src/main/java/app/fedilab/fedilabtube/PeertubeRegisterActivity.java b/app/src/main/java/app/fedilab/fedilabtube/PeertubeRegisterActivity.java index bf7d0ce..a9e5af6 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/PeertubeRegisterActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/PeertubeRegisterActivity.java @@ -25,17 +25,11 @@ import android.text.method.LinkMovementMethod; import android.util.Patterns; import android.view.MenuItem; import android.view.View; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.LinearLayout; import android.widget.TextView; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; -import com.google.android.material.textfield.TextInputEditText; -import com.google.android.material.textfield.TextInputLayout; - import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -43,6 +37,7 @@ import java.util.regex.Pattern; import app.fedilab.fedilabtube.client.APIResponse; import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; import app.fedilab.fedilabtube.client.entities.AccountCreation; +import app.fedilab.fedilabtube.databinding.ActivityRegisterPeertubeBinding; import app.fedilab.fedilabtube.helper.Helper; import es.dmoral.toasty.Toasty; @@ -51,100 +46,89 @@ import static app.fedilab.fedilabtube.MainActivity.PICK_INSTANCE; public class PeertubeRegisterActivity extends AppCompatActivity { - private Button signup; - private TextView error_message; private String instance; - private TextInputEditText login_instance; + private ActivityRegisterPeertubeBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_register_peertube); + binding = ActivityRegisterPeertubeBinding.inflate(getLayoutInflater()); + View mainView = binding.getRoot(); + setContentView(mainView); if (getSupportActionBar() != null) getSupportActionBar().setDisplayHomeAsUpEnabled(true); - signup = findViewById(R.id.signup); - TextInputLayout login_instance_container = findViewById(R.id.login_instance_container); - LinearLayout title_login_instance = findViewById(R.id.title_login_instance); - TextInputEditText username = findViewById(R.id.username); - TextInputEditText email = findViewById(R.id.email); - TextInputEditText password = findViewById(R.id.password); - TextInputEditText password_confirm = findViewById(R.id.password_confirm); - login_instance = findViewById(R.id.login_instance); - CheckBox agreement = findViewById(R.id.agreement); - error_message = findViewById(R.id.error_message); if (BuildConfig.full_instances) { - login_instance_container.setVisibility(View.VISIBLE); - title_login_instance.setVisibility(View.VISIBLE); + binding.loginInstanceContainer.setVisibility(View.VISIBLE); + binding.titleLoginInstance.setVisibility(View.VISIBLE); } else { - login_instance_container.setVisibility(View.GONE); - title_login_instance.setVisibility(View.GONE); + binding.loginInstanceContainer.setVisibility(View.GONE); + binding.titleLoginInstance.setVisibility(View.GONE); } - username.setOnFocusChangeListener((view, focused) -> { - if (!focused && username.getText() != null) { + binding.username.setOnFocusChangeListener((view, focused) -> { + if (!focused && binding.username.getText() != null) { Pattern patternUsername = Pattern.compile("^[a-z0-9._]{1,50}$"); - Matcher matcherMaxId = patternUsername.matcher(username.getText().toString()); + Matcher matcherMaxId = patternUsername.matcher(binding.username.getText().toString()); if (!matcherMaxId.matches()) { - username.setError(getString(R.string.username_error)); + binding.username.setError(getString(R.string.username_error)); } } }); - Button instance_help = findViewById(R.id.instance_help); - instance_help.setOnClickListener(v -> { + binding.instanceHelp.setOnClickListener(v -> { Intent intent = new Intent(PeertubeRegisterActivity.this, InstancePickerActivity.class); startActivityForResult(intent, PICK_INSTANCE); }); - email.setOnFocusChangeListener((view, focused) -> { - if (!focused && email.getText() != null) { + binding.email.setOnFocusChangeListener((view, focused) -> { + if (!focused && binding.email.getText() != null) { Pattern patternUsername = Patterns.EMAIL_ADDRESS; - Matcher matcherMaxId = patternUsername.matcher(email.getText().toString()); + Matcher matcherMaxId = patternUsername.matcher(binding.email.getText().toString()); if (!matcherMaxId.matches()) { - email.setError(getString(R.string.email_error)); + binding.email.setError(getString(R.string.email_error)); } } }); - password.setOnFocusChangeListener((view, focused) -> { - if (!focused && password.getText() != null) { - if (password.getText().length() < 6) { - password.setError(getString(R.string.password_length_error)); + binding.password.setOnFocusChangeListener((view, focused) -> { + if (!focused && binding.password.getText() != null) { + if (binding.password.getText().length() < 6) { + binding.password.setError(getString(R.string.password_length_error)); } } }); - password_confirm.setOnFocusChangeListener((view, focused) -> { - if (!focused && password_confirm.getText() != null && password.getText() != null) { - if (password_confirm.getText().toString().compareTo(password.getText().toString()) != 0) { - password_confirm.setError(getString(R.string.password)); + binding.passwordConfirm.setOnFocusChangeListener((view, focused) -> { + if (!focused && binding.passwordConfirm.getText() != null && binding.password.getText() != null) { + if (binding.passwordConfirm.getText().toString().compareTo(binding.password.getText().toString()) != 0) { + binding.passwordConfirm.setError(getString(R.string.password)); } } }); setTextAgreement(); - signup.setOnClickListener(view -> { - error_message.setVisibility(View.GONE); - if (username.getText() == null || email.getText() == null || password.getText() == null || password_confirm.getText() == null || username.getText().toString().trim().length() == 0 || email.getText().toString().trim().length() == 0 || - password.getText().toString().trim().length() == 0 || password_confirm.getText().toString().trim().length() == 0 || !agreement.isChecked()) { + binding.signup.setOnClickListener(view -> { + binding.errorMessage.setVisibility(View.GONE); + if ( binding.username.getText() == null || binding.email.getText() == null || binding.password.getText() == null || binding.passwordConfirm.getText() == null || binding.username.getText().toString().trim().length() == 0 || binding.email.getText().toString().trim().length() == 0 || + binding.password.getText().toString().trim().length() == 0 || binding.passwordConfirm.getText().toString().trim().length() == 0 || ! binding.agreement.isChecked()) { Toasty.error(PeertubeRegisterActivity.this, getString(R.string.all_field_filled)).show(); return; } - if (!password.getText().toString().trim().equals(password_confirm.getText().toString().trim())) { + if (! binding.password.getText().toString().trim().equals( binding.passwordConfirm.getText().toString().trim())) { Toasty.error(PeertubeRegisterActivity.this, getString(R.string.password_error)).show(); return; } - if (!android.util.Patterns.EMAIL_ADDRESS.matcher(email.getText().toString().trim()).matches()) { + if (!android.util.Patterns.EMAIL_ADDRESS.matcher( binding.email.getText().toString().trim()).matches()) { Toasty.error(PeertubeRegisterActivity.this, getString(R.string.email_error)).show(); return; } - String[] emailArray = email.getText().toString().split("@"); + String[] emailArray = binding.email.getText().toString().split("@"); if (!BuildConfig.full_instances) { if (emailArray.length > 1 && !Arrays.asList(Helper.valideEmails).contains(emailArray[1])) { Toasty.error(PeertubeRegisterActivity.this, getString(R.string.email_error_domain, emailArray[1])).show(); @@ -152,23 +136,23 @@ public class PeertubeRegisterActivity extends AppCompatActivity { } } - if (password.getText().toString().trim().length() < 8) { + if ( binding.password.getText().toString().trim().length() < 8) { Toasty.error(PeertubeRegisterActivity.this, getString(R.string.password_too_short)).show(); return; } - if (username.getText().toString().matches("[a-z0-9_]")) { + if ( binding.username.getText().toString().matches("[a-z0-9_]")) { Toasty.error(PeertubeRegisterActivity.this, getString(R.string.username_error)).show(); return; } - signup.setEnabled(false); + binding.signup.setEnabled(false); if (BuildConfig.full_instances) { - if (login_instance.getText() != null) { - instance = login_instance.getText().toString(); + if ( binding.loginInstance.getText() != null) { + instance = binding.loginInstance.getText().toString(); } else { instance = ""; } - login_instance.setOnFocusChangeListener((view1, focus) -> { + binding.loginInstance.setOnFocusChangeListener((view1, focus) -> { if (!focus) { setTextAgreement(); } @@ -182,10 +166,10 @@ public class PeertubeRegisterActivity extends AppCompatActivity { } AccountCreation accountCreation = new AccountCreation(); - accountCreation.setEmail(email.getText().toString().trim()); - accountCreation.setPassword(password.getText().toString().trim()); - accountCreation.setPasswordConfirm(password_confirm.getText().toString().trim()); - accountCreation.setUsername(username.getText().toString().trim()); + accountCreation.setEmail( binding.email.getText().toString().trim()); + accountCreation.setPassword( binding.password.getText().toString().trim()); + accountCreation.setPasswordConfirm( binding.passwordConfirm.getText().toString().trim()); + accountCreation.setUsername( binding.username.getText().toString().trim()); accountCreation.setInstance(instance); new Thread(() -> { @@ -210,9 +194,9 @@ public class PeertubeRegisterActivity extends AppCompatActivity { } else { errorMessage = getString(R.string.toast_error); } - error_message.setText(errorMessage); - error_message.setVisibility(View.VISIBLE); - signup.setEnabled(true); + binding.errorMessage.setText(errorMessage); + binding.errorMessage.setVisibility(View.VISIBLE); + binding.signup.setEnabled(true); return; } @@ -260,8 +244,8 @@ public class PeertubeRegisterActivity extends AppCompatActivity { if (requestCode == PICK_INSTANCE && resultCode == Activity.RESULT_OK) { if (data != null && data.getData() != null) { String instance = String.valueOf(data.getData()); - login_instance.setText(instance); - login_instance.setSelection(instance.length()); + binding.loginInstance.setText(instance); + binding.loginInstance.setSelection(instance.length()); setTextAgreement(); } } @@ -275,9 +259,9 @@ public class PeertubeRegisterActivity extends AppCompatActivity { agreement_text.setMovementMethod(null); agreement_text.setText(null); if (BuildConfig.full_instances) { - if (login_instance.getText() != null) { + if ( binding.loginInstance.getText() != null) { content_agreement = getString(R.string.agreement_check_peertube, - "" + tos + "" + "" + tos + "" ); } } else { diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java b/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java index 54ecd91..46d1988 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java +++ b/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java @@ -29,6 +29,7 @@ import app.fedilab.fedilabtube.client.data.VideoData; import app.fedilab.fedilabtube.client.data.VideoPlaylistData; import app.fedilab.fedilabtube.client.entities.CaptionsParams; import app.fedilab.fedilabtube.client.entities.ChannelParams; +import app.fedilab.fedilabtube.client.entities.NotificationSettings; import app.fedilab.fedilabtube.client.entities.Oauth; import app.fedilab.fedilabtube.client.entities.OverviewVideo; import app.fedilab.fedilabtube.client.entities.PlaylistExist; @@ -192,6 +193,10 @@ public interface PeertubeService { @GET("users/me/notifications") Call getNotifications(@Header("Authorization") String credentials, @Query("start") String maxId, @Query("count") String count, @Query("since_id") String sinceId); + //Update Notification settings + @PUT("users/me/notification-settings") + Call updateNotifications(@Header("Authorization") String credentials, @Body NotificationSettings notificationSettings); + //Get/Post/Update/Delete video //Get a video @GET("videos/{id}") diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java b/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java index 426bd72..1b4d8dd 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java +++ b/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java @@ -54,6 +54,7 @@ import app.fedilab.fedilabtube.client.entities.AccountCreation; import app.fedilab.fedilabtube.client.entities.ChannelParams; import app.fedilab.fedilabtube.client.entities.Error; import app.fedilab.fedilabtube.client.entities.InstanceParams; +import app.fedilab.fedilabtube.client.entities.NotificationSettings; import app.fedilab.fedilabtube.client.entities.Oauth; import app.fedilab.fedilabtube.client.entities.OauthParams; import app.fedilab.fedilabtube.client.entities.OverviewVideo; @@ -237,6 +238,33 @@ public class RetrofitPeertubeAPI { return null; } + + /** + * Retrieve notifications + * + * @return APIResponse + */ + public APIResponse getNotifications() { + APIResponse apiResponse = new APIResponse(); + PeertubeService peertubeService = init(); + + Call notificationsCall = peertubeService.getNotifications("Bearer " + token, "0", "40", null); + try { + Response response = notificationsCall.execute(); + if (response.isSuccessful() && response.body() != null) { + apiResponse.setPeertubeNotifications(response.body().data); + } else { + setError(apiResponse, response.code(), response.errorBody()); + } + } catch (IOException e) { + Error error = new Error(); + error.setError(_context.getString(R.string.toast_error)); + apiResponse.setError(error); + e.printStackTrace(); + } + return apiResponse; + } + /** * Retrieve notifications * @@ -458,6 +486,9 @@ public class RetrofitPeertubeAPI { APIResponse apiResponse = new APIResponse(); UserMe.AvatarResponse avatarResponse = null; PeertubeService peertubeService = init(); + + peertubeService.updateNotifications(getToken(), userSettings.getNotificationSettings()); + Call updateUser = peertubeService.updateUser(getToken(), userSettings.isVideosHistoryEnabled(), userSettings.isAutoPlayVideo(), diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/entities/UserSettings.java b/app/src/main/java/app/fedilab/fedilabtube/client/entities/UserSettings.java index fdb5dce..1c78d87 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/client/entities/UserSettings.java +++ b/app/src/main/java/app/fedilab/fedilabtube/client/entities/UserSettings.java @@ -30,6 +30,7 @@ public class UserSettings { private String displayName; private Uri avatarfile; private String fileName; + private NotificationSettings notificationSettings; public Boolean isVideosHistoryEnabled() { return videosHistoryEnabled; @@ -122,5 +123,13 @@ public class UserSettings { this.fileName = fileName; } } + + public NotificationSettings getNotificationSettings() { + return notificationSettings; + } + + public void setNotificationSettings(NotificationSettings notificationSettings) { + this.notificationSettings = notificationSettings; + } } diff --git a/app/src/main/java/app/fedilab/fedilabtube/fragment/DisplayNotificationsFragment.java b/app/src/main/java/app/fedilab/fedilabtube/fragment/DisplayNotificationsFragment.java index f9f3113..933f3d8 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/fragment/DisplayNotificationsFragment.java +++ b/app/src/main/java/app/fedilab/fedilabtube/fragment/DisplayNotificationsFragment.java @@ -42,16 +42,28 @@ import app.fedilab.fedilabtube.viewmodel.NotificationsVM; import es.dmoral.toasty.Toasty; +@SuppressWarnings({"unused", "RedundantSuppression"}) public class DisplayNotificationsFragment extends Fragment { //Peertube notification type - public static int NEW_VIDEO_FROM_SUBSCRIPTION = 1; - public static int UNBLACKLIST_ON_MY_VIDEO = 5; - public static int BLACKLIST_ON_MY_VIDEO = 4; - public static int MY_VIDEO_PUBLISHED = 6; - public static int MY_VIDEO_IMPORT_SUCCESS = 7; - public static int MY_VIDEO_IMPORT_ERROR = 8; - public static int MY_VIDEO_REPPORT_SUCCESS = 15; + public final static int NEW_VIDEO_FROM_SUBSCRIPTION = 1; + public final static int NEW_COMMENT_ON_MY_VIDEO = 2; + public final static int NEW_ABUSE_FOR_MODERATORS = 3; + public final static int BLACKLIST_ON_MY_VIDEO = 4; + public final static int UNBLACKLIST_ON_MY_VIDEO = 5; + public final static int MY_VIDEO_PUBLISHED = 6; + public final static int MY_VIDEO_IMPORT_SUCCESS = 7; + public final static int MY_VIDEO_IMPORT_ERROR = 8; + public final static int NEW_USER_REGISTRATION = 9; + public final static int NEW_FOLLOW = 10; + public final static int COMMENT_MENTION = 11; + public final static int VIDEO_AUTO_BLACKLIST_FOR_MODERATORS = 12; + public final static int NEW_INSTANCE_FOLLOWER = 13; + public final static int AUTO_INSTANCE_FOLLOWING = 14; + public final static int MY_VIDEO_REPPORT_SUCCESS = 15; + public final static int ABUSE_NEW_MESSAGE = 16; + + private boolean flag_loading; private Context context; private PeertubeNotificationsListAdapter peertubeNotificationsListAdapter; diff --git a/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java b/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java index 1ef96eb..7881838 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java +++ b/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java @@ -91,6 +91,8 @@ public class Helper { public static final int QUALITY_LOW = 2; public static final int ADD_USER_INTENT = 5; public static final String SET_SHARE_DETAILS = "set_share_details"; + public static final String NOTIFICATION_INTERVAL = "notification_interval"; + public static final String LAST_NOTIFICATION_READ = "last_notification_read"; public static final int DEFAULT_VIDEO_CACHE_MB = 100; @SuppressWarnings({"unused", "RedundantSuppression"}) public static final String TAG = "mastodon_etalab"; diff --git a/app/src/main/java/app/fedilab/fedilabtube/helper/NotificationHelper.java b/app/src/main/java/app/fedilab/fedilabtube/helper/NotificationHelper.java new file mode 100644 index 0000000..4078f64 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/helper/NotificationHelper.java @@ -0,0 +1,87 @@ +package app.fedilab.fedilabtube.helper; + +/* Copyright 2020 Thomas Schneider + * + * This file is a part of TubeLab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with TubeLab; if not, + * see . */ +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.media.RingtoneManager; +import android.os.Build; + +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; + +import app.fedilab.fedilabtube.R; +import app.fedilab.fedilabtube.client.data.AccountData; + +import static app.fedilab.fedilabtube.worker.NotificationsWorker.FETCH_NOTIFICATION_CHANNEL_ID; + +public class NotificationHelper { + + + /** + * Sends notification with intent + * + * @param context Context + * @param intent Intent associated to the notifcation + * @param icon Bitmap profile picture + * @param title String title of the notification + * @param message String message for the notification + */ + public static void notify_user(Context context, AccountData.Account account, Intent intent, Bitmap icon, String title, String message) { + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); + int notificationId = (int) System.currentTimeMillis(); + PendingIntent pIntent = PendingIntent.getActivity(context, notificationId, intent, PendingIntent.FLAG_ONE_SHOT); + intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP); + RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); + + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, FETCH_NOTIFICATION_CHANNEL_ID) + .setSmallIcon(R.drawable.ic_notification_tubelab).setTicker(message) + .setWhen(System.currentTimeMillis()) + .setAutoCancel(true); + notificationBuilder.setGroup(account.getAcct()) + .setContentIntent(pIntent) + .setContentText(message); + + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel; + NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + channel = new NotificationChannel(FETCH_NOTIFICATION_CHANNEL_ID, context.getString(R.string.fetch_notification_channel_name), NotificationManager.IMPORTANCE_DEFAULT); + mNotificationManager.createNotificationChannel(channel); + } + notificationBuilder.setContentTitle(title); + notificationBuilder.setLargeIcon(icon); + notificationManager.notify(notificationId, notificationBuilder.build()); + + Notification summaryNotification = + new NotificationCompat.Builder(context, FETCH_NOTIFICATION_CHANNEL_ID) + .setContentTitle(title) + .setContentText(context.getApplicationContext().getString(R.string.fetch_notification_channel_name)) + .setContentIntent(pIntent) + .setLargeIcon(icon) + .setSmallIcon(R.drawable.ic_notification_tubelab) + .setGroup(account.getAcct()) + .setGroupSummary(true) + .build(); + notificationManager.notify(0, summaryNotification); + } + + +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/worker/NotificationsWorker.java b/app/src/main/java/app/fedilab/fedilabtube/worker/NotificationsWorker.java new file mode 100644 index 0000000..cc6b711 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/worker/NotificationsWorker.java @@ -0,0 +1,322 @@ +package app.fedilab.fedilabtube.worker; +/* Copyright 2020 Thomas Schneider + * + * This file is a part of TubeLab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with TubeLab; if not, + * see . */ + +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Build; +import android.os.Bundle; +import android.text.Html; + +import androidx.annotation.NonNull; +import androidx.core.app.NotificationCompat; +import androidx.work.ForegroundInfo; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.FutureTarget; + +import java.util.List; +import java.util.concurrent.ExecutionException; + +import app.fedilab.fedilabtube.MainActivity; +import app.fedilab.fedilabtube.PeertubeActivity; +import app.fedilab.fedilabtube.R; +import app.fedilab.fedilabtube.ShowAccountActivity; +import app.fedilab.fedilabtube.client.APIResponse; +import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; +import app.fedilab.fedilabtube.client.data.AccountData; +import app.fedilab.fedilabtube.client.data.NotificationData; +import app.fedilab.fedilabtube.client.entities.Actor; +import app.fedilab.fedilabtube.client.entities.Error; +import app.fedilab.fedilabtube.client.entities.NotificationSettings; +import app.fedilab.fedilabtube.client.entities.UserMe; +import app.fedilab.fedilabtube.fragment.DisplayNotificationsFragment; +import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.helper.NotificationHelper; +import app.fedilab.fedilabtube.sqlite.AccountDAO; +import app.fedilab.fedilabtube.sqlite.Sqlite; + +import static android.content.Context.NOTIFICATION_SERVICE; + +public class NotificationsWorker extends Worker { + + private final NotificationManager notificationManager; + public static String FETCH_NOTIFICATION_CHANNEL_ID = "fetch_notification_peertube"; + public static int pendingNotificationID = 1; + + public NotificationsWorker( + @NonNull Context context, + @NonNull WorkerParameters params) { + super(context, params); + notificationManager = (NotificationManager) + context.getSystemService(NOTIFICATION_SERVICE); + } + + + @NonNull + @Override + public Result doWork() { + Context applicationContext = getApplicationContext(); + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + List accounts = new AccountDAO(applicationContext, db).getAllAccount(); + if( accounts == null || accounts.size() == 0) { + return Result.success(); + } + setForegroundAsync(createForegroundInfo()); + fetchNotification(); + return Result.success(); + } + + + @SuppressWarnings({"SwitchStatementWithoutDefaultBranch", "DuplicateBranchesInSwitch"}) + private void fetchNotification() { + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + List accounts = new AccountDAO(getApplicationContext(), db).getAllAccount(); + SharedPreferences sharedpreferences = getApplicationContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedpreferences.edit(); + for(AccountData.Account account: accounts) { + RetrofitPeertubeAPI retrofitPeertubeAPI = new RetrofitPeertubeAPI(getApplicationContext(), account.getHost(), account.getToken()); + APIResponse apiResponse = retrofitPeertubeAPI.getNotifications(); + try { + UserMe userMe = retrofitPeertubeAPI.verifyCredentials(); + if( userMe != null) { + List notifications = apiResponse.getPeertubeNotifications(); + NotificationSettings notificationSettings = userMe.getNotificationSettings(); + if( apiResponse != null && apiResponse.getPeertubeNotifications() != null && apiResponse.getPeertubeNotifications().size() > 0 ) { + String last_read = sharedpreferences.getString(Helper.LAST_NOTIFICATION_READ + account.getId() + account.getHost(), null); + if( last_read != null) { + for(NotificationData.Notification notification: notifications) { + String title = ""; + String message = ""; + FutureTarget futureBitmap = Glide.with(getApplicationContext()) + .asBitmap() + .load("https://"+account.getHost()+account.getAvatar()).submit(); + Bitmap icon; + try { + icon = futureBitmap.get(); + } catch (Exception e) { + icon = BitmapFactory.decodeResource(getApplicationContext().getResources(), + R.drawable.missing_peertube); + } + + Intent intent = null; + if( last_read == null || notification.getId().compareTo(last_read) > 0) { + switch (notification.getType()) { + case DisplayNotificationsFragment.NEW_VIDEO_FROM_SUBSCRIPTION: + if(notificationSettings.getNewVideoFromSubscription() == 1 || notificationSettings.getNewVideoFromSubscription() == 3) { + if( notification.getVideo().getChannel().getAvatar() != null ) { + FutureTarget futureBitmapChannel = Glide.with(getApplicationContext()) + .asBitmap() + .load("https://"+account.getHost()+notification.getVideo().getChannel().getAvatar().getPath()).submit(); + try { + icon = futureBitmapChannel.get(); + } catch (Exception e) { + icon = BitmapFactory.decodeResource(getApplicationContext().getResources(), + R.drawable.missing_peertube); + } + + }else{ + icon = BitmapFactory.decodeResource(getApplicationContext().getResources(), + R.drawable.missing_peertube); + } + title = getApplicationContext().getString(R.string.new_video); + message = getApplicationContext().getString(R.string.peertube_video_from_subscription, notification.getVideo().getChannel().getDisplayName(), notification.getVideo().getName()); + intent = new Intent(getApplicationContext(), PeertubeActivity.class); + Bundle b = new Bundle(); + b.putParcelable("video", notification.getVideo()); + b.putString("peertube_instance", notification.getVideo().getChannel().getHost()); + b.putBoolean("isMyVideo", false); + b.putString("video_id", notification.getVideo().getId()); + b.putString("video_uuid", notification.getVideo().getUuid()); + intent.putExtras(b); + } + break; + case DisplayNotificationsFragment.NEW_COMMENT_ON_MY_VIDEO: + if(notificationSettings.getNewCommentOnMyVideo() == 1 || notificationSettings.getNewCommentOnMyVideo() == 3) { + if( notification.getComment().getAccount().getAvatar() != null ) { + FutureTarget futureBitmapChannel = Glide.with(getApplicationContext()) + .asBitmap() + .load("https://"+account.getHost()+ notification.getComment().getAccount().getAvatar().getPath()).submit(); + try { + icon = futureBitmapChannel.get(); + } catch (Exception e) { + icon = BitmapFactory.decodeResource(getApplicationContext().getResources(), + R.drawable.missing_peertube); + } + + }else{ + icon = BitmapFactory.decodeResource(getApplicationContext().getResources(), + R.drawable.missing_peertube); + } + title = getApplicationContext().getString(R.string.new_comment); + message = getApplicationContext().getString(R.string.peertube_comment_on_video, notification.getComment().getAccount().getDisplayName(), notification.getComment().getAccount().getUsername()); + intent = new Intent(getApplicationContext(), PeertubeActivity.class); + Bundle b = new Bundle(); + b.putParcelable("video", notification.getVideo()); + b.putString("peertube_instance", notification.getVideo().getChannel().getHost()); + b.putBoolean("isMyVideo", false); + b.putString("video_id", notification.getVideo().getId()); + b.putString("video_uuid", notification.getVideo().getUuid()); + intent.putExtras(b); + } + + break; + case DisplayNotificationsFragment.NEW_ABUSE_FOR_MODERATORS: + + break; + case DisplayNotificationsFragment.BLACKLIST_ON_MY_VIDEO: + if(notificationSettings.getBlacklistOnMyVideo() == 1 || notificationSettings.getBlacklistOnMyVideo() == 3) { + title = getApplicationContext().getString(R.string.new_blacklist); + message = getApplicationContext().getString(R.string.peertube_video_blacklist, notification.getVideo().getName()); + } + break; + case DisplayNotificationsFragment.UNBLACKLIST_ON_MY_VIDEO: + if(notificationSettings.getBlacklistOnMyVideo() == 1 || notificationSettings.getBlacklistOnMyVideo() == 3) { + title = getApplicationContext().getString(R.string.new_blacklist); + message = getApplicationContext().getString(R.string.peertube_video_unblacklist, notification.getVideo().getName()); + } + break; + case DisplayNotificationsFragment.MY_VIDEO_PUBLISHED: + if(notificationSettings.getMyVideoPublished() == 1 || notificationSettings.getMyVideoPublished() == 3) { + title = getApplicationContext().getString(R.string.new_my_video_published); + message = getApplicationContext().getString(R.string.peertube_video_published, notification.getVideo().getName()); + } + break; + case DisplayNotificationsFragment.MY_VIDEO_IMPORT_SUCCESS: + if(notificationSettings.getMyVideoPublished() == 1 || notificationSettings.getMyVideoPublished() == 3) { + message = getApplicationContext().getString(R.string.peertube_video_import_success, notification.getVideo().getName()); + title = getApplicationContext().getString(R.string.new_my_video_error); + } + break; + case DisplayNotificationsFragment.MY_VIDEO_IMPORT_ERROR: + if(notificationSettings.getMyVideoPublished() == 1 || notificationSettings.getMyVideoPublished() == 3) { + message = getApplicationContext().getString(R.string.peertube_video_import_error, notification.getVideo().getName()); + title = getApplicationContext().getString(R.string.new_my_video_error); + } + break; + case DisplayNotificationsFragment.NEW_USER_REGISTRATION: + + break; + case DisplayNotificationsFragment.NEW_FOLLOW: + if(notificationSettings.getNewFollow() == 1 || notificationSettings.getNewFollow() == 3) { + if( notification.getVideo().getChannel().getAvatar() != null ) { + FutureTarget futureBitmapChannel = Glide.with(getApplicationContext()) + .asBitmap() + .load("https://"+account.getHost()+notification.getVideo().getChannel().getAvatar().getPath()).submit(); + icon = futureBitmapChannel.get(); + + }else{ + icon = BitmapFactory.decodeResource(getApplicationContext().getResources(), + R.drawable.missing_peertube); + } + title = getApplicationContext().getString(R.string.new_video); + String type = notification.getActorFollow().getFollowing().getType(); + if (type != null && type.compareTo("channel") == 0) { + message = getApplicationContext().getString(R.string.peertube_follow_channel, notification.getActorFollow().getFollower().getDisplayName(), notification.getActorFollow().getFollowing().getDisplayName()); + } else { + message = getApplicationContext().getString(R.string.peertube_follow_account, notification.getActorFollow().getFollower().getDisplayName()); + } + Bundle b = new Bundle(); + Actor actor = notification.getActorFollow().getFollower(); + AccountData.Account accountAction = new AccountData.Account(); + accountAction.setAvatar(actor.getAvatar()); + accountAction.setDisplayName(actor.getDisplayName()); + accountAction.setHost(actor.getHost()); + accountAction.setUsername(actor.getName()); + intent = new Intent(getApplicationContext(), ShowAccountActivity.class); + b.putParcelable("account", accountAction); + b.putString("accountAcct", accountAction.getUsername() + "@" + accountAction.getHost()); + intent.putExtras(b); + } + break; + case DisplayNotificationsFragment.COMMENT_MENTION: + + break; + case DisplayNotificationsFragment.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS: + + break; + case DisplayNotificationsFragment.NEW_INSTANCE_FOLLOWER: + + break; + case DisplayNotificationsFragment.AUTO_INSTANCE_FOLLOWING: + + break; + case DisplayNotificationsFragment.MY_VIDEO_REPPORT_SUCCESS: + + break; + case DisplayNotificationsFragment.ABUSE_NEW_MESSAGE: + + break; + } + if( message != null && icon != null && title != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + message = Html.fromHtml(message, Html.FROM_HTML_MODE_LEGACY).toString(); + else + message = Html.fromHtml(message).toString(); + NotificationHelper.notify_user(getApplicationContext(), account, intent, icon, title, message); + } + }else { + break; + } + } + } + editor.putString(Helper.LAST_NOTIFICATION_READ + account.getId() + account.getHost(), apiResponse.getPeertubeNotifications().get(0).getId()); + editor.apply(); + } + } + + } catch (Error | InterruptedException | ExecutionException error) { + error.printStackTrace(); + } + + } + } + + @NonNull + private ForegroundInfo createForegroundInfo() { + + String title = getApplicationContext().getString(R.string.fetch_notification_channel_name); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel(FETCH_NOTIFICATION_CHANNEL_ID, + getApplicationContext().getString(R.string.fetch_notification_channel_name), + NotificationManager.IMPORTANCE_DEFAULT); + notificationManager.createNotificationChannel(channel); + } + Intent myIntent = new Intent(getApplicationContext(), MainActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity( + getApplicationContext(), + 0, + myIntent, + PendingIntent.FLAG_UPDATE_CURRENT); + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(getApplicationContext(), FETCH_NOTIFICATION_CHANNEL_ID) + .setContentTitle(title) + .setTicker(title) + .setProgress(100, 0, false) + .setOnlyAlertOnce(true) + .setContentIntent(pendingIntent) + .setSmallIcon(R.drawable.ic_notification_tubelab) + .setOngoing(true); + return new ForegroundInfo(pendingNotificationID, notificationBuilder.build()); + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/worker/WorkHelper.java b/app/src/main/java/app/fedilab/fedilabtube/worker/WorkHelper.java new file mode 100644 index 0000000..1d5b2f9 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/worker/WorkHelper.java @@ -0,0 +1,51 @@ +package app.fedilab.fedilabtube.worker; +/* Copyright 2020 Thomas Schneider + * + * This file is a part of TubeLab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with TubeLab; if not, + * see . */ +import android.app.Application; +import androidx.work.BackoffPolicy; +import androidx.work.Constraints; +import androidx.work.ExistingPeriodicWorkPolicy; +import androidx.work.NetworkType; +import androidx.work.PeriodicWorkRequest; +import androidx.work.WorkManager; + +import java.util.concurrent.TimeUnit; + +public class WorkHelper { + + public static String NOTIFICATION_WORKER = "NOTIFICATION_WORKER"; + + public static void fetchNotifications(Application application, int interval) { + + // Create Network constraint + Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build(); + + WorkManager workManager = WorkManager.getInstance(application.getApplicationContext()); + PeriodicWorkRequest periodicSyncDataWork = + new PeriodicWorkRequest.Builder(NotificationsWorker.class, interval, TimeUnit.MINUTES) + .setConstraints(constraints) + // setting a backoff on case the work needs to retry + .setBackoffCriteria(BackoffPolicy.LINEAR, PeriodicWorkRequest.MIN_BACKOFF_MILLIS, TimeUnit.MILLISECONDS) + .build(); + workManager.enqueueUniquePeriodicWork( + NOTIFICATION_WORKER, + ExistingPeriodicWorkPolicy.KEEP, //Existing Periodic Work policy + periodicSyncDataWork //work request + ); + + } +} diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 1205045..3f884a6 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -59,56 +59,57 @@ android:paddingStart="50dp" android:paddingEnd="50dp"> - - + app:layout_constraintTop_toTopOf="parent" + app:borderColor="?attr/colorAccent" + app:borderWidth="1dp" + app:cornerRadius="8dp" + android:padding="16dp"> - + android:singleLine="true" + android:importantForAutofill="no" /> - + - + app:layout_constraintTop_toBottomOf="@id/login_instance_container" + app:borderColor="?attr/colorAccent" + app:borderWidth="1dp" + app:cornerRadius="8dp" + android:padding="16dp"> - + android:singleLine="true" + android:importantForAutofill="no" /> + - - - - - + - - + android:singleLine="true" + android:importantForAutofill="no" /> +