Merge branch 'unifiedpush' into develop
This commit is contained in:
commit
6cffe6a5f6
|
@ -1,5 +1,8 @@
|
|||
apply plugin: 'com.android.application'
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
def flavor
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "29.0.3"
|
||||
|
@ -42,6 +45,7 @@ android {
|
|||
buildConfigField "boolean", "DONATIONS", "true"
|
||||
buildConfigField "boolean", "lite", "false"
|
||||
resValue "string", "app_name", "Fedilab"
|
||||
flavor = "fdroid"
|
||||
}
|
||||
lite {
|
||||
minSdkVersion 21
|
||||
|
@ -49,12 +53,14 @@ android {
|
|||
buildConfigField "boolean", "DONATIONS", "true"
|
||||
buildConfigField "boolean", "lite", "true"
|
||||
resValue "string", "app_name", "Fedilab Lite"
|
||||
flavor = "lite"
|
||||
}
|
||||
playstore {
|
||||
applicationId "app.fedilab.android"
|
||||
buildConfigField "boolean", "DONATIONS", "false"
|
||||
buildConfigField "boolean", "lite", "false"
|
||||
resValue "string", "app_name", "Fedilab"
|
||||
flavor = "playstore"
|
||||
}
|
||||
}
|
||||
buildFeatures {
|
||||
|
@ -62,7 +68,7 @@ android {
|
|||
}
|
||||
sourceSets {
|
||||
playstore {
|
||||
manifest.srcFile "src/common/AndroidManifest.xml"
|
||||
manifest.srcFile "src/playstore/AndroidManifest.xml"
|
||||
assets.srcDirs = ['/src/mains/assets', 'src/common/assets']
|
||||
java.srcDirs = ['src/main/java', 'src/playstore/java','src/common/java']
|
||||
res.srcDirs = ['src/main/res', 'src/playstore/res','src/common/res']
|
||||
|
@ -71,13 +77,13 @@ android {
|
|||
fdroid {
|
||||
manifest.srcFile "src/common/AndroidManifest.xml"
|
||||
assets.srcDirs = ['/src/mains/assets', 'src/common/assets']
|
||||
java.srcDirs = ['src/main/java', 'src/fdroid/java','src/common/java']
|
||||
java.srcDirs = ['src/main/java', 'src/fdroid/java','src/common/java', 'src/fdroidcommon/java']
|
||||
res.srcDirs = ['src/main/res', 'src/fdroid/res','src/common/res']
|
||||
}
|
||||
lite {
|
||||
manifest.srcFile "src/lite/AndroidManifest.xml"
|
||||
assets.srcDirs = ['/src/mains/assets']
|
||||
java.srcDirs = ['src/main/java', 'src/lite/java']
|
||||
java.srcDirs = ['src/main/java', 'src/lite/java', 'src/fdroidcommon/java']
|
||||
res.srcDirs = ['src/main/res', 'src/lite/res']
|
||||
}
|
||||
}
|
||||
|
@ -173,6 +179,12 @@ dependencies {
|
|||
//debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'
|
||||
implementation 'com.huangyz0918:androidwm-light:0.1.2'
|
||||
|
||||
|
||||
implementation 'com.github.UnifiedPush:android-connector:1.1.0'
|
||||
implementation "com.madgag.spongycastle:bctls-jdk15on:1.58.0.0"
|
||||
|
||||
|
||||
implementation 'commons-net:commons-net:3.6'
|
||||
//Flavors
|
||||
|
||||
//Playstore
|
||||
|
@ -182,7 +194,7 @@ dependencies {
|
|||
playstoreImplementation 'org.framagit.tom79:country-picker-android:1.2.0'
|
||||
playstoreImplementation 'com.vanniktech:emoji-one:0.6.0'
|
||||
playstoreImplementation 'ja.burhanrashid52:photoeditor:0.4.0'
|
||||
|
||||
playstoreImplementation 'com.github.UnifiedPush:android-connector_fcm_added:1.0.0'
|
||||
|
||||
//Fdroid
|
||||
fdroidApi 'com.theartofdev.edmodo:android-image-cropper:2.8.+'
|
||||
|
@ -191,4 +203,29 @@ dependencies {
|
|||
fdroidImplementation 'com.vanniktech:emoji-one:0.6.0'
|
||||
fdroidImplementation 'ja.burhanrashid52:photoeditor:0.4.0'
|
||||
|
||||
|
||||
|
||||
}
|
||||
def getCurrentFlavor() {
|
||||
Gradle gradle = getGradle()
|
||||
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
|
||||
|
||||
Pattern pattern
|
||||
|
||||
if( tskReqStr.contains( "assemble" ) )
|
||||
pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
|
||||
else
|
||||
pattern = Pattern.compile("generate(\\w+)(Release|Debug)")
|
||||
Matcher matcher = pattern.matcher( tskReqStr )
|
||||
|
||||
if( matcher.find() ) {
|
||||
return matcher.group(1).toLowerCase()
|
||||
}else
|
||||
{
|
||||
println "NO MATCH FOUND"
|
||||
return ""
|
||||
}
|
||||
}
|
||||
if( getCurrentFlavor() == "playstore") {
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
}
|
||||
|
|
|
@ -84,5 +84,20 @@
|
|||
<activity
|
||||
android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
|
||||
android:theme="@style/Base.Theme.AppCompat" />
|
||||
|
||||
|
||||
<receiver
|
||||
android:name=".services.UnifiedPushService"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="org.unifiedpush.android.connector.MESSAGE" />
|
||||
<action android:name="org.unifiedpush.android.connector.UNREGISTERED" />
|
||||
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT" />
|
||||
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED" />
|
||||
<action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
</application>
|
||||
</manifest>
|
|
@ -1,23 +0,0 @@
|
|||
package app.fedilab.android.services;
|
||||
/* Copyright 2020 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Fedilab 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 Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import app.fedilab.android.helper.Helper;
|
||||
|
||||
public class LiveNotificationService extends BaseLiveNotificationService {
|
||||
static {
|
||||
Helper.installProvider();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package app.fedilab.android.helper;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.text.SpannableString;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.util.Linkify;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import org.unifiedpush.android.connector.Registration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.client.Entities.Account;
|
||||
import app.fedilab.android.jobs.ApplicationJob;
|
||||
import app.fedilab.android.jobs.NotificationsSyncJob;
|
||||
import app.fedilab.android.sqlite.AccountDAO;
|
||||
import app.fedilab.android.sqlite.Sqlite;
|
||||
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
import static app.fedilab.android.helper.BaseHelper.NOTIF_NONE;
|
||||
import static app.fedilab.android.helper.BaseHelper.NOTIF_PUSH;
|
||||
import static app.fedilab.android.helper.BaseHelper.liveNotifType;
|
||||
|
||||
public class PushHelper {
|
||||
|
||||
public static void startStreaming(Context context) {
|
||||
int liveNotifications = liveNotifType(context);
|
||||
ApplicationJob.cancelAllJob(NotificationsSyncJob.NOTIFICATION_REFRESH);
|
||||
NotificationsSyncJob.schedule(false);
|
||||
switch (liveNotifications) {
|
||||
case NOTIF_PUSH:
|
||||
new Thread(() -> {
|
||||
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||
List<Account> accounts = new AccountDAO(context, db).getPushNotificationAccounts();
|
||||
((Activity) context).runOnUiThread(() -> {
|
||||
for (Account account : accounts) {
|
||||
registerAppWithDialog(context, account.getUsername() + "@" + account.getInstance());
|
||||
}
|
||||
});
|
||||
}).start();
|
||||
break;
|
||||
case NOTIF_NONE:
|
||||
new Registration().unregisterApp(context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void registerAppWithDialog(Context context, String slug) {
|
||||
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
|
||||
int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK);
|
||||
int style;
|
||||
if (theme == Helper.THEME_DARK) {
|
||||
style = R.style.DialogDark;
|
||||
} else if (theme == Helper.THEME_BLACK) {
|
||||
style = R.style.DialogBlack;
|
||||
} else {
|
||||
style = R.style.Dialog;
|
||||
}
|
||||
|
||||
Registration registration = new Registration();
|
||||
List<String> distributors = registration.getDistributors(context);
|
||||
if (distributors.size() == 1 || !registration.getDistributor(context).isEmpty()) {
|
||||
if (distributors.size() == 1) {
|
||||
registration.saveDistributor(context, distributors.get(0));
|
||||
} else {
|
||||
registration.saveDistributor(context, registration.getDistributor(context));
|
||||
}
|
||||
registration.registerApp(context, slug);
|
||||
return;
|
||||
}
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(context, style);
|
||||
if (distributors.size() == 0) {
|
||||
alert.setTitle(R.string.no_distributors_found);
|
||||
final TextView message = new TextView(context);
|
||||
String link = "https://fedilab.app/wiki/features/push-notifications#fdroid";
|
||||
final SpannableString s =
|
||||
new SpannableString(context.getString(R.string.no_distributors_explanation, link));
|
||||
Linkify.addLinks(s, Linkify.WEB_URLS);
|
||||
message.setText(s);
|
||||
message.setPadding(30, 20, 30, 10);
|
||||
message.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
alert.setView(message);
|
||||
alert.setPositiveButton(R.string.close, (dialog, whichButton) -> dialog.dismiss());
|
||||
} else {
|
||||
alert.setTitle(R.string.select_distributors);
|
||||
String[] distributorsStr = distributors.toArray(new String[0]);
|
||||
alert.setSingleChoiceItems(distributorsStr, -1, (dialog, item) -> {
|
||||
String distributor = distributorsStr[item];
|
||||
registration.saveDistributor(context, distributor);
|
||||
registration.registerApp(context, slug);
|
||||
dialog.dismiss();
|
||||
});
|
||||
}
|
||||
alert.show();
|
||||
}
|
||||
}
|
|
@ -69,18 +69,7 @@
|
|||
android:name="app.fedilab.android.services.BackupNotificationInDataBaseService"
|
||||
android:exported="false" />
|
||||
|
||||
<receiver android:name=".services.UpgradeReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name="app.fedilab.android.services.RestartLiveNotificationReceiver"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name="app.fedilab.android.services.PeertubeUploadReceiver"
|
||||
android:exported="false">
|
||||
|
@ -88,20 +77,7 @@
|
|||
<action android:name="app.fedilab.android.uploadservice.broadcast.status" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name="app.fedilab.android.services.StopLiveNotificationReceiver"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="StopLiveNotificationReceiver" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name="app.fedilab.android.services.StopDelayedNotificationReceiver"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="StopDelayedNotificationReceiver" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name="app.fedilab.android.services.StreamingHomeTimelineService"
|
||||
android:exported="false" />
|
||||
|
@ -461,5 +437,19 @@
|
|||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
|
||||
<receiver
|
||||
android:name=".services.UnifiedPushService"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="org.unifiedpush.android.connector.MESSAGE" />
|
||||
<action android:name="org.unifiedpush.android.connector.UNREGISTERED" />
|
||||
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT" />
|
||||
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED" />
|
||||
<action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
</application>
|
||||
</manifest>
|
|
@ -133,9 +133,11 @@ import app.fedilab.android.fragments.TabLayoutNotificationsFragment;
|
|||
import app.fedilab.android.fragments.TabLayoutScheduleFragment;
|
||||
import app.fedilab.android.fragments.WhoToFollowFragment;
|
||||
import app.fedilab.android.helper.CrossActions;
|
||||
import app.fedilab.android.helper.ECDH;
|
||||
import app.fedilab.android.helper.ExpandableHeightListView;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.helper.MenuFloating;
|
||||
import app.fedilab.android.helper.PushHelper;
|
||||
import app.fedilab.android.interfaces.OnFilterActionInterface;
|
||||
import app.fedilab.android.interfaces.OnRetrieveEmojiAccountInterface;
|
||||
import app.fedilab.android.interfaces.OnRetrieveFeedsInterface;
|
||||
|
@ -156,7 +158,8 @@ import es.dmoral.toasty.Toasty;
|
|||
|
||||
import static app.fedilab.android.activities.WebviewActivity.trackingDomains;
|
||||
import static app.fedilab.android.asynctasks.ManageFiltersAsyncTask.action.GET_ALL_FILTER;
|
||||
import static app.fedilab.android.helper.BaseHelper.startStreaming;
|
||||
import static app.fedilab.android.helper.BaseHelper.NOTIF_NONE;
|
||||
import static app.fedilab.android.helper.BaseHelper.NOTIF_PUSH;
|
||||
import static app.fedilab.android.helper.Helper.changeDrawableColor;
|
||||
|
||||
|
||||
|
@ -215,6 +218,7 @@ public abstract class BaseMainActivity extends BaseActivity
|
|||
Account account = new AccountDAO(BaseMainActivity.this, db).getUniqAccount(userId, instance);
|
||||
Intent intent = getIntent();
|
||||
PackageManager pm = getPackageManager();
|
||||
|
||||
try {
|
||||
if (intent != null && intent.getComponent() != null) {
|
||||
ActivityInfo ai = pm.getActivityInfo(intent.getComponent(), PackageManager.GET_META_DATA);
|
||||
|
@ -249,6 +253,7 @@ public abstract class BaseMainActivity extends BaseActivity
|
|||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
if (account == null) {
|
||||
Helper.logoutCurrentUser(BaseMainActivity.this);
|
||||
Intent myIntent = new Intent(BaseMainActivity.this, LoginActivity.class);
|
||||
|
@ -306,6 +311,7 @@ public abstract class BaseMainActivity extends BaseActivity
|
|||
activity = this;
|
||||
rateThisApp();
|
||||
|
||||
|
||||
//Intialize Peertube information
|
||||
//This task will allow to instance a static PeertubeInformation class
|
||||
if (social == UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE) {
|
||||
|
@ -418,6 +424,9 @@ public abstract class BaseMainActivity extends BaseActivity
|
|||
add_new = findViewById(R.id.add_new);
|
||||
|
||||
main_app_container = findViewById(R.id.main_app_container);
|
||||
|
||||
|
||||
|
||||
if (social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA || social == UpdateAccountInfoAsyncTask.SOCIAL.GNU || social == UpdateAccountInfoAsyncTask.SOCIAL.FRIENDICA) {
|
||||
new SyncTimelinesAsyncTask(BaseMainActivity.this, 0, Helper.canFetchList(BaseMainActivity.this, account), BaseMainActivity.this);
|
||||
|
||||
|
@ -662,7 +671,7 @@ public abstract class BaseMainActivity extends BaseActivity
|
|||
}
|
||||
boolean popupShown = sharedpreferences.getBoolean(Helper.SET_POPUP_PUSH, false);
|
||||
if (popupShown) {
|
||||
Helper.startStreaming(BaseMainActivity.this);
|
||||
PushHelper.startStreaming(BaseMainActivity.this);
|
||||
}
|
||||
|
||||
if (hidde_menu != null)
|
||||
|
@ -1194,55 +1203,39 @@ public abstract class BaseMainActivity extends BaseActivity
|
|||
|
||||
//Live notification mode
|
||||
final Spinner set_live_type = dialogView.findViewById(R.id.set_live_type);
|
||||
String[] labels = {getString(R.string.live_notif), getString(R.string.live_delayed), getString(R.string.no_live_notif)};
|
||||
String[] labels = {getString(R.string.push_notif), getString(R.string.no_live_notif)};
|
||||
ArrayAdapter<String> adapterLive = new ArrayAdapter<>(BaseMainActivity.this,
|
||||
android.R.layout.simple_spinner_dropdown_item, labels);
|
||||
set_live_type.setAdapter(adapterLive);
|
||||
TextView set_live_type_indication = dialogView.findViewById(R.id.set_live_type_indication);
|
||||
switch (Helper.liveNotifType(BaseMainActivity.this)) {
|
||||
case Helper.NOTIF_LIVE:
|
||||
set_live_type_indication.setText(R.string.live_notif_indication);
|
||||
break;
|
||||
case Helper.NOTIF_DELAYED:
|
||||
set_live_type_indication.setText(R.string.set_live_type_indication);
|
||||
case Helper.NOTIF_PUSH:
|
||||
set_live_type_indication.setText(R.string.set_push_notifications);
|
||||
break;
|
||||
case Helper.NOTIF_NONE:
|
||||
set_live_type_indication.setText(R.string.no_live_indication);
|
||||
break;
|
||||
}
|
||||
set_live_type.setSelection(Helper.liveNotifType(BaseMainActivity.this), false);
|
||||
int livenotif = Helper.liveNotifType(BaseMainActivity.this);
|
||||
int selection = 0;
|
||||
if (livenotif == NOTIF_NONE)
|
||||
selection = 1;
|
||||
set_live_type.setSelection(selection, false);
|
||||
set_live_type.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||
switch (position) {
|
||||
case Helper.NOTIF_LIVE:
|
||||
editor.putBoolean(Helper.SET_LIVE_NOTIFICATIONS, true);
|
||||
editor.putBoolean(Helper.SET_DELAYED_NOTIFICATIONS, false);
|
||||
case 0:
|
||||
editor.putBoolean(Helper.SET_PUSH_NOTIFICATIONS, true);
|
||||
editor.apply();
|
||||
startStreaming(BaseMainActivity.this);
|
||||
PushHelper.startStreaming(BaseMainActivity.this);
|
||||
set_live_type_indication.setText(R.string.set_push_notifications);
|
||||
break;
|
||||
case Helper.NOTIF_DELAYED:
|
||||
editor.putBoolean(Helper.SET_LIVE_NOTIFICATIONS, false);
|
||||
editor.putBoolean(Helper.SET_DELAYED_NOTIFICATIONS, true);
|
||||
editor.apply();
|
||||
startStreaming(BaseMainActivity.this);
|
||||
break;
|
||||
case Helper.NOTIF_NONE:
|
||||
editor.putBoolean(Helper.SET_LIVE_NOTIFICATIONS, false);
|
||||
editor.putBoolean(Helper.SET_DELAYED_NOTIFICATIONS, false);
|
||||
editor.apply();
|
||||
break;
|
||||
}
|
||||
switch (Helper.liveNotifType(BaseMainActivity.this)) {
|
||||
case Helper.NOTIF_LIVE:
|
||||
set_live_type_indication.setText(R.string.live_notif_indication);
|
||||
break;
|
||||
case Helper.NOTIF_DELAYED:
|
||||
set_live_type_indication.setText(R.string.set_live_type_indication);
|
||||
break;
|
||||
case Helper.NOTIF_NONE:
|
||||
case 1:
|
||||
editor.putBoolean(Helper.SET_PUSH_NOTIFICATIONS, false);
|
||||
set_live_type_indication.setText(R.string.no_live_indication);
|
||||
editor.apply();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1386,6 +1379,7 @@ public abstract class BaseMainActivity extends BaseActivity
|
|||
|
||||
protected abstract void rateThisApp();
|
||||
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
|
@ -2426,6 +2420,7 @@ public abstract class BaseMainActivity extends BaseActivity
|
|||
|
||||
protected abstract void launchOwnerNotificationsActivity();
|
||||
|
||||
|
||||
public enum iconLauncher {
|
||||
BUBBLES,
|
||||
FEDIVERSE,
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
package app.fedilab.android.activities;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
|
@ -121,6 +122,7 @@ public class LoginActivity extends BaseActivity {
|
|||
return Helper.instanceWithProtocol(context, instance) + Helper.EP_AUTHORIZE + "?" + queryString;
|
||||
}
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
@ -325,18 +327,23 @@ public class LoginActivity extends BaseActivity {
|
|||
thread.interrupt();
|
||||
thread = null;
|
||||
}
|
||||
|
||||
if (oldSearch == null || !oldSearch.equals(s.toString().trim())) {
|
||||
thread = new Thread(() -> {
|
||||
try {
|
||||
final String response = new HttpsConnection(LoginActivity.this, instance).get("https://instances.social/api/1.0" + action, 30, parameters, Helper.THEKINRAR_SECRET_TOKEN);
|
||||
String response = null;
|
||||
try {
|
||||
response = new HttpsConnection(LoginActivity.this, instance).get("https://instances.social/api/1.0" + action, 30, parameters, Helper.THEKINRAR_SECRET_TOKEN);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
return;
|
||||
}
|
||||
String finalResponse = response;
|
||||
runOnUiThread(() -> {
|
||||
String[] instances;
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(response);
|
||||
JSONObject jsonObject = new JSONObject(finalResponse);
|
||||
JSONArray jsonArray = jsonObject.getJSONArray("instances");
|
||||
int length = 0;
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
|
|
|
@ -30,6 +30,7 @@ import app.fedilab.android.client.Entities.Account;
|
|||
import app.fedilab.android.client.Entities.Error;
|
||||
import app.fedilab.android.client.GNUAPI;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.helper.PushHelper;
|
||||
import app.fedilab.android.interfaces.OnPostStatusActionInterface;
|
||||
import app.fedilab.android.sqlite.Sqlite;
|
||||
import app.fedilab.android.sqlite.TagsCacheDAO;
|
||||
|
@ -125,11 +126,6 @@ public class PostStatusAsyncTask {
|
|||
}
|
||||
};
|
||||
thread.start();
|
||||
if (account != null) {
|
||||
String key = account.getUsername() + "@" + account.getInstance();
|
||||
Helper.sleeps.put(key, 30000);
|
||||
Helper.startStreaming(contextReference.get());
|
||||
}
|
||||
};
|
||||
mainHandler.post(myRunnable);
|
||||
}).start();
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/* Copyright 2021 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Fedilab 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 Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
package app.fedilab.android.asynctasks;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import app.fedilab.android.client.API;
|
||||
import app.fedilab.android.client.APIResponse;
|
||||
import app.fedilab.android.client.Entities.Account;
|
||||
|
||||
|
||||
public class PostSubscriptionAsyncTask {
|
||||
|
||||
private final WeakReference<Context> contextReference;
|
||||
private APIResponse apiResponse;
|
||||
private final String endpoint;
|
||||
private final Account account;
|
||||
|
||||
public PostSubscriptionAsyncTask(Context context, Account account, String endpoint) {
|
||||
this.contextReference = new WeakReference<>(context);
|
||||
this.endpoint = endpoint;
|
||||
this.account = account;
|
||||
doInBackground();
|
||||
}
|
||||
|
||||
|
||||
protected void doInBackground() {
|
||||
new Thread(() -> {
|
||||
apiResponse = new API(contextReference.get(), account.getInstance(), account.getToken()).pushSubscription(endpoint, account);
|
||||
}).start();
|
||||
}
|
||||
|
||||
}
|
|
@ -23,7 +23,10 @@ import android.os.Bundle;
|
|||
import android.text.Html;
|
||||
import android.text.SpannableString;
|
||||
|
||||
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
|
@ -58,6 +61,7 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -88,6 +92,7 @@ import app.fedilab.android.client.Entities.Notification;
|
|||
import app.fedilab.android.client.Entities.Peertube;
|
||||
import app.fedilab.android.client.Entities.Poll;
|
||||
import app.fedilab.android.client.Entities.PollOptions;
|
||||
import app.fedilab.android.client.Entities.PushSubscription;
|
||||
import app.fedilab.android.client.Entities.Reaction;
|
||||
import app.fedilab.android.client.Entities.Relationship;
|
||||
import app.fedilab.android.client.Entities.Report;
|
||||
|
@ -99,11 +104,15 @@ import app.fedilab.android.client.Entities.Tag;
|
|||
import app.fedilab.android.client.Entities.Trends;
|
||||
import app.fedilab.android.client.Entities.TrendsHistory;
|
||||
import app.fedilab.android.fragments.DisplayNotificationsFragment;
|
||||
import app.fedilab.android.helper.ECDH;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.sqlite.AccountDAO;
|
||||
import app.fedilab.android.sqlite.Sqlite;
|
||||
import app.fedilab.android.sqlite.TimelineCacheDAO;
|
||||
|
||||
import static app.fedilab.android.helper.ECDH.kp_private;
|
||||
import static app.fedilab.android.helper.ECDH.kp_public;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Thomas on 23/04/2017.
|
||||
|
@ -555,6 +564,34 @@ public class API {
|
|||
return reactions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse a push notification
|
||||
*
|
||||
* @param resobj JSONObject
|
||||
* @return PushSubscription
|
||||
*/
|
||||
private static PushSubscription parsePushNotifications(JSONObject resobj) {
|
||||
PushSubscription pushSubscription = new PushSubscription();
|
||||
try {
|
||||
pushSubscription.setId(resobj.getString("id"));
|
||||
pushSubscription.setEndpoint(resobj.getString("endpoint"));
|
||||
pushSubscription.setServer_key(resobj.getString("server_key"));
|
||||
JSONObject alertsObject = resobj.getJSONObject("alerts");
|
||||
Iterator<String> iter = alertsObject.keys();
|
||||
HashMap<String, Boolean> alertsList = new HashMap<>();
|
||||
while (iter.hasNext()) {
|
||||
String key = iter.next();
|
||||
boolean value = (boolean) alertsObject.get(key);
|
||||
alertsList.put(key, value);
|
||||
}
|
||||
pushSubscription.setAlertsList(alertsList);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return pushSubscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a reaction
|
||||
*
|
||||
|
@ -5101,7 +5138,6 @@ public class API {
|
|||
private APIResponse getNotifications(DisplayNotificationsFragment.Type type, String max_id, String since_id, int limit, boolean display) {
|
||||
|
||||
HashMap<String, String> params = new HashMap<>();
|
||||
|
||||
if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PIXELFED) {
|
||||
params.put("pg", "true");
|
||||
params.put("page", max_id);
|
||||
|
@ -5767,6 +5803,133 @@ public class API {
|
|||
return apiResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subscribed push notifications
|
||||
*
|
||||
* @return APIResponse
|
||||
*/
|
||||
public APIResponse getPushSubscription() {
|
||||
PushSubscription pushSubscription = null;
|
||||
try {
|
||||
String response = new HttpsConnection(context, this.instance).get(getAbsoluteUrl("/push/subscription"), 10, null, prefKeyOauthTokenT);
|
||||
pushSubscription = parsePushNotifications(new JSONObject(response));
|
||||
} catch (HttpsConnection.HttpsConnectionException e) {
|
||||
setError(e.getStatusCode(), e);
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
apiResponse.setPushSubscription(pushSubscription);
|
||||
return apiResponse;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update subscribed push notifications
|
||||
*
|
||||
* @return APIResponse
|
||||
*/
|
||||
public APIResponse updatePushSubscription(String endpoint) {
|
||||
PushSubscription pushSubscription = new PushSubscription();
|
||||
final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||
boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true);
|
||||
boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true);
|
||||
boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true);
|
||||
boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true);
|
||||
boolean notif_status = sharedpreferences.getBoolean(Helper.SET_NOTIF_STATUS, true);
|
||||
boolean notif_poll = sharedpreferences.getBoolean(Helper.SET_NOTIF_POLL, true);
|
||||
|
||||
|
||||
HashMap<String, String> params = new HashMap<>();
|
||||
try {
|
||||
endpoint = URLEncoder.encode(endpoint, "UTF-8");
|
||||
} catch (UnsupportedEncodingException ignored) {
|
||||
}
|
||||
params.put("subscription[endpoint]", endpoint);
|
||||
params.put("data[alerts][follow]", String.valueOf(notif_follow));
|
||||
params.put("data[alerts][mention]", String.valueOf(notif_mention));
|
||||
params.put("data[alerts][favourite]", String.valueOf(notif_add));
|
||||
params.put("data[alerts][reblog]", String.valueOf(notif_share));
|
||||
params.put("data[alerts][poll]", String.valueOf(notif_poll));
|
||||
|
||||
try {
|
||||
|
||||
String response = new HttpsConnection(context, this.instance).put(getAbsoluteUrl("/push/subscription"), 10, params, prefKeyOauthTokenT);
|
||||
pushSubscription = parsePushNotifications(new JSONObject(response));
|
||||
} catch (HttpsConnection.HttpsConnectionException e) {
|
||||
setError(e.getStatusCode(), e);
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
apiResponse.setPushSubscription(pushSubscription);
|
||||
return apiResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to push notifications
|
||||
*
|
||||
* @return APIResponse
|
||||
*/
|
||||
public APIResponse pushSubscription(String endpoint, Account account) {
|
||||
PushSubscription pushSubscription = new PushSubscription();
|
||||
final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||
boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true);
|
||||
boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true);
|
||||
boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true);
|
||||
boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true);
|
||||
boolean notif_status = sharedpreferences.getBoolean(Helper.SET_NOTIF_STATUS, true);
|
||||
boolean notif_poll = sharedpreferences.getBoolean(Helper.SET_NOTIF_POLL, true);
|
||||
|
||||
SharedPreferences prefs = PreferenceManager
|
||||
.getDefaultSharedPreferences(context);
|
||||
String strPub = prefs.getString(kp_public, "");
|
||||
String strPriv = prefs.getString(kp_private, "");
|
||||
ECDH ecdh = ECDH.getInstance();
|
||||
if (strPub.trim().isEmpty() || strPriv.trim().isEmpty()) {
|
||||
ecdh.newPair(context);
|
||||
}
|
||||
String pubKey = ecdh.getPublicKey(context);
|
||||
byte[] randBytes = new byte[16];
|
||||
new Random().nextBytes(randBytes);
|
||||
String auth = ECDH.base64Encode(randBytes);
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
|
||||
try {
|
||||
JSONObject jsonObjectSub = new JSONObject();
|
||||
jsonObjectSub.put("endpoint", endpoint);
|
||||
JSONObject jsonObjectkey = new JSONObject();
|
||||
jsonObjectkey.put("p256dh", pubKey);
|
||||
jsonObjectkey.put("auth", auth);
|
||||
jsonObjectSub.put("keys", jsonObjectkey);
|
||||
jsonObject.put("subscription", jsonObjectSub);
|
||||
|
||||
JSONObject jsonObjectdata = new JSONObject();
|
||||
JSONObject jsonObjectarlerts = new JSONObject();
|
||||
jsonObjectarlerts.put("follow", notif_follow);
|
||||
jsonObjectarlerts.put("mention", notif_mention);
|
||||
jsonObjectarlerts.put("favourite", notif_add);
|
||||
jsonObjectarlerts.put("reblog", notif_share);
|
||||
jsonObjectarlerts.put("poll", notif_poll);
|
||||
jsonObjectdata.put("alerts", jsonObjectarlerts);
|
||||
jsonObject.put("data", jsonObjectdata);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
String response = new HttpsConnection(context, this.instance).postJson(getAbsoluteUrl("/push/subscription"), 10, jsonObject, prefKeyOauthTokenT);
|
||||
pushSubscription = parsePushNotifications(new JSONObject(response));
|
||||
ecdh.saveServerKey(context, account, pushSubscription.getServer_key());
|
||||
} catch (HttpsConnection.HttpsConnectionException e) {
|
||||
setError(e.getStatusCode(), e);
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
apiResponse.setPushSubscription(pushSubscription);
|
||||
return apiResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a list by its id
|
||||
*
|
||||
|
|
|
@ -35,6 +35,7 @@ import app.fedilab.android.client.Entities.PeertubeNotification;
|
|||
import app.fedilab.android.client.Entities.PixelFedStory;
|
||||
import app.fedilab.android.client.Entities.PixelFedStoryItem;
|
||||
import app.fedilab.android.client.Entities.Playlist;
|
||||
import app.fedilab.android.client.Entities.PushSubscription;
|
||||
import app.fedilab.android.client.Entities.Relationship;
|
||||
import app.fedilab.android.client.Entities.Report;
|
||||
import app.fedilab.android.client.Entities.Results;
|
||||
|
@ -56,6 +57,7 @@ public class APIResponse {
|
|||
private List<Notification> notifications = null;
|
||||
private List<Relationship> relationships = null;
|
||||
private List<Announcement> announcements = null;
|
||||
private PushSubscription pushSubscription;
|
||||
private String targetedId = null;
|
||||
private Results results = null;
|
||||
private List<HowToVideo> howToVideos = null;
|
||||
|
@ -85,6 +87,14 @@ public class APIResponse {
|
|||
return accounts;
|
||||
}
|
||||
|
||||
public PushSubscription getPushSubscription() {
|
||||
return pushSubscription;
|
||||
}
|
||||
|
||||
public void setPushSubscription(PushSubscription pushSubscription) {
|
||||
this.pushSubscription = pushSubscription;
|
||||
}
|
||||
|
||||
public void setAccounts(List<Account> accounts) {
|
||||
this.accounts = accounts;
|
||||
}
|
||||
|
|
|
@ -828,6 +828,37 @@ public class Account implements Parcelable {
|
|||
}
|
||||
}, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
matcher = Pattern.compile("gemini://([\\d\\w.-]*)(:\\d+)?(/\\S*)?").matcher(fieldSpan);
|
||||
while (matcher.find()) {
|
||||
URLSpan[] urls = fieldSpan.getSpans(0, fieldSpan.length(), URLSpan.class);
|
||||
for (URLSpan span : urls)
|
||||
fieldSpan.removeSpan(span);
|
||||
int matchStart = matcher.start(0);
|
||||
int matchEnd = matcher.end();
|
||||
final String url = fieldSpan.toString().substring(matchStart, matchEnd);
|
||||
if (matchStart >= 0 && matchEnd <= fieldSpan.toString().length() && matchEnd >= matchStart) {
|
||||
fieldSpan.setSpan(new ClickableSpan() {
|
||||
@Override
|
||||
public void onClick(@NonNull View textView) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
context.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Toasty.error(context, context.getString(R.string.toast_no_apps), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDrawState(@NonNull TextPaint ds) {
|
||||
super.updateDrawState(ds);
|
||||
ds.setUnderlineText(false);
|
||||
ds.setColor(link_color);
|
||||
}
|
||||
}, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
fieldsSpan.put(keySpan, fieldSpan);
|
||||
}
|
||||
|
||||
}
|
||||
if (accountsMentionUnknown.size() > 0) {
|
||||
for (Account accountMention : accountsMentionUnknown) {
|
||||
String targetedAccount = "@" + accountMention.getAcct();
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package app.fedilab.android.client.Entities;
|
||||
/* Copyright 2021 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Fedilab 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 Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
import java.util.HashMap;
|
||||
|
||||
public class PushSubscription {
|
||||
private String id;
|
||||
private String endpoint;
|
||||
private HashMap<String, Boolean> alerts;
|
||||
private String server_key;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getEndpoint() {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
public void setEndpoint(String endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
public HashMap<String, Boolean> getAlertsList() {
|
||||
return alerts;
|
||||
}
|
||||
|
||||
public void setAlertsList(HashMap<String, Boolean> alertsList) {
|
||||
this.alerts = alertsList;
|
||||
}
|
||||
|
||||
public String getServer_key() {
|
||||
return server_key;
|
||||
}
|
||||
|
||||
public void setServer_key(String server_key) {
|
||||
this.server_key = server_key;
|
||||
}
|
||||
|
||||
}
|
|
@ -665,9 +665,7 @@ public class Status implements Parcelable {
|
|||
intent.putExtras(b);
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://"))
|
||||
finalUrl = "http://" + url;
|
||||
Helper.openBrowser(context, finalUrl);
|
||||
Helper.openBrowser(context, url);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -891,10 +889,7 @@ public class Status implements Parcelable {
|
|||
contentSpanTranslated.setSpan(new ClickableSpan() {
|
||||
@Override
|
||||
public void onClick(@NonNull View textView) {
|
||||
String finalUrl = url;
|
||||
if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://"))
|
||||
finalUrl = "http://" + url;
|
||||
Helper.openBrowser(context, finalUrl);
|
||||
Helper.openBrowser(context, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -474,6 +474,63 @@ public class HttpsConnection {
|
|||
|
||||
}
|
||||
|
||||
|
||||
String postJson(String urlConnection, int timeout, JSONObject jsonObject, String token) throws IOException, NoSuchAlgorithmException, KeyManagementException, HttpsConnectionException {
|
||||
|
||||
URL url = new URL(urlConnection);
|
||||
byte[] postDataBytes;
|
||||
postDataBytes = jsonObject.toString().getBytes(StandardCharsets.UTF_8);
|
||||
if (proxy != null)
|
||||
httpURLConnection = (HttpURLConnection) url.openConnection(proxy);
|
||||
else
|
||||
httpURLConnection = (HttpURLConnection) url.openConnection();
|
||||
httpURLConnection.setRequestProperty("User-Agent", USER_AGENT);
|
||||
httpURLConnection.setConnectTimeout(timeout * 1000);
|
||||
httpURLConnection.setDoOutput(true);
|
||||
if (httpURLConnection instanceof HttpsURLConnection) {
|
||||
((HttpsURLConnection) httpURLConnection).setSSLSocketFactory(new TLSSocketFactory(this.instance));
|
||||
}
|
||||
httpURLConnection.setRequestProperty("Content-Type", "application/json");
|
||||
httpURLConnection.setRequestProperty("Accept", "application/json");
|
||||
httpURLConnection.setRequestMethod("POST");
|
||||
setToken(token);
|
||||
httpURLConnection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
|
||||
|
||||
|
||||
httpURLConnection.getOutputStream().write(postDataBytes);
|
||||
String response;
|
||||
if (httpURLConnection.getResponseCode() >= 200 && httpURLConnection.getResponseCode() < 400) {
|
||||
getSinceMaxId();
|
||||
response = converToString(httpURLConnection.getInputStream());
|
||||
} else {
|
||||
String error = null;
|
||||
if (httpURLConnection.getErrorStream() != null) {
|
||||
InputStream stream = httpURLConnection.getErrorStream();
|
||||
if (stream == null) {
|
||||
stream = httpURLConnection.getInputStream();
|
||||
}
|
||||
try (Scanner scanner = new Scanner(stream)) {
|
||||
scanner.useDelimiter("\\Z");
|
||||
if (scanner.hasNext()) {
|
||||
error = scanner.next();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
int responseCode = httpURLConnection.getResponseCode();
|
||||
try {
|
||||
httpURLConnection.getInputStream().close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
throw new HttpsConnectionException(responseCode, error);
|
||||
}
|
||||
getSinceMaxId();
|
||||
httpURLConnection.getInputStream().close();
|
||||
return response;
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
String postMisskey(String urlConnection, int timeout, JSONObject paramaters, String token) throws IOException, NoSuchAlgorithmException, KeyManagementException, HttpsConnectionException {
|
||||
URL url = new URL(urlConnection);
|
||||
|
|
|
@ -1660,8 +1660,8 @@ public class PeertubeAPI {
|
|||
String errorM = jsonObject.get("error").toString();
|
||||
message = "Error " + statusCode + " : " + errorM;
|
||||
} catch (JSONException e) {
|
||||
if (error.getMessage().split(".").length > 0) {
|
||||
String errorM = error.getMessage().split(".")[0];
|
||||
if (error.getMessage().split("\\.").length > 0) {
|
||||
String errorM = error.getMessage().split("\\.")[0];
|
||||
message = "Error " + statusCode + " : " + errorM;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ import java.util.List;
|
|||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.client.Entities.Account;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.services.LiveNotificationDelayedService;
|
||||
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
|
||||
|
@ -72,12 +71,6 @@ public class AccountLiveAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
|
|||
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||
editor.putBoolean(Helper.SET_ALLOW_STREAM + accounts.get(i).getId() + accounts.get(i).getInstance(), holder.account_acct_live_notifications.isChecked());
|
||||
editor.apply();
|
||||
if (holder.account_acct_live_notifications.isChecked()) {
|
||||
LiveNotificationDelayedService.totalAccount++;
|
||||
} else {
|
||||
LiveNotificationDelayedService.totalAccount--;
|
||||
}
|
||||
Helper.startStreaming(context);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -632,18 +632,36 @@ public abstract class BaseStatusListAdapter extends RecyclerView.Adapter<Recycle
|
|||
String v = status.getVisibility();
|
||||
holder.status_privacy.setContentDescription(context.getString(R.string.toot_visibility_tilte) + ": " + v);
|
||||
});
|
||||
|
||||
holder.decoration_container.removeAllViews();
|
||||
if (holder.decoration_container != null) {
|
||||
holder.decoration_container.removeAllViews();
|
||||
}
|
||||
if (type == RetrieveFeedsAsyncTask.Type.CONTEXT && holder.decoration_container != null && status.getIn_reply_to_id() != null) {
|
||||
|
||||
int ident = CommentDecorationHelper.getIndentation(status.getIn_reply_to_id(), statuses);
|
||||
for (int j = 0; j <= ident; j++) {
|
||||
View view = new View(context);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(1, LinearLayout.LayoutParams.MATCH_PARENT);
|
||||
params.setMargins((int) Helper.convertDpToPixel(5, context), 0, 0, 0);
|
||||
params.setMargins((int) Helper.convertDpToPixel(2, context), 0, 0, 0);
|
||||
view.setLayoutParams(params);
|
||||
holder.decoration_container.addView(view, 0);
|
||||
view.setBackgroundResource(R.color.cyanea_accent_reference);
|
||||
int colorPosition = (ident - j) % 7 + 1;
|
||||
int color = R.color.cyanea_accent_reference;
|
||||
if (colorPosition == 1) {
|
||||
color = R.color.rainbow_1;
|
||||
} else if (colorPosition == 2) {
|
||||
color = R.color.rainbow_2;
|
||||
} else if (colorPosition == 3) {
|
||||
color = R.color.rainbow_3;
|
||||
} else if (colorPosition == 4) {
|
||||
color = R.color.rainbow_4;
|
||||
} else if (colorPosition == 5) {
|
||||
color = R.color.rainbow_5;
|
||||
} else if (colorPosition == 6) {
|
||||
color = R.color.rainbow_6;
|
||||
} else if (colorPosition == 7) {
|
||||
color = R.color.rainbow_7;
|
||||
}
|
||||
view.setBackgroundResource(color);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,6 @@ package app.fedilab.android.fragments;
|
|||
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.TimePickerDialog;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentUris;
|
||||
|
@ -29,7 +27,6 @@ import android.database.Cursor;
|
|||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
|
@ -74,7 +71,6 @@ import java.util.Set;
|
|||
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.activities.BaseMainActivity;
|
||||
import app.fedilab.android.activities.LiveNotificationSettingsAccountsActivity;
|
||||
import app.fedilab.android.activities.MainActivity;
|
||||
import app.fedilab.android.asynctasks.RetrieveRelationshipAsyncTask;
|
||||
import app.fedilab.android.asynctasks.RetrieveRemoteDataAsyncTask;
|
||||
|
@ -88,14 +84,10 @@ import app.fedilab.android.drawers.AccountSearchDevAdapter;
|
|||
import app.fedilab.android.filelister.FileListerDialog;
|
||||
import app.fedilab.android.helper.ExpandableHeightListView;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.helper.PushHelper;
|
||||
import app.fedilab.android.interfaces.OnRetrieveRelationshipInterface;
|
||||
import app.fedilab.android.interfaces.OnRetrieveRemoteAccountInterface;
|
||||
import app.fedilab.android.jobs.ApplicationJob;
|
||||
import app.fedilab.android.jobs.NotificationsSyncJob;
|
||||
import app.fedilab.android.services.DownloadTrackingDBScriptsService;
|
||||
import app.fedilab.android.services.LiveNotificationDelayedService;
|
||||
import app.fedilab.android.services.StopDelayedNotificationReceiver;
|
||||
import app.fedilab.android.services.StopLiveNotificationReceiver;
|
||||
import app.fedilab.android.sqlite.AccountDAO;
|
||||
import app.fedilab.android.sqlite.MainMenuDAO;
|
||||
import app.fedilab.android.sqlite.Sqlite;
|
||||
|
@ -637,23 +629,14 @@ public class ContentSettingsFragment extends Fragment implements OnRetrieveRemot
|
|||
final Button sound_media = rootView.findViewById(R.id.sound_media);
|
||||
final Button sound_status = rootView.findViewById(R.id.sound_status);
|
||||
|
||||
final ImageButton set_hide_status_bar = rootView.findViewById(R.id.set_hide_status_bar);
|
||||
|
||||
Button set_notif_sound = rootView.findViewById(R.id.set_notif_sound);
|
||||
settings_time_from.setText(time_from);
|
||||
settings_time_to.setText(time_to);
|
||||
final LinearLayout set_hide_status_bar_container = rootView.findViewById(R.id.set_hide_status_bar_container);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
set_notif_sound.setVisibility(View.GONE);
|
||||
channels_container.setVisibility(View.VISIBLE);
|
||||
set_hide_status_bar_container.setVisibility(View.VISIBLE);
|
||||
set_hide_status_bar.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
|
||||
intent.putExtra(Settings.EXTRA_CHANNEL_ID, LiveNotificationDelayedService.CHANNEL_ID);
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
|
||||
sound_boost.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
|
||||
|
@ -923,36 +906,6 @@ public class ContentSettingsFragment extends Fragment implements OnRetrieveRemot
|
|||
}
|
||||
}
|
||||
|
||||
boolean allow_live_notifications = sharedpreferences.getBoolean(Helper.SET_ALLOW_STREAM + userId + instance, true);
|
||||
TextView set_allow_live_notifications_title = rootView.findViewById(R.id.set_allow_live_notifications_title);
|
||||
if (account != null) {
|
||||
set_allow_live_notifications_title.setText(context.getString(R.string.set_allow_live_notifications, account.getAcct() + "@" + account.getInstance()));
|
||||
}
|
||||
final SwitchCompat set_allow_live_notifications = rootView.findViewById(R.id.set_allow_live_notifications);
|
||||
set_allow_live_notifications.setChecked(allow_live_notifications);
|
||||
set_allow_live_notifications.setOnClickListener(v -> {
|
||||
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||
editor.putBoolean(Helper.SET_ALLOW_STREAM + userId + instance, set_allow_live_notifications.isChecked());
|
||||
editor.apply();
|
||||
if (set_allow_live_notifications.isChecked()) {
|
||||
LiveNotificationDelayedService.totalAccount++;
|
||||
} else {
|
||||
LiveNotificationDelayedService.totalAccount--;
|
||||
}
|
||||
if (set_allow_live_notifications.isChecked()) {
|
||||
LiveNotificationDelayedService.totalAccount++;
|
||||
} else {
|
||||
LiveNotificationDelayedService.totalAccount--;
|
||||
}
|
||||
Helper.startStreaming(context);
|
||||
|
||||
});
|
||||
|
||||
final ImageButton set_allow_live_notifications_others = rootView.findViewById(R.id.set_allow_live_notifications_others);
|
||||
set_allow_live_notifications_others.setOnClickListener(view -> {
|
||||
Intent intent = new Intent(context, LiveNotificationSettingsAccountsActivity.class);
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
|
||||
boolean notify = sharedpreferences.getBoolean(Helper.SET_NOTIFY, true);
|
||||
|
@ -971,35 +924,22 @@ public class ContentSettingsFragment extends Fragment implements OnRetrieveRemot
|
|||
editor.apply();
|
||||
if (isChecked) {
|
||||
notification_container.setVisibility(View.VISIBLE);
|
||||
notification_container.setVisibility(View.VISIBLE);
|
||||
Helper.startStreaming(context);
|
||||
} else {
|
||||
context.sendBroadcast(new Intent(context, StopLiveNotificationReceiver.class));
|
||||
context.sendBroadcast(new Intent(context, StopDelayedNotificationReceiver.class));
|
||||
ApplicationJob.cancelAllJob(NotificationsSyncJob.NOTIFICATION_REFRESH);
|
||||
notification_container.setVisibility(View.GONE);
|
||||
}
|
||||
PushHelper.startStreaming(context);
|
||||
});
|
||||
|
||||
|
||||
//Live notification mode
|
||||
final Spinner set_live_type = rootView.findViewById(R.id.set_live_type);
|
||||
String[] labels = {context.getString(R.string.live_notif), context.getString(R.string.live_delayed), context.getString(R.string.no_live_notif)};
|
||||
ArrayAdapter<String> adapterLive = new ArrayAdapter<>(Objects.requireNonNull(getActivity()),
|
||||
android.R.layout.simple_spinner_dropdown_item, labels);
|
||||
String[] labels = {context.getString(R.string.push_notif), context.getString(R.string.no_live_notif)};
|
||||
|
||||
|
||||
LinearLayout live_notif_per_account = rootView.findViewById(R.id.live_notif_per_account);
|
||||
set_live_type.setAdapter(adapterLive);
|
||||
if (Helper.liveNotifType(context) == Helper.NOTIF_NONE) {
|
||||
live_notif_per_account.setVisibility(View.GONE);
|
||||
}
|
||||
TextView set_live_type_indication = rootView.findViewById(R.id.set_live_type_indication);
|
||||
switch (Helper.liveNotifType(context)) {
|
||||
case Helper.NOTIF_LIVE:
|
||||
set_live_type_indication.setText(R.string.live_notif_indication);
|
||||
break;
|
||||
case Helper.NOTIF_DELAYED:
|
||||
set_live_type_indication.setText(R.string.set_live_type_indication);
|
||||
case Helper.NOTIF_PUSH:
|
||||
set_live_type_indication.setText(R.string.set_push_notifications);
|
||||
break;
|
||||
case Helper.NOTIF_NONE:
|
||||
set_live_type_indication.setText(R.string.no_live_indication);
|
||||
|
@ -1008,40 +948,25 @@ public class ContentSettingsFragment extends Fragment implements OnRetrieveRemot
|
|||
set_live_type.setSelection(Helper.liveNotifType(context));
|
||||
liveNotificationCount = 0;
|
||||
set_live_type.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@SuppressLint("ApplySharedPref")
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (liveNotificationCount > 0) {
|
||||
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||
|
||||
context.sendBroadcast(new Intent(context, StopLiveNotificationReceiver.class));
|
||||
context.sendBroadcast(new Intent(context, StopDelayedNotificationReceiver.class));
|
||||
ApplicationJob.cancelAllJob(NotificationsSyncJob.NOTIFICATION_REFRESH);
|
||||
switch (position) {
|
||||
case Helper.NOTIF_LIVE:
|
||||
editor.putBoolean(Helper.SET_LIVE_NOTIFICATIONS, true);
|
||||
editor.putBoolean(Helper.SET_DELAYED_NOTIFICATIONS, false);
|
||||
live_notif_per_account.setVisibility(View.VISIBLE);
|
||||
case Helper.NOTIF_PUSH:
|
||||
editor.putBoolean(Helper.SET_PUSH_NOTIFICATIONS, true);
|
||||
editor.commit();
|
||||
set_live_type_indication.setText(R.string.live_notif_indication);
|
||||
Helper.startStreaming(context);
|
||||
break;
|
||||
case Helper.NOTIF_DELAYED:
|
||||
editor.putBoolean(Helper.SET_LIVE_NOTIFICATIONS, false);
|
||||
editor.putBoolean(Helper.SET_DELAYED_NOTIFICATIONS, true);
|
||||
live_notif_per_account.setVisibility(View.VISIBLE);
|
||||
set_live_type_indication.setText(R.string.set_live_type_indication);
|
||||
editor.commit();
|
||||
Helper.startStreaming(context);
|
||||
set_live_type_indication.setText(R.string.set_push_notifications);
|
||||
|
||||
break;
|
||||
case Helper.NOTIF_NONE:
|
||||
editor.putBoolean(Helper.SET_LIVE_NOTIFICATIONS, false);
|
||||
editor.putBoolean(Helper.SET_DELAYED_NOTIFICATIONS, false);
|
||||
editor.putBoolean(Helper.SET_PUSH_NOTIFICATIONS, false);
|
||||
editor.commit();
|
||||
set_live_type_indication.setText(R.string.no_live_indication);
|
||||
live_notif_per_account.setVisibility(View.GONE);
|
||||
NotificationsSyncJob.schedule(false);
|
||||
break;
|
||||
}
|
||||
PushHelper.startStreaming(context);
|
||||
} else {
|
||||
liveNotificationCount++;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
@ -55,12 +54,10 @@ import app.fedilab.android.drawers.NotificationsListAdapter;
|
|||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.interfaces.OnRetrieveMissingNotificationsInterface;
|
||||
import app.fedilab.android.interfaces.OnRetrieveNotificationsInterface;
|
||||
import app.fedilab.android.services.LiveNotificationDelayedService;
|
||||
import app.fedilab.android.sqlite.AccountDAO;
|
||||
import app.fedilab.android.sqlite.Sqlite;
|
||||
|
||||
import es.dmoral.toasty.Toasty;
|
||||
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
|
||||
import static app.fedilab.android.activities.BaseMainActivity.countNewNotifications;
|
||||
|
||||
|
||||
|
@ -314,12 +311,6 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve
|
|||
if (type == Type.ALL) {
|
||||
if (lastReadNotifications != null && notifications.get(0).getId().compareTo(lastReadNotifications) > 0) {
|
||||
countNewNotifications++;
|
||||
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
|
||||
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
|
||||
String instance = sharedpreferences.getString(Helper.PREF_INSTANCE, Helper.getLiveInstance(context));
|
||||
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||
Account accountdb = new AccountDAO(context, db).getUniqAccount(userId, instance);
|
||||
LiveNotificationDelayedService.since_ids.put(accountdb.getAcct() + "@" + accountdb.getInstance(), notifications.get(0).getId());
|
||||
}
|
||||
}
|
||||
for (Notification tmpNotification : notifications) {
|
||||
|
@ -446,14 +437,6 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve
|
|||
MainActivity.lastNotificationId = notifications.get(0).getId();
|
||||
updateNotificationLastId(notifications.get(0).getId());
|
||||
}
|
||||
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
|
||||
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
|
||||
String instance = sharedpreferences.getString(Helper.PREF_INSTANCE, Helper.getLiveInstance(context));
|
||||
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||
Account account = new AccountDAO(context, db).getUniqAccount(userId, instance);
|
||||
if (MainActivity.lastNotificationId != null && notifications.get(0).getId().compareTo(MainActivity.lastNotificationId) > 0) {
|
||||
LiveNotificationDelayedService.since_ids.put(account.getAcct() + "@" + account.getInstance(), notifications.get(0).getId());
|
||||
}
|
||||
}
|
||||
if (textviewNoAction.getVisibility() == View.VISIBLE) {
|
||||
textviewNoAction.setVisibility(View.GONE);
|
||||
|
|
|
@ -96,6 +96,9 @@ import app.fedilab.android.sqlite.SearchDAO;
|
|||
import app.fedilab.android.sqlite.Sqlite;
|
||||
import es.dmoral.toasty.Toasty;
|
||||
|
||||
import static app.fedilab.android.helper.BaseHelper.NOTIF_PUSH;
|
||||
import static app.fedilab.android.helper.BaseHelper.liveNotifType;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Thomas on 24/04/2017.
|
||||
|
@ -756,7 +759,7 @@ public class DisplayStatusFragment extends Fragment implements OnPostActionInter
|
|||
public void onResume() {
|
||||
super.onResume();
|
||||
swipeRefreshLayout.setEnabled(true);
|
||||
boolean liveNotifications = sharedpreferences.getBoolean(Helper.SET_LIVE_NOTIFICATIONS, true);
|
||||
boolean liveNotifications = liveNotifType(context) == NOTIF_PUSH;
|
||||
if (type == RetrieveFeedsAsyncTask.Type.HOME || type == RetrieveFeedsAsyncTask.Type.PF_HOME) {
|
||||
if (this.isVisible()) {
|
||||
if (statuses != null && statuses.size() > 0) {
|
||||
|
@ -861,7 +864,7 @@ public class DisplayStatusFragment extends Fragment implements OnPostActionInter
|
|||
super.setMenuVisibility(visible);
|
||||
if (context == null)
|
||||
return;
|
||||
int liveNotifications = Helper.liveNotifType(context);
|
||||
int liveNotifications = liveNotifType(context);
|
||||
//Store last toot id for home timeline to avoid to notify for those that have been already seen
|
||||
if (type == RetrieveFeedsAsyncTask.Type.HOME || type == RetrieveFeedsAsyncTask.Type.PF_HOME) {
|
||||
if (visible) {
|
||||
|
|
|
@ -211,8 +211,8 @@ import app.fedilab.android.client.Entities.Status;
|
|||
import app.fedilab.android.client.Entities.Tag;
|
||||
import app.fedilab.android.client.Entities.TagTimeline;
|
||||
import app.fedilab.android.client.Tls12SocketFactory;
|
||||
import app.fedilab.android.services.LiveNotificationDelayedService;
|
||||
import app.fedilab.android.services.LiveNotificationService;
|
||||
import app.fedilab.android.jobs.ApplicationJob;
|
||||
import app.fedilab.android.jobs.NotificationsSyncJob;
|
||||
import app.fedilab.android.sqlite.AccountDAO;
|
||||
import app.fedilab.android.sqlite.MainMenuDAO;
|
||||
import app.fedilab.android.sqlite.Sqlite;
|
||||
|
@ -239,6 +239,7 @@ import static app.fedilab.android.helper.ThemeHelper.getAttColor;
|
|||
import static app.fedilab.android.sqlite.StatusCacheDAO.ARCHIVE_CACHE;
|
||||
import static app.fedilab.android.sqlite.StatusCacheDAO.BOOKMARK_CACHE;
|
||||
import static app.fedilab.android.webview.ProxyHelper.setProxy;
|
||||
import static cafe.adriel.androidaudiorecorder.Util.formatSeconds;
|
||||
import static com.koushikdutta.async.util.StreamUtility.copyStream;
|
||||
|
||||
|
||||
|
@ -254,8 +255,8 @@ public class BaseHelper {
|
|||
|
||||
public static final String TAG = "mastodon_etalab";
|
||||
public static final String CLIENT_NAME_VALUE = "Fedilab";
|
||||
public static final String OAUTH_SCOPES = "read write follow";
|
||||
public static final String OAUTH_SCOPES_ADMIN = "read write follow admin:read admin:write admin";
|
||||
public static final String OAUTH_SCOPES = "read write follow push";
|
||||
public static final String OAUTH_SCOPES_ADMIN = "read write follow push admin:read admin:write admin";
|
||||
public static final String OAUTH_SCOPES_PEERTUBE = "user";
|
||||
public static final String PREF_KEY_OAUTH_TOKEN = "oauth_token";
|
||||
|
||||
|
@ -277,6 +278,8 @@ public class BaseHelper {
|
|||
//Some definitions
|
||||
public static final String CLIENT_NAME = "client_name";
|
||||
public static final String APP_PREFS = "app_prefs";
|
||||
public static final String SERVER_KEY = "server_key";
|
||||
public static final String SERVER_ENDPOINT = "server_endpoint";
|
||||
public static final String ID = "id";
|
||||
public static final String CLIENT_ID = "client_id";
|
||||
public static final String CLIENT_SECRET = "client_secret";
|
||||
|
@ -334,7 +337,7 @@ public class BaseHelper {
|
|||
public static final String SET_TIME_FROM = "set_time_from";
|
||||
public static final String SET_TIME_TO = "set_time_to";
|
||||
public static final String SET_AUTO_STORE = "set_auto_store";
|
||||
public static final String SET_POPUP_PUSH = "set_popup_push_new";
|
||||
public static final String SET_POPUP_PUSH = "set_popup_push_new_push";
|
||||
public static final String SET_POPUP_RELEASE_NOTES = "set_popup_push_release_notes";
|
||||
public static final String SET_NSFW_TIMEOUT = "set_nsfw_timeout";
|
||||
public static final String SET_MED_DESC_TIMEOUT = "set_med_desc_timeout";
|
||||
|
@ -346,8 +349,7 @@ public class BaseHelper {
|
|||
public static final String SET_SHOW_BOOSTS = "set_show_boost";
|
||||
public static final String SET_SHOW_REPLIES = "set_show_replies";
|
||||
public static final String SET_VIDEO_NSFW = "set_video_nsfw";
|
||||
public static final String SET_LIVE_NOTIFICATIONS = "set_live_notifications";
|
||||
public static final String SET_DELAYED_NOTIFICATIONS = "set_delayed_notifications";
|
||||
public static final String SET_PUSH_NOTIFICATIONS = "set_push_notifications";
|
||||
public static final String SET_DISABLE_GIF = "set_disable_gif";
|
||||
public static final String SET_DISABLE_ANIMATED_EMOJI = "set_disable_animated_emoji";
|
||||
public static final String SET_CAPITALIZE = "set_capitalize";
|
||||
|
@ -404,8 +406,7 @@ public class BaseHelper {
|
|||
public static final int THEME_DARK = 2;
|
||||
public static final int THEME_BLACK = 3;
|
||||
|
||||
public static final int NOTIF_LIVE = 0;
|
||||
public static final int NOTIF_DELAYED = 1;
|
||||
public static final int NOTIF_PUSH = 3;
|
||||
public static final int NOTIF_NONE = 2;
|
||||
|
||||
public static final int LED_COLOUR = 0;
|
||||
|
@ -587,12 +588,9 @@ public class BaseHelper {
|
|||
|
||||
public static int liveNotifType(Context context) {
|
||||
SharedPreferences sharedpreferences = context.getSharedPreferences(APP_PREFS, Context.MODE_PRIVATE);
|
||||
boolean live = sharedpreferences.getBoolean(SET_LIVE_NOTIFICATIONS, false);
|
||||
boolean delayed = sharedpreferences.getBoolean(SET_DELAYED_NOTIFICATIONS, true);
|
||||
if (delayed) {
|
||||
return NOTIF_DELAYED;
|
||||
} else if (live) {
|
||||
return NOTIF_LIVE;
|
||||
boolean push = sharedpreferences.getBoolean(SET_PUSH_NOTIFICATIONS, false);
|
||||
if (push) {
|
||||
return NOTIF_PUSH;
|
||||
} else {
|
||||
return NOTIF_NONE;
|
||||
}
|
||||
|
@ -3166,7 +3164,7 @@ public class BaseHelper {
|
|||
public static void openBrowser(Context context, String url) {
|
||||
SharedPreferences sharedpreferences = context.getSharedPreferences(APP_PREFS, android.content.Context.MODE_PRIVATE);
|
||||
boolean embedded_browser = sharedpreferences.getBoolean(SET_EMBEDDED_BROWSER, true);
|
||||
if (embedded_browser) {
|
||||
if (embedded_browser && !url.toLowerCase().startsWith("gemini://")) {
|
||||
Intent intent = new Intent(context, WebviewActivity.class);
|
||||
Bundle b = new Bundle();
|
||||
String finalUrl = url;
|
||||
|
@ -4329,29 +4327,6 @@ public class BaseHelper {
|
|||
|
||||
}
|
||||
|
||||
public static void startStreaming(Context context) {
|
||||
int liveNotifications = liveNotifType(context);
|
||||
Intent streamingIntent = null;
|
||||
switch (liveNotifications) {
|
||||
case NOTIF_LIVE:
|
||||
streamingIntent = new Intent(context, LiveNotificationService.class);
|
||||
break;
|
||||
case NOTIF_DELAYED:
|
||||
streamingIntent = new Intent(context, LiveNotificationDelayedService.class);
|
||||
break;
|
||||
}
|
||||
if (streamingIntent != null) {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
context.startForegroundService(streamingIntent);
|
||||
} else {
|
||||
context.startService(streamingIntent);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
context.startService(streamingIntent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean canFetchList(Context context, Account account) {
|
||||
SharedPreferences sharedpreferences = context.getSharedPreferences(APP_PREFS, Context.MODE_PRIVATE);
|
||||
|
|
|
@ -0,0 +1,298 @@
|
|||
package app.fedilab.android.helper;
|
||||
/* Copyright 2021 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Fedilab 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 Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.util.Base64;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import org.spongycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.spongycastle.asn1.x9.ECNamedCurveTable;
|
||||
import org.spongycastle.asn1.x9.X9ECParameters;
|
||||
import org.spongycastle.crypto.params.ECNamedDomainParameters;
|
||||
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.spongycastle.jce.spec.ECNamedCurveSpec;
|
||||
import org.spongycastle.jce.spec.ECParameterSpec;
|
||||
import org.spongycastle.jce.spec.ECPrivateKeySpec;
|
||||
import org.spongycastle.jce.spec.ECPublicKeySpec;
|
||||
import org.spongycastle.math.ec.ECCurve;
|
||||
import org.spongycastle.math.ec.ECPoint;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Security;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.KeyAgreement;
|
||||
|
||||
|
||||
import app.fedilab.android.client.Entities.Account;
|
||||
import app.fedilab.android.sqlite.AccountDAO;
|
||||
import app.fedilab.android.sqlite.Sqlite;
|
||||
|
||||
|
||||
|
||||
public class ECDH {
|
||||
|
||||
|
||||
public static final String kp_public = "kp_public";
|
||||
public static final String peer_public = "peer_public";
|
||||
public static final String PROVIDER = org.spongycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;
|
||||
|
||||
public static final String kp_private = "kp_private";
|
||||
public static final String KEGEN_ALG = "ECDH";
|
||||
|
||||
public static final String name = "prime256v1";
|
||||
|
||||
private static final String kp_public_affine_x = "kp_public_affine_x";
|
||||
private static final String kp_public_affine_y = "kp_public_affine_y";
|
||||
|
||||
private static ECDH instance;
|
||||
|
||||
static {
|
||||
Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider());
|
||||
}
|
||||
|
||||
public final KeyFactory kf;
|
||||
private final KeyPairGenerator kpg;
|
||||
|
||||
|
||||
public ECDH() {
|
||||
try {
|
||||
kf = KeyFactory.getInstance(KEGEN_ALG, PROVIDER);
|
||||
kpg = KeyPairGenerator.getInstance(KEGEN_ALG, PROVIDER);
|
||||
|
||||
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized ECDH getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new ECDH();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static String base64Encode(byte[] b) {
|
||||
|
||||
byte[] encoded = Base64.encode(
|
||||
b, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
|
||||
return new String(encoded);
|
||||
}
|
||||
|
||||
static byte[] base64Decode(String str) {
|
||||
return Base64.decode(str, Base64.DEFAULT);
|
||||
}
|
||||
|
||||
synchronized KeyPair generateKeyPair()
|
||||
throws Exception {
|
||||
ECGenParameterSpec ecParamSpec = new ECGenParameterSpec(name);
|
||||
kpg.initialize(ecParamSpec);
|
||||
|
||||
return kpg.generateKeyPair();
|
||||
}
|
||||
|
||||
private byte[] generateSecret(PrivateKey myPrivKey, PublicKey otherPubKey) throws Exception {
|
||||
KeyAgreement keyAgreement = KeyAgreement.getInstance(KEGEN_ALG);
|
||||
keyAgreement.init(myPrivKey);
|
||||
keyAgreement.doPhase(otherPubKey, true);
|
||||
|
||||
return keyAgreement.generateSecret();
|
||||
}
|
||||
|
||||
|
||||
synchronized KeyPair readKeyPair(Context context)
|
||||
throws Exception {
|
||||
return new KeyPair(readMyPublicKey(context), readMyPrivateKey(context));
|
||||
}
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
public KeyPair newPair(Context context) {
|
||||
SharedPreferences.Editor prefsEditor = PreferenceManager
|
||||
.getDefaultSharedPreferences(context).edit();
|
||||
KeyPair kp;
|
||||
try {
|
||||
kp = generateKeyPair();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
ECPublicKey key = (ECPublicKey) kp.getPublic();
|
||||
byte[] x = key.getW().getAffineX().toByteArray();
|
||||
byte[] y = key.getW().getAffineY().toByteArray();
|
||||
BigInteger xbi = new BigInteger(1, x);
|
||||
BigInteger ybi = new BigInteger(1, y);
|
||||
X9ECParameters x9 = ECNamedCurveTable.getByName(name);
|
||||
ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID(name);
|
||||
|
||||
ECCurve curve = x9.getCurve();
|
||||
ECPoint point = curve.createPoint(xbi, ybi);
|
||||
ECNamedDomainParameters dParams = new ECNamedDomainParameters(oid,
|
||||
x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
|
||||
|
||||
ECPublicKeyParameters pubKey = new ECPublicKeyParameters(point, dParams);
|
||||
|
||||
|
||||
ECPrivateKeyParameters privateKey = new ECPrivateKeyParameters(new BigInteger(kp.getPrivate().getEncoded()), pubKey.getParameters());
|
||||
byte[] privateKeyBytes = privateKey.getD().toByteArray();
|
||||
|
||||
String keyString = base64Encode(pubKey.getQ().getEncoded(false));
|
||||
String keypString = base64Encode(privateKeyBytes);
|
||||
prefsEditor.putString(kp_public, keyString);
|
||||
prefsEditor.putString(kp_public_affine_x, key.getW().getAffineX().toString());
|
||||
prefsEditor.putString(kp_public_affine_y, key.getW().getAffineY().toString());
|
||||
prefsEditor.putString(kp_private, keypString);
|
||||
prefsEditor.commit();
|
||||
return kp;
|
||||
}
|
||||
|
||||
|
||||
synchronized PublicKey readMyPublicKey(Context context) throws Exception {
|
||||
|
||||
X9ECParameters x9 = ECNamedCurveTable.getByName(name);
|
||||
ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID(name);
|
||||
|
||||
SharedPreferences prefs = PreferenceManager
|
||||
.getDefaultSharedPreferences(context);
|
||||
BigInteger xbi = new BigInteger(prefs.getString(kp_public_affine_x, "0"));
|
||||
BigInteger ybi = new BigInteger(prefs.getString(kp_public_affine_y, "0"));
|
||||
|
||||
ECNamedDomainParameters dParams = new ECNamedDomainParameters(oid,
|
||||
x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
|
||||
|
||||
|
||||
ECNamedCurveSpec ecNamedCurveSpec = new ECNamedCurveSpec(name, dParams.getCurve(), dParams.getG(), dParams.getN());
|
||||
java.security.spec.ECPoint w = new java.security.spec.ECPoint(xbi, ybi);
|
||||
return kf.generatePublic(new java.security.spec.ECPublicKeySpec(w, ecNamedCurveSpec));
|
||||
}
|
||||
|
||||
|
||||
public String uncryptMessage(Context context, String cyphered, String slug) {
|
||||
getInstance();
|
||||
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||
String[] slugArray = slug.split("@");
|
||||
Account account = new AccountDAO(context, db).getUniqAccountUsernameInstance(slugArray[0], slugArray[1]);
|
||||
byte[] privateKey = getSharedSecret(context, account);
|
||||
try {
|
||||
Cipher outCipher = Cipher.getInstance("ECIES", PROVIDER);
|
||||
// outCipher.init(Cipher.DECRYPT_MODE, readPrivateKey(privateKey));
|
||||
outCipher.init(Cipher.DECRYPT_MODE, readPrivateKey(privateKey));
|
||||
byte[] plaintext = outCipher.doFinal(Base64.decode(cyphered, Base64.DEFAULT));
|
||||
String finalText = new String(plaintext);
|
||||
return finalText;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "";
|
||||
|
||||
}
|
||||
|
||||
|
||||
public PublicKey readPublicKey(String keyStr) throws Exception {
|
||||
ECParameterSpec parameterSpec = org.spongycastle.jce.ECNamedCurveTable.getParameterSpec(name);
|
||||
ECCurve curve = parameterSpec.getCurve();
|
||||
ECPoint point = curve.decodePoint(base64Decode(keyStr));
|
||||
ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, parameterSpec);
|
||||
return kf.generatePublic(pubSpec);
|
||||
}
|
||||
|
||||
|
||||
public PrivateKey readPrivateKey(byte[] key) throws Exception {
|
||||
ECParameterSpec parameterSpec = org.spongycastle.jce.ECNamedCurveTable.getParameterSpec(name);
|
||||
ECPrivateKeySpec pubSpec = new ECPrivateKeySpec(new BigInteger(1, key), parameterSpec);
|
||||
return kf.generatePrivate(pubSpec);
|
||||
}
|
||||
|
||||
synchronized PrivateKey readMyPrivateKey(Context context) throws Exception {
|
||||
X9ECParameters x9 = ECNamedCurveTable.getByName(name);
|
||||
ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID(name);
|
||||
|
||||
SharedPreferences prefs = PreferenceManager
|
||||
.getDefaultSharedPreferences(context);
|
||||
BigInteger ybi = new BigInteger(prefs.getString(kp_public_affine_y, "0"));
|
||||
ECNamedDomainParameters dParams = new ECNamedDomainParameters(oid,
|
||||
x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
|
||||
ECNamedCurveSpec ecNamedCurveSpec = new ECNamedCurveSpec(name, dParams.getCurve(), dParams.getG(), dParams.getN());
|
||||
return kf.generatePrivate(new java.security.spec.ECPrivateKeySpec(ybi, ecNamedCurveSpec));
|
||||
}
|
||||
|
||||
|
||||
private synchronized KeyPair getPair(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager
|
||||
.getDefaultSharedPreferences(context);
|
||||
String strPub = prefs.getString(kp_public, "");
|
||||
String strPriv = prefs.getString(kp_private, "");
|
||||
if (strPub.trim().isEmpty() || strPriv.trim().isEmpty()) {
|
||||
return newPair(context);
|
||||
}
|
||||
try {
|
||||
return readKeyPair(context);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
PublicKey getServerKey(Context context, Account account) throws Exception {
|
||||
SharedPreferences prefs = PreferenceManager
|
||||
.getDefaultSharedPreferences(context);
|
||||
String serverKey = prefs.getString(peer_public + account.getId() + account.getInstance(), "");
|
||||
return readPublicKey(serverKey);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unused", "RedundantSuppression"})
|
||||
public byte[] getSharedSecret(Context context, Account account) {
|
||||
try {
|
||||
KeyPair keyPair = getPair(context);
|
||||
if (keyPair != null) {
|
||||
return generateSecret(keyPair.getPrivate(), getServerKey(context, account));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getPublicKey(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager
|
||||
.getDefaultSharedPreferences(context);
|
||||
|
||||
return prefs.getString(kp_public, "");
|
||||
}
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
public void saveServerKey(Context context, Account account, String strPeerPublic) {
|
||||
SharedPreferences.Editor prefsEditor = PreferenceManager
|
||||
.getDefaultSharedPreferences(context).edit();
|
||||
|
||||
prefsEditor.putString(peer_public + account.getId() + account.getInstance(), strPeerPublic);
|
||||
prefsEditor.commit();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,311 @@
|
|||
package app.fedilab.android.helper;
|
||||
/* Copyright 2021 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Fedilab 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 Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.Html;
|
||||
import android.text.SpannableString;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.DataSource;
|
||||
import com.bumptech.glide.load.engine.GlideException;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.target.CustomTarget;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.activities.MainActivity;
|
||||
import app.fedilab.android.client.API;
|
||||
import app.fedilab.android.client.APIResponse;
|
||||
import app.fedilab.android.client.Entities.Account;
|
||||
import app.fedilab.android.client.Entities.Notification;
|
||||
import app.fedilab.android.client.GNUAPI;
|
||||
import app.fedilab.android.fragments.DisplayNotificationsFragment;
|
||||
|
||||
import static android.text.Html.FROM_HTML_MODE_LEGACY;
|
||||
import static app.fedilab.android.helper.BaseHelper.INTENT_ACTION;
|
||||
import static app.fedilab.android.helper.BaseHelper.INTENT_TARGETED_ACCOUNT;
|
||||
import static app.fedilab.android.helper.BaseHelper.NOTIFICATION_INTENT;
|
||||
import static app.fedilab.android.helper.BaseHelper.PREF_INSTANCE;
|
||||
import static app.fedilab.android.helper.BaseHelper.PREF_KEY_ID;
|
||||
import static app.fedilab.android.helper.BaseHelper.getMainLogo;
|
||||
|
||||
import static app.fedilab.android.helper.BaseHelper.notify_user;
|
||||
|
||||
|
||||
public class NotificationsHelper {
|
||||
|
||||
public static HashMap<String, String> since_ids = new HashMap<>();
|
||||
|
||||
public static void task(Context context, Account account) {
|
||||
APIResponse apiResponse;
|
||||
String key = account.getUsername() + "@" + account.getInstance();
|
||||
|
||||
String last_notifid = null;
|
||||
if (since_ids.containsKey(key)) {
|
||||
last_notifid = since_ids.get(key);
|
||||
}
|
||||
|
||||
final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||
//Check which notifications the user wants to see
|
||||
boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true);
|
||||
boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true);
|
||||
boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true);
|
||||
boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true);
|
||||
boolean notif_poll = sharedpreferences.getBoolean(Helper.SET_NOTIF_POLL, true);
|
||||
//User disagree with all notifications
|
||||
if (!notif_follow && !notif_add && !notif_mention && !notif_share && !notif_poll)
|
||||
return; //Nothing is done
|
||||
//No account connected, the service is stopped
|
||||
if (!Helper.isLoggedIn(context))
|
||||
return;
|
||||
//If WIFI only and on WIFI OR user defined any connections to use the service.
|
||||
if (!sharedpreferences.getBoolean(Helper.SET_WIFI_ONLY, false) || Helper.isOnWIFI(context)) {
|
||||
if (account.getSocial().compareTo("FRIENDICA") != 0 && account.getSocial().compareTo("GNU") != 0) {
|
||||
API api = new API(context, account.getInstance(), account.getToken());
|
||||
apiResponse = api.getNotificationsSince(DisplayNotificationsFragment.Type.ALL, last_notifid, false);
|
||||
} else {
|
||||
GNUAPI gnuApi = new GNUAPI(context, account.getInstance(), account.getToken());
|
||||
apiResponse = gnuApi.getNotificationsSince(DisplayNotificationsFragment.Type.ALL, last_notifid);
|
||||
}
|
||||
onRetrieveNotifications(context, apiResponse, account);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void onRetrieveNotifications(Context context, APIResponse apiResponse, final Account account) {
|
||||
List<Notification> notificationsReceived = apiResponse.getNotifications();
|
||||
if (apiResponse.getError() != null || notificationsReceived == null || notificationsReceived.size() == 0 || account == null)
|
||||
return;
|
||||
String key = account.getUsername() + "@" + account.getInstance();
|
||||
since_ids.put(key, apiResponse.getNotifications().get(0).getId());
|
||||
final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||
boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true);
|
||||
boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true);
|
||||
boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true);
|
||||
boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true);
|
||||
boolean notif_poll = sharedpreferences.getBoolean(Helper.SET_NOTIF_POLL, true);
|
||||
boolean notif_status = sharedpreferences.getBoolean(Helper.SET_NOTIF_STATUS, true);
|
||||
final String max_id = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), null);
|
||||
final List<Notification> notifications = new ArrayList<>();
|
||||
int pos = 0;
|
||||
for (Notification notif : notificationsReceived) {
|
||||
if (max_id == null || notif.getId().compareTo(max_id) > 0) {
|
||||
notifications.add(pos, notif);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
if (notifications.size() == 0)
|
||||
return;
|
||||
//No previous notifications in cache, so no notification will be sent
|
||||
int newFollows = 0;
|
||||
int newAdds = 0;
|
||||
int newMentions = 0;
|
||||
int newShare = 0;
|
||||
int newPolls = 0;
|
||||
int newStatus = 0;
|
||||
String notificationUrl = null;
|
||||
String title = null;
|
||||
String message = null;
|
||||
String targeted_account = null;
|
||||
Helper.NotifType notifType = Helper.NotifType.MENTION;
|
||||
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
|
||||
for (Notification notification : notifications) {
|
||||
switch (notification.getType()) {
|
||||
case "mention":
|
||||
notifType = Helper.NotifType.MENTION;
|
||||
if (notif_mention) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), context.getString(R.string.notif_mention));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), context.getString(R.string.notif_mention));
|
||||
if (notification.getStatus() != null) {
|
||||
if (notification.getStatus().getSpoiler_text() != null && notification.getStatus().getSpoiler_text().length() > 0) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getSpoiler_text(), FROM_HTML_MODE_LEGACY));
|
||||
else
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getSpoiler_text()));
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getContent(), FROM_HTML_MODE_LEGACY));
|
||||
else
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getContent()));
|
||||
}
|
||||
}
|
||||
newFollows++;
|
||||
}
|
||||
break;
|
||||
case "status":
|
||||
notifType = Helper.NotifType.STATUS;
|
||||
if (notif_status) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), context.getString(R.string.notif_status));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), context.getString(R.string.notif_status));
|
||||
if (notification.getStatus() != null) {
|
||||
if (notification.getStatus().getSpoiler_text() != null && notification.getStatus().getSpoiler_text().length() > 0) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getSpoiler_text(), FROM_HTML_MODE_LEGACY));
|
||||
else
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getSpoiler_text()));
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getContent(), FROM_HTML_MODE_LEGACY));
|
||||
else
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getContent()));
|
||||
}
|
||||
}
|
||||
newStatus++;
|
||||
}
|
||||
break;
|
||||
case "reblog":
|
||||
notifType = Helper.NotifType.BOOST;
|
||||
if (notif_share) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), context.getString(R.string.notif_reblog));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), context.getString(R.string.notif_reblog));
|
||||
newShare++;
|
||||
}
|
||||
break;
|
||||
case "favourite":
|
||||
notifType = Helper.NotifType.FAV;
|
||||
if (notif_add) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), context.getString(R.string.notif_favourite));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), context.getString(R.string.notif_favourite));
|
||||
newAdds++;
|
||||
}
|
||||
break;
|
||||
case "follow_request":
|
||||
notifType = Helper.NotifType.FOLLLOW;
|
||||
if (notif_follow) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), context.getString(R.string.notif_follow_request));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), context.getString(R.string.notif_follow_request));
|
||||
targeted_account = notification.getAccount().getId();
|
||||
newFollows++;
|
||||
}
|
||||
break;
|
||||
case "follow":
|
||||
notifType = Helper.NotifType.FOLLLOW;
|
||||
if (notif_follow) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), context.getString(R.string.notif_follow));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), context.getString(R.string.notif_follow));
|
||||
targeted_account = notification.getAccount().getId();
|
||||
newFollows++;
|
||||
}
|
||||
break;
|
||||
case "poll":
|
||||
notifType = Helper.NotifType.POLL;
|
||||
if (notif_poll) {
|
||||
if (notification.getAccount().getId() != null && notification.getAccount().getId().equals(userId))
|
||||
message = context.getString(R.string.notif_poll_self);
|
||||
else
|
||||
message = context.getString(R.string.notif_poll);
|
||||
newPolls++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int allNotifCount = newFollows + newAdds + newMentions + newShare + newPolls + newStatus;
|
||||
if (allNotifCount > 0) {
|
||||
//Some others notification
|
||||
final Intent intent = new Intent(context, MainActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(INTENT_ACTION, NOTIFICATION_INTENT);
|
||||
intent.putExtra(PREF_KEY_ID, account.getId());
|
||||
if (targeted_account != null && notifType == Helper.NotifType.FOLLLOW)
|
||||
intent.putExtra(INTENT_TARGETED_ACCOUNT, targeted_account);
|
||||
intent.putExtra(PREF_INSTANCE, account.getInstance());
|
||||
notificationUrl = notifications.get(0).getAccount().getAvatar();
|
||||
if (notificationUrl != null) {
|
||||
|
||||
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
final String finalNotificationUrl = notificationUrl;
|
||||
Helper.NotifType finalNotifType = notifType;
|
||||
String finalMessage = message;
|
||||
String finalMessage1 = message;
|
||||
Runnable myRunnable = () -> Glide.with(context)
|
||||
.asBitmap()
|
||||
.load(finalNotificationUrl)
|
||||
.listener(new RequestListener<Bitmap>() {
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
|
||||
notify_user(context, account, intent, BitmapFactory.decodeResource(context.getResources(),
|
||||
getMainLogo(context)), finalNotifType, context.getString(R.string.top_notification), finalMessage1);
|
||||
String lastNotif = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), null);
|
||||
if (lastNotif == null || notifications.get(0).getId().compareTo(lastNotif) > 0) {
|
||||
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||
editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), notifications.get(0).getId());
|
||||
editor.apply();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.into(new CustomTarget<Bitmap>() {
|
||||
@Override
|
||||
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
|
||||
notify_user(context, account, intent, resource, finalNotifType, context.getString(R.string.top_notification), finalMessage);
|
||||
String lastNotif = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), null);
|
||||
if (lastNotif == null || notifications.get(0).getId().compareTo(lastNotif) > 0) {
|
||||
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||
editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), notifications.get(0).getId());
|
||||
editor.apply();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadCleared(@Nullable Drawable placeholder) {
|
||||
|
||||
}
|
||||
});
|
||||
mainHandler.post(myRunnable);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package app.fedilab.android.helper;
|
||||
/* Copyright 2021 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Fedilab 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 Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import app.fedilab.android.asynctasks.PostSubscriptionAsyncTask;
|
||||
|
||||
import app.fedilab.android.client.Entities.Account;
|
||||
import app.fedilab.android.sqlite.AccountDAO;
|
||||
import app.fedilab.android.sqlite.Sqlite;
|
||||
|
||||
|
||||
public class PushNotifications {
|
||||
|
||||
|
||||
public void registerPushNotifications(Context context, String endpoint, String slug) {
|
||||
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||
String[] slugArray = slug.split("@");
|
||||
Account account = new AccountDAO(context, db).getUniqAccountUsernameInstance(slugArray[0], slugArray[1]);
|
||||
new PostSubscriptionAsyncTask(context, account, endpoint);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -15,54 +15,26 @@ package app.fedilab.android.jobs;
|
|||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
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.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.DataSource;
|
||||
import com.bumptech.glide.load.engine.GlideException;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.target.CustomTarget;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
import com.evernote.android.job.Job;
|
||||
import com.evernote.android.job.JobManager;
|
||||
import com.evernote.android.job.JobRequest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.activities.MainActivity;
|
||||
import app.fedilab.android.client.API;
|
||||
import app.fedilab.android.client.APIResponse;
|
||||
import app.fedilab.android.client.Entities.Account;
|
||||
import app.fedilab.android.client.Entities.Notification;
|
||||
import app.fedilab.android.client.GNUAPI;
|
||||
import app.fedilab.android.fragments.DisplayNotificationsFragment;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.helper.NotificationsHelper;
|
||||
import app.fedilab.android.sqlite.AccountDAO;
|
||||
import app.fedilab.android.sqlite.Sqlite;
|
||||
|
||||
import static app.fedilab.android.helper.Helper.INTENT_ACTION;
|
||||
import static app.fedilab.android.helper.Helper.INTENT_TARGETED_ACCOUNT;
|
||||
import static app.fedilab.android.helper.Helper.NOTIFICATION_INTENT;
|
||||
import static app.fedilab.android.helper.Helper.PREF_INSTANCE;
|
||||
import static app.fedilab.android.helper.Helper.PREF_KEY_ID;
|
||||
import static app.fedilab.android.helper.Helper.canNotify;
|
||||
import static app.fedilab.android.helper.Helper.getMainLogo;
|
||||
import static app.fedilab.android.helper.Helper.notify_user;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
@ -137,223 +109,8 @@ public class BaseNotificationsSyncJob extends Job {
|
|||
return;
|
||||
//Retrieve users in db that owner has.
|
||||
for (Account account : accounts) {
|
||||
APIResponse apiResponse;
|
||||
if (account.getSocial().compareTo("FRIENDICA") != 0 && account.getSocial().compareTo("GNU") != 0) {
|
||||
API api = new API(getContext(), account.getInstance(), account.getToken());
|
||||
apiResponse = api.getNotificationsSince(DisplayNotificationsFragment.Type.ALL, null, false);
|
||||
} else {
|
||||
GNUAPI gnuApi = new GNUAPI(getContext(), account.getInstance(), account.getToken());
|
||||
apiResponse = gnuApi.getNotificationsSince(DisplayNotificationsFragment.Type.ALL, null);
|
||||
}
|
||||
onRetrieveNotifications(apiResponse, account);
|
||||
NotificationsHelper.task(getContext(), account);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void onRetrieveNotifications(APIResponse apiResponse, final Account account) {
|
||||
List<Notification> notificationsReceived = apiResponse.getNotifications();
|
||||
if (apiResponse.getError() != null || notificationsReceived == null || notificationsReceived.size() == 0 || account == null)
|
||||
return;
|
||||
final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||
boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true);
|
||||
boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true);
|
||||
boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true);
|
||||
boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true);
|
||||
boolean notif_poll = sharedpreferences.getBoolean(Helper.SET_NOTIF_POLL, true);
|
||||
boolean notif_status = sharedpreferences.getBoolean(Helper.SET_NOTIF_STATUS, true);
|
||||
final String max_id = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), null);
|
||||
final List<Notification> notifications = new ArrayList<>();
|
||||
int pos = 0;
|
||||
for (Notification notif : notificationsReceived) {
|
||||
if (max_id == null || notif.getId().compareTo(max_id) > 0) {
|
||||
notifications.add(pos, notif);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
if (notifications.size() == 0)
|
||||
return;
|
||||
//No previous notifications in cache, so no notification will be sent
|
||||
int newFollows = 0;
|
||||
int newAdds = 0;
|
||||
int newMentions = 0;
|
||||
int newShare = 0;
|
||||
int newPolls = 0;
|
||||
int newStatus = 0;
|
||||
String notificationUrl = null;
|
||||
String title = null;
|
||||
final String message;
|
||||
String targeted_account = null;
|
||||
Helper.NotifType notifType = Helper.NotifType.MENTION;
|
||||
|
||||
for (Notification notification : notifications) {
|
||||
switch (notification.getType()) {
|
||||
case "mention":
|
||||
notifType = Helper.NotifType.MENTION;
|
||||
if (notif_mention) {
|
||||
newMentions++;
|
||||
if (notificationUrl == null) {
|
||||
notificationUrl = notification.getAccount().getAvatar();
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
title = String.format("%s %s", notification.getAccount().getDisplay_name(), getContext().getString(R.string.notif_mention));
|
||||
else
|
||||
title = String.format("@%s %s", notification.getAccount().getAcct(), getContext().getString(R.string.notif_mention));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "status":
|
||||
notifType = Helper.NotifType.STATUS;
|
||||
if (notif_status) {
|
||||
newStatus++;
|
||||
if (notificationUrl == null) {
|
||||
notificationUrl = notification.getAccount().getAvatar();
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
title = String.format("%s %s", notification.getAccount().getDisplay_name(), getContext().getString(R.string.notif_status));
|
||||
else
|
||||
title = String.format("@%s %s", notification.getAccount().getAcct(), getContext().getString(R.string.notif_status));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "reblog":
|
||||
notifType = Helper.NotifType.BOOST;
|
||||
if (notif_share) {
|
||||
newShare++;
|
||||
if (notificationUrl == null) {
|
||||
notificationUrl = notification.getAccount().getAvatar();
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
title = String.format("%s %s", notification.getAccount().getDisplay_name(), getContext().getString(R.string.notif_reblog));
|
||||
else
|
||||
title = String.format("@%s %s", notification.getAccount().getAcct(), getContext().getString(R.string.notif_reblog));
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "favourite":
|
||||
notifType = Helper.NotifType.FAV;
|
||||
if (notif_add) {
|
||||
newAdds++;
|
||||
if (notificationUrl == null) {
|
||||
notificationUrl = notification.getAccount().getAvatar();
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
title = String.format("%s %s", notification.getAccount().getDisplay_name(), getContext().getString(R.string.notif_favourite));
|
||||
else
|
||||
title = String.format("@%s %s", notification.getAccount().getAcct(), getContext().getString(R.string.notif_favourite));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "follow_request":
|
||||
notifType = Helper.NotifType.FOLLLOW;
|
||||
if (notif_follow) {
|
||||
newFollows++;
|
||||
if (notificationUrl == null) {
|
||||
notificationUrl = notification.getAccount().getAvatar();
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
title = String.format("%s %s", notification.getAccount().getDisplay_name(), getContext().getString(R.string.notif_follow_request));
|
||||
else
|
||||
title = String.format("@%s %s", notification.getAccount().getAcct(), getContext().getString(R.string.notif_follow_request));
|
||||
targeted_account = notification.getAccount().getId();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "follow":
|
||||
notifType = Helper.NotifType.FOLLLOW;
|
||||
if (notif_follow) {
|
||||
newFollows++;
|
||||
if (notificationUrl == null) {
|
||||
notificationUrl = notification.getAccount().getAvatar();
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
title = String.format("%s %s", notification.getAccount().getDisplay_name(), getContext().getString(R.string.notif_follow));
|
||||
else
|
||||
title = String.format("@%s %s", notification.getAccount().getAcct(), getContext().getString(R.string.notif_follow));
|
||||
targeted_account = notification.getAccount().getId();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "poll":
|
||||
notifType = Helper.NotifType.POLL;
|
||||
if (notif_poll) {
|
||||
newPolls++;
|
||||
if (notificationUrl == null) {
|
||||
notificationUrl = notification.getAccount().getAvatar();
|
||||
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
|
||||
if (notification.getAccount().getId() != null && notification.getAccount().getId().equals(userId))
|
||||
title = getContext().getString(R.string.notif_poll_self);
|
||||
else
|
||||
title = getContext().getString(R.string.notif_poll);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
int allNotifCount = newFollows + newAdds + newMentions + newShare + newPolls + newStatus;
|
||||
if (allNotifCount > 0) {
|
||||
//Some others notification
|
||||
int other = allNotifCount - 1;
|
||||
if (other > 0)
|
||||
message = getContext().getResources().getQuantityString(R.plurals.other_notifications, other, other);
|
||||
else
|
||||
message = "";
|
||||
final Intent intent = new Intent(getContext(), MainActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(INTENT_ACTION, NOTIFICATION_INTENT);
|
||||
intent.putExtra(PREF_KEY_ID, account.getId());
|
||||
if (targeted_account != null && notifType == Helper.NotifType.FOLLLOW)
|
||||
intent.putExtra(INTENT_TARGETED_ACCOUNT, targeted_account);
|
||||
intent.putExtra(PREF_INSTANCE, account.getInstance());
|
||||
if (notificationUrl != null) {
|
||||
final String finalTitle = title;
|
||||
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
final String finalNotificationUrl = notificationUrl;
|
||||
Helper.NotifType finalNotifType = notifType;
|
||||
Runnable myRunnable = () -> Glide.with(getContext())
|
||||
.asBitmap()
|
||||
.load(finalNotificationUrl)
|
||||
.listener(new RequestListener<Bitmap>() {
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
|
||||
|
||||
notify_user(getContext(), account, intent, BitmapFactory.decodeResource(getContext().getResources(),
|
||||
getMainLogo(getContext())), finalNotifType, finalTitle, message);
|
||||
String lastNotif = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), null);
|
||||
if (lastNotif == null || notifications.get(0).getId().compareTo(lastNotif) > 0) {
|
||||
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||
editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), notifications.get(0).getId());
|
||||
editor.apply();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.into(new CustomTarget<Bitmap>() {
|
||||
@Override
|
||||
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
|
||||
notify_user(getContext(), account, intent, resource, finalNotifType, finalTitle, message);
|
||||
String lastNotif = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), null);
|
||||
if (lastNotif == null || notifications.get(0).getId().compareTo(lastNotif) > 0) {
|
||||
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||
editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), notifications.get(0).getId());
|
||||
editor.apply();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadCleared(@Nullable Drawable placeholder) {
|
||||
|
||||
}
|
||||
});
|
||||
mainHandler.post(myRunnable);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,536 +0,0 @@
|
|||
package app.fedilab.android.services;
|
||||
/* Copyright 2017 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Fedilab 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 Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.Html;
|
||||
import android.text.SpannableString;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.DataSource;
|
||||
import com.bumptech.glide.load.engine.GlideException;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.target.CustomTarget;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
import com.koushikdutta.async.http.AsyncHttpClient;
|
||||
import com.koushikdutta.async.http.AsyncHttpRequest;
|
||||
import com.koushikdutta.async.http.Headers;
|
||||
import com.koushikdutta.async.http.WebSocket;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.activities.MainActivity;
|
||||
import app.fedilab.android.client.API;
|
||||
import app.fedilab.android.client.Entities.Account;
|
||||
import app.fedilab.android.client.Entities.Notification;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.sqlite.AccountDAO;
|
||||
import app.fedilab.android.sqlite.Sqlite;
|
||||
|
||||
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY;
|
||||
import static app.fedilab.android.helper.Helper.getMainLogo;
|
||||
import static app.fedilab.android.helper.Helper.getNotificationIcon;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Thomas on 29/11/2017.
|
||||
* Manage service for streaming api and new notifications
|
||||
*/
|
||||
|
||||
public abstract class BaseLiveNotificationService extends Service implements NetworkStateReceiver.NetworkStateReceiverListener {
|
||||
|
||||
public static String CHANNEL_ID = "live_notifications";
|
||||
public static int totalAccount = 0;
|
||||
public static int eventsCount = 0;
|
||||
private static final HashMap<String, String> lastNotification = new HashMap<>();
|
||||
private static final HashMap<String, WebSocket> webSocketFutures = new HashMap<>();
|
||||
|
||||
|
||||
protected Account account;
|
||||
private NetworkStateReceiver networkStateReceiver;
|
||||
private NotificationChannel channel;
|
||||
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||
boolean notify = sharedpreferences.getBoolean(Helper.SET_NOTIFY, true);
|
||||
networkStateReceiver = new NetworkStateReceiver();
|
||||
networkStateReceiver.addListener(this);
|
||||
registerReceiver(networkStateReceiver, new IntentFilter(android.net.ConnectivityManager.CONNECTIVITY_ACTION));
|
||||
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
channel = new NotificationChannel(CHANNEL_ID,
|
||||
"Live notifications",
|
||||
NotificationManager.IMPORTANCE_DEFAULT);
|
||||
|
||||
((NotificationManager) Objects.requireNonNull(getSystemService(Context.NOTIFICATION_SERVICE))).createNotificationChannel(channel);
|
||||
}
|
||||
SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||
List<Account> accountStreams = new AccountDAO(BaseLiveNotificationService.this, db).getAllAccountCrossAction();
|
||||
totalAccount = 0;
|
||||
if (accountStreams != null) {
|
||||
for (Account account : accountStreams) {
|
||||
if (account.getSocial() == null || account.getSocial().equals("MASTODON") || account.getSocial().equals("PLEROMA")) {
|
||||
boolean allowStream = sharedpreferences.getBoolean(Helper.SET_ALLOW_STREAM + account.getId() + account.getInstance(), true);
|
||||
if (allowStream) {
|
||||
totalAccount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
Intent myIntent = new Intent(BaseLiveNotificationService.this, MainActivity.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
BaseLiveNotificationService.this,
|
||||
0,
|
||||
myIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
android.app.Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setContentTitle(getString(R.string.top_notification))
|
||||
.setContentIntent(pendingIntent)
|
||||
.setSmallIcon(getNotificationIcon(BaseLiveNotificationService.this))
|
||||
.setContentText(getString(R.string.top_notification_message, String.valueOf(totalAccount), String.valueOf(eventsCount))).build();
|
||||
|
||||
if (notification != null) {
|
||||
startForeground(2, notification);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!notify) {
|
||||
stopSelf();
|
||||
return;
|
||||
}
|
||||
if (totalAccount == 0) {
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
|
||||
private void startStream() {
|
||||
|
||||
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||
boolean liveNotifications = sharedpreferences.getBoolean(Helper.SET_LIVE_NOTIFICATIONS, true);
|
||||
SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||
if (liveNotifications) {
|
||||
List<Account> accountStreams = new AccountDAO(BaseLiveNotificationService.this, db).getAllAccountCrossAction();
|
||||
if (accountStreams != null) {
|
||||
for (final Account accountStream : accountStreams) {
|
||||
if (accountStream.getSocial() == null || accountStream.getSocial().equals("MASTODON") || accountStream.getSocial().equals("PLEROMA")) {
|
||||
startWork(accountStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||
boolean notify = sharedpreferences.getBoolean(Helper.SET_NOTIFY, true);
|
||||
|
||||
|
||||
if (!notify || intent == null || intent.getBooleanExtra("stop", false)) {
|
||||
totalAccount = 0;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
stopForeground(STOP_FOREGROUND_DETACH);
|
||||
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
assert notificationManager != null;
|
||||
notificationManager.deleteNotificationChannel(CHANNEL_ID);
|
||||
}
|
||||
if (intent != null) {
|
||||
intent.replaceExtras(new Bundle());
|
||||
}
|
||||
stopSelf();
|
||||
}
|
||||
if (totalAccount > 0) {
|
||||
return START_STICKY;
|
||||
}
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
for (Thread t : Thread.getAllStackTraces().keySet()) {
|
||||
if (t.getName().startsWith("notif_live_")) {
|
||||
t.interrupt();
|
||||
}
|
||||
}
|
||||
Thread.currentThread().interrupt();
|
||||
if (networkStateReceiver != null) {
|
||||
networkStateReceiver.removeListener(this);
|
||||
unregisterReceiver(networkStateReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private void taks(Account account) {
|
||||
|
||||
if (account != null) {
|
||||
Headers headers = new Headers();
|
||||
headers.add("Authorization", "Bearer " + account.getToken());
|
||||
headers.add("Connection", "Keep-Alive");
|
||||
headers.add("method", "GET");
|
||||
headers.add("scheme", "https");
|
||||
String notif_url = "user:notification";
|
||||
if (account.getSocial().toUpperCase().equals("PLEROMA"))
|
||||
notif_url = "user";
|
||||
String urlKey = "wss://" + account.getInstance() + "/api/v1/streaming/?stream=" + notif_url + "&access_token=" + account.getToken();
|
||||
Uri url = Uri.parse(urlKey);
|
||||
AsyncHttpRequest.setDefaultHeaders(headers, url);
|
||||
String key = account.getAcct() + "@" + account.getInstance();
|
||||
if (webSocketFutures.get(key) == null || !Objects.requireNonNull(webSocketFutures.get(key)).isOpen()) {
|
||||
AsyncHttpClient.getDefaultInstance().websocket("wss://" + account.getInstance() + "/api/v1/streaming/?stream=" + notif_url + "&access_token=" + account.getToken(), "wss", (ex, webSocket) -> {
|
||||
webSocketFutures.put(account.getAcct() + "@" + account.getInstance(), webSocket);
|
||||
if (ex != null) {
|
||||
return;
|
||||
}
|
||||
webSocket.setStringCallback(s -> {
|
||||
try {
|
||||
JSONObject eventJson = new JSONObject(s);
|
||||
onRetrieveStreaming(account, eventJson);
|
||||
} catch (JSONException ignored) {
|
||||
}
|
||||
});
|
||||
|
||||
webSocket.setClosedCallback(ex1 -> {
|
||||
if (networkStateReceiver.connected) {
|
||||
startWork(account);
|
||||
}
|
||||
});
|
||||
webSocket.setDataCallback((emitter, byteBufferList) -> {
|
||||
// note that this data has been read
|
||||
byteBufferList.recycle();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void startWork(Account accountStream) {
|
||||
|
||||
String key = accountStream.getAcct() + "@" + accountStream.getInstance();
|
||||
Thread thread = Helper.getThreadByName("notif_live_" + key);
|
||||
if (thread == null) {
|
||||
thread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
taks(accountStream);
|
||||
}
|
||||
};
|
||||
thread.setName("notif_live_" + key);
|
||||
thread.start();
|
||||
} else if (thread.getState() != Thread.State.RUNNABLE) {
|
||||
thread.interrupt();
|
||||
thread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
taks(accountStream);
|
||||
}
|
||||
};
|
||||
thread.setName("notif_live_" + key);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void onRetrieveStreaming(Account account, JSONObject response) {
|
||||
if (response == null)
|
||||
return;
|
||||
final Notification notification;
|
||||
Bundle b = new Bundle();
|
||||
boolean canSendBroadCast = true;
|
||||
Helper.EventStreaming event;
|
||||
final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
|
||||
try {
|
||||
if ("notification".equals(response.get("event").toString())) {
|
||||
eventsCount++;
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
channel = new NotificationChannel(CHANNEL_ID,
|
||||
"Live notifications",
|
||||
NotificationManager.IMPORTANCE_DEFAULT);
|
||||
((NotificationManager) Objects.requireNonNull(getSystemService(Context.NOTIFICATION_SERVICE))).createNotificationChannel(channel);
|
||||
android.app.Notification notificationChannel = new NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setContentTitle(getString(R.string.top_notification))
|
||||
.setSmallIcon(getNotificationIcon(BaseLiveNotificationService.this))
|
||||
.setContentText(getString(R.string.top_notification_message, String.valueOf(totalAccount), String.valueOf(eventsCount))).build();
|
||||
if (notificationChannel != null) {
|
||||
startForeground(2, notificationChannel);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
event = Helper.EventStreaming.NOTIFICATION;
|
||||
notification = API.parseNotificationResponse(BaseLiveNotificationService.this, new JSONObject(response.get("payload").toString()));
|
||||
b.putParcelable("data", notification);
|
||||
boolean liveNotifications = sharedpreferences.getBoolean(Helper.SET_LIVE_NOTIFICATIONS, true);
|
||||
boolean canNotify = Helper.canNotify(BaseLiveNotificationService.this);
|
||||
boolean notify = sharedpreferences.getBoolean(Helper.SET_NOTIFY, true);
|
||||
String targeted_account = null;
|
||||
Helper.NotifType notifType = Helper.NotifType.MENTION;
|
||||
boolean activityRunning = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("isMainActivityRunning", false);
|
||||
String key = account.getAcct() + "@" + account.getInstance();
|
||||
if (lastNotification.containsKey(key) && notification.getId().compareTo(Objects.requireNonNull(lastNotification.get(key))) <= 0) {
|
||||
canNotify = false;
|
||||
}
|
||||
String lastNotif = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), null);
|
||||
if (notification.getId().compareTo(Objects.requireNonNull(lastNotif)) <= 0) {
|
||||
canNotify = false;
|
||||
}
|
||||
boolean allowStream = sharedpreferences.getBoolean(Helper.SET_ALLOW_STREAM + account.getId() + account.getInstance(), true);
|
||||
if (!allowStream) {
|
||||
canNotify = false;
|
||||
}
|
||||
|
||||
if ((userId == null || !userId.equals(account.getId()) || !activityRunning) && liveNotifications && canNotify && notify) {
|
||||
lastNotification.put(key, notification.getId());
|
||||
boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true);
|
||||
boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true);
|
||||
boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true);
|
||||
boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true);
|
||||
boolean notif_status = sharedpreferences.getBoolean(Helper.SET_NOTIF_STATUS, true);
|
||||
boolean notif_poll = sharedpreferences.getBoolean(Helper.SET_NOTIF_POLL, true);
|
||||
boolean somethingToPush = (notif_follow || notif_add || notif_mention || notif_share || notif_poll || notif_status);
|
||||
String message = null;
|
||||
if (somethingToPush) {
|
||||
switch (notification.getType()) {
|
||||
case "mention":
|
||||
notifType = Helper.NotifType.MENTION;
|
||||
if (notif_mention) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), getString(R.string.notif_mention));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), getString(R.string.notif_mention));
|
||||
if (notification.getStatus() != null) {
|
||||
if (notification.getStatus().getSpoiler_text() != null && notification.getStatus().getSpoiler_text().length() > 0) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getSpoiler_text(), FROM_HTML_MODE_LEGACY));
|
||||
else
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getSpoiler_text()));
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getContent(), FROM_HTML_MODE_LEGACY));
|
||||
else
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getContent()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
canSendBroadCast = false;
|
||||
}
|
||||
break;
|
||||
case "status":
|
||||
notifType = Helper.NotifType.STATUS;
|
||||
if (notif_status) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), getString(R.string.notif_status));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), getString(R.string.notif_status));
|
||||
if (notification.getStatus() != null) {
|
||||
if (notification.getStatus().getSpoiler_text() != null && notification.getStatus().getSpoiler_text().length() > 0) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getSpoiler_text(), FROM_HTML_MODE_LEGACY));
|
||||
else
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getSpoiler_text()));
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getContent(), FROM_HTML_MODE_LEGACY));
|
||||
else
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getContent()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
canSendBroadCast = false;
|
||||
}
|
||||
break;
|
||||
case "reblog":
|
||||
notifType = Helper.NotifType.BOOST;
|
||||
if (notif_share) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), getString(R.string.notif_reblog));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), getString(R.string.notif_reblog));
|
||||
} else {
|
||||
canSendBroadCast = false;
|
||||
}
|
||||
break;
|
||||
case "favourite":
|
||||
notifType = Helper.NotifType.FAV;
|
||||
if (notif_add) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), getString(R.string.notif_favourite));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), getString(R.string.notif_favourite));
|
||||
} else {
|
||||
canSendBroadCast = false;
|
||||
}
|
||||
break;
|
||||
case "follow_request":
|
||||
notifType = Helper.NotifType.FOLLLOW;
|
||||
if (notif_follow) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), getString(R.string.notif_follow_request));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), getString(R.string.notif_follow_request));
|
||||
targeted_account = notification.getAccount().getId();
|
||||
} else {
|
||||
canSendBroadCast = false;
|
||||
}
|
||||
break;
|
||||
case "follow":
|
||||
notifType = Helper.NotifType.FOLLLOW;
|
||||
if (notif_follow) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), getString(R.string.notif_follow));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), getString(R.string.notif_follow));
|
||||
targeted_account = notification.getAccount().getId();
|
||||
} else {
|
||||
canSendBroadCast = false;
|
||||
}
|
||||
break;
|
||||
case "poll":
|
||||
notifType = Helper.NotifType.POLL;
|
||||
if (notif_poll) {
|
||||
if (notification.getAccount().getId() != null && notification.getAccount().getId().equals(userId))
|
||||
message = getString(R.string.notif_poll_self);
|
||||
else
|
||||
message = getString(R.string.notif_poll);
|
||||
} else {
|
||||
canSendBroadCast = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
//Some others notification
|
||||
final Intent intent = new Intent(BaseLiveNotificationService.this, MainActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(Helper.INTENT_ACTION, Helper.NOTIFICATION_INTENT);
|
||||
intent.putExtra(Helper.PREF_KEY_ID, account.getId());
|
||||
intent.putExtra(Helper.PREF_INSTANCE, account.getInstance());
|
||||
if (targeted_account != null) {
|
||||
intent.putExtra(Helper.INTENT_TARGETED_ACCOUNT, targeted_account);
|
||||
}
|
||||
final String finalMessage = message;
|
||||
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
Helper.NotifType finalNotifType = notifType;
|
||||
Runnable myRunnable = () -> {
|
||||
if (finalMessage != null) {
|
||||
Glide.with(BaseLiveNotificationService.this)
|
||||
.asBitmap()
|
||||
.load(notification.getAccount().getAvatar())
|
||||
.listener(new RequestListener<Bitmap>() {
|
||||
@Override
|
||||
public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
|
||||
assert e != null;
|
||||
Helper.notify_user(BaseLiveNotificationService.this, account, intent, BitmapFactory.decodeResource(getResources(),
|
||||
getMainLogo(BaseLiveNotificationService.this)), finalNotifType, "@" + notification.getAccount().getAcct(), finalMessage);
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.into(new CustomTarget<Bitmap>() {
|
||||
@Override
|
||||
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
|
||||
Helper.notify_user(BaseLiveNotificationService.this, account, intent, resource, finalNotifType, "@" + notification.getAccount().getAcct(), finalMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadCleared(@Nullable Drawable placeholder) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
mainHandler.post(myRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
if (canSendBroadCast) {
|
||||
b.putString("userIdService", account.getId());
|
||||
Intent intentBC = new Intent(Helper.RECEIVE_DATA);
|
||||
intentBC.putExtra("eventStreaming", event);
|
||||
intentBC.putExtras(b);
|
||||
b.putParcelable("data", notification);
|
||||
LocalBroadcastManager.getInstance(BaseLiveNotificationService.this).sendBroadcast(intentBC);
|
||||
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||
editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), notification.getId());
|
||||
editor.apply();
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void networkAvailable() {
|
||||
startStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void networkUnavailable() {
|
||||
for (Thread t : Thread.getAllStackTraces().keySet()) {
|
||||
if (t.getName().startsWith("notif_live_")) {
|
||||
t.interrupt();
|
||||
}
|
||||
}
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
|
@ -1,520 +0,0 @@
|
|||
package app.fedilab.android.services;
|
||||
/* Copyright 2019 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Fedilab 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 Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
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.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.Html;
|
||||
import android.text.SpannableString;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.DataSource;
|
||||
import com.bumptech.glide.load.engine.GlideException;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.target.CustomTarget;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.activities.MainActivity;
|
||||
import app.fedilab.android.client.API;
|
||||
import app.fedilab.android.client.APIResponse;
|
||||
import app.fedilab.android.client.Entities.Account;
|
||||
import app.fedilab.android.client.Entities.Notification;
|
||||
import app.fedilab.android.client.GNUAPI;
|
||||
import app.fedilab.android.fragments.DisplayNotificationsFragment;
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.sqlite.AccountDAO;
|
||||
import app.fedilab.android.sqlite.Sqlite;
|
||||
|
||||
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY;
|
||||
import static app.fedilab.android.helper.Helper.getMainLogo;
|
||||
import static app.fedilab.android.helper.Helper.getNotificationIcon;
|
||||
import static app.fedilab.android.helper.Helper.sleeps;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Thomas on 10/09/2019.
|
||||
* Manage service for live notifications delayed
|
||||
*/
|
||||
|
||||
public class LiveNotificationDelayedService extends Service {
|
||||
|
||||
|
||||
public static String CHANNEL_ID = "live_notifications";
|
||||
public static int totalAccount = 0;
|
||||
public static int eventsCount = 0;
|
||||
public static HashMap<String, String> since_ids = new HashMap<>();
|
||||
protected Account account;
|
||||
private NotificationChannel channel;
|
||||
private boolean fetch;
|
||||
|
||||
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||
boolean notify = sharedpreferences.getBoolean(Helper.SET_NOTIFY, true);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
channel = new NotificationChannel(CHANNEL_ID,
|
||||
"Live notifications",
|
||||
NotificationManager.IMPORTANCE_DEFAULT);
|
||||
|
||||
((NotificationManager) Objects.requireNonNull(getSystemService(Context.NOTIFICATION_SERVICE))).createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||
List<Account> accountStreams = new AccountDAO(LiveNotificationDelayedService.this, db).getAllAccountCrossAction();
|
||||
totalAccount = 0;
|
||||
if (accountStreams != null) {
|
||||
for (Account account : accountStreams) {
|
||||
boolean allowStream = sharedpreferences.getBoolean(Helper.SET_ALLOW_STREAM + account.getId() + account.getInstance(), true);
|
||||
if (allowStream) {
|
||||
totalAccount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
Intent myIntent = new Intent(LiveNotificationDelayedService.this, MainActivity.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
LiveNotificationDelayedService.this,
|
||||
0,
|
||||
myIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
android.app.Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setShowWhen(false)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setContentTitle(getString(R.string.top_notification))
|
||||
.setSmallIcon(getNotificationIcon(LiveNotificationDelayedService.this))
|
||||
.setContentText(getString(R.string.top_notification_message, String.valueOf(totalAccount), String.valueOf(eventsCount))).build();
|
||||
if (notification != null) {
|
||||
startForeground(1, notification);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!notify) {
|
||||
stopSelf();
|
||||
return;
|
||||
}
|
||||
if (totalAccount > 0) {
|
||||
startStream();
|
||||
} else {
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
for (Thread t : Thread.getAllStackTraces().keySet()) {
|
||||
if (t.getName().startsWith("notif_delayed_")) {
|
||||
t.interrupt();
|
||||
}
|
||||
}
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||
boolean notify = sharedpreferences.getBoolean(Helper.SET_NOTIFY, true);
|
||||
if (!notify || intent == null || intent.getBooleanExtra("stop", false)) {
|
||||
totalAccount = 0;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
stopForeground(STOP_FOREGROUND_DETACH);
|
||||
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
assert notificationManager != null;
|
||||
notificationManager.deleteNotificationChannel(CHANNEL_ID);
|
||||
}
|
||||
if (intent != null) {
|
||||
intent.replaceExtras(new Bundle());
|
||||
}
|
||||
stopSelf();
|
||||
}
|
||||
if (totalAccount > 0) {
|
||||
return START_STICKY;
|
||||
}
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
|
||||
private void startStream() {
|
||||
|
||||
SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||
if (Helper.liveNotifType(LiveNotificationDelayedService.this) == Helper.NOTIF_DELAYED) {
|
||||
List<Account> accountStreams = new AccountDAO(LiveNotificationDelayedService.this, db).getAllAccountCrossAction();
|
||||
final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||
fetch = true;
|
||||
if (accountStreams != null) {
|
||||
for (final Account accountStream : accountStreams) {
|
||||
String key = accountStream.getUsername() + "@" + accountStream.getInstance();
|
||||
boolean allowStream = sharedpreferences.getBoolean(Helper.SET_ALLOW_STREAM + accountStream.getId() + accountStream.getInstance(), true);
|
||||
if (!allowStream) {
|
||||
continue;
|
||||
}
|
||||
if (!sleeps.containsKey(key)) {
|
||||
sleeps.put(key, 30000);
|
||||
}
|
||||
Thread thread = Helper.getThreadByName("notif_delayed_" + key);
|
||||
if (thread == null) {
|
||||
startThread(accountStream, key);
|
||||
} else if (thread.getState() != Thread.State.RUNNABLE) {
|
||||
thread.interrupt();
|
||||
startThread(accountStream, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startThread(Account accountStream, String key) {
|
||||
Thread thread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (fetch) {
|
||||
task(accountStream);
|
||||
fetch = (Helper.liveNotifType(LiveNotificationDelayedService.this) == Helper.NOTIF_DELAYED);
|
||||
if (sleeps.containsKey(key) && sleeps.get(key) != null) {
|
||||
try {
|
||||
Thread.sleep(sleeps.get(key));
|
||||
} catch (InterruptedException e) {
|
||||
SystemClock.sleep(sleeps.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
thread.setName("notif_delayed_" + key);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private void task(Account account) {
|
||||
String key = account.getUsername() + "@" + account.getInstance();
|
||||
APIResponse apiResponse;
|
||||
|
||||
String last_notifid = null;
|
||||
if (since_ids.containsKey(key)) {
|
||||
last_notifid = since_ids.get(key);
|
||||
}
|
||||
apiResponse = null;
|
||||
try {
|
||||
if (account.getSocial().compareTo("FRIENDICA") != 0 && account.getSocial().compareTo("GNU") != 0) {
|
||||
API api;
|
||||
api = new API(LiveNotificationDelayedService.this, account.getInstance(), account.getToken());
|
||||
apiResponse = api.getNotificationsSince(DisplayNotificationsFragment.Type.ALL, last_notifid, false);
|
||||
} else {
|
||||
GNUAPI gnuApi;
|
||||
gnuApi = new GNUAPI(LiveNotificationDelayedService.this, account.getInstance(), account.getToken());
|
||||
apiResponse = gnuApi.getNotificationsSince(DisplayNotificationsFragment.Type.ALL, last_notifid);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
if (apiResponse != null && apiResponse.getNotifications() != null && apiResponse.getNotifications().size() > 0) {
|
||||
since_ids.put(key, apiResponse.getNotifications().get(0).getId());
|
||||
for (Notification notification : apiResponse.getNotifications()) {
|
||||
if (last_notifid != null && notification.getId().compareTo(last_notifid) > 0) {
|
||||
onRetrieveStreaming(account, notification);
|
||||
sleeps.put(key, 30000);
|
||||
} else {
|
||||
if (apiResponse.getNotifications().size() == 1) { //TODO: use min id with Pixelfed when available for removing this fix.
|
||||
if (sleeps.containsKey(key) && sleeps.get(key) != null) {
|
||||
int newWaitTime = sleeps.get(key) + 30000;
|
||||
if (newWaitTime > 900000) {
|
||||
newWaitTime = 900000;
|
||||
}
|
||||
sleeps.put(key, newWaitTime);
|
||||
} else {
|
||||
sleeps.put(key, 60000);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (sleeps.containsKey(key) && sleeps.get(key) != null) {
|
||||
int newWaitTime = sleeps.get(key) + 30000;
|
||||
if (newWaitTime > 900000) {
|
||||
newWaitTime = 900000;
|
||||
}
|
||||
sleeps.put(key, newWaitTime);
|
||||
} else {
|
||||
sleeps.put(key, 60000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void onRetrieveStreaming(Account account, Notification notification) {
|
||||
|
||||
Bundle b = new Bundle();
|
||||
boolean canSendBroadCast = true;
|
||||
Helper.EventStreaming event;
|
||||
final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
|
||||
try {
|
||||
eventsCount++;
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
channel = new NotificationChannel(CHANNEL_ID,
|
||||
"Live notifications",
|
||||
NotificationManager.IMPORTANCE_DEFAULT);
|
||||
((NotificationManager) Objects.requireNonNull(getSystemService(Context.NOTIFICATION_SERVICE))).createNotificationChannel(channel);
|
||||
android.app.Notification notificationChannel = new NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setShowWhen(false)
|
||||
.setContentTitle(getString(R.string.top_notification))
|
||||
.setSmallIcon(getNotificationIcon(LiveNotificationDelayedService.this))
|
||||
.setContentText(getString(R.string.top_notification_message, String.valueOf(totalAccount), String.valueOf(eventsCount))).build();
|
||||
|
||||
if (notificationChannel != null) {
|
||||
startForeground(1, notificationChannel);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
event = Helper.EventStreaming.NOTIFICATION;
|
||||
boolean canNotify = Helper.canNotify(LiveNotificationDelayedService.this);
|
||||
boolean notify = sharedpreferences.getBoolean(Helper.SET_NOTIFY, true);
|
||||
String targeted_account = null;
|
||||
Helper.NotifType notifType = Helper.NotifType.MENTION;
|
||||
boolean activityRunning = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("isMainActivityRunning", false);
|
||||
boolean allowStream = sharedpreferences.getBoolean(Helper.SET_ALLOW_STREAM + account.getId() + account.getInstance(), true);
|
||||
if (!allowStream) {
|
||||
canNotify = false;
|
||||
}
|
||||
if ((userId == null || !userId.equals(account.getId()) || !activityRunning) && canNotify && notify) {
|
||||
boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true);
|
||||
boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true);
|
||||
boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true);
|
||||
boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true);
|
||||
boolean notif_status = sharedpreferences.getBoolean(Helper.SET_NOTIF_STATUS, true);
|
||||
boolean notif_poll = sharedpreferences.getBoolean(Helper.SET_NOTIF_POLL, true);
|
||||
boolean somethingToPush = (notif_follow || notif_add || notif_mention || notif_share || notif_poll || notif_status);
|
||||
|
||||
String message = null;
|
||||
if (somethingToPush) {
|
||||
switch (notification.getType()) {
|
||||
case "mention":
|
||||
notifType = Helper.NotifType.MENTION;
|
||||
if (notif_mention) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), getString(R.string.notif_mention));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), getString(R.string.notif_mention));
|
||||
if (notification.getStatus() != null) {
|
||||
if (notification.getStatus().getSpoiler_text() != null && notification.getStatus().getSpoiler_text().length() > 0) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getSpoiler_text(), FROM_HTML_MODE_LEGACY));
|
||||
else
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getSpoiler_text()));
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getContent(), FROM_HTML_MODE_LEGACY));
|
||||
else
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getContent()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
canSendBroadCast = false;
|
||||
}
|
||||
break;
|
||||
case "status":
|
||||
notifType = Helper.NotifType.STATUS;
|
||||
if (notif_status) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), getString(R.string.notif_status));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), getString(R.string.notif_status));
|
||||
if (notification.getStatus() != null) {
|
||||
if (notification.getStatus().getSpoiler_text() != null && notification.getStatus().getSpoiler_text().length() > 0) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getSpoiler_text(), FROM_HTML_MODE_LEGACY));
|
||||
else
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getSpoiler_text()));
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getContent(), FROM_HTML_MODE_LEGACY));
|
||||
else
|
||||
message = "\n" + new SpannableString(Html.fromHtml(notification.getStatus().getContent()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
canSendBroadCast = false;
|
||||
}
|
||||
break;
|
||||
case "reblog":
|
||||
notifType = Helper.NotifType.BOOST;
|
||||
if (notif_share) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), getString(R.string.notif_reblog));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), getString(R.string.notif_reblog));
|
||||
} else {
|
||||
canSendBroadCast = false;
|
||||
}
|
||||
break;
|
||||
case "favourite":
|
||||
notifType = Helper.NotifType.FAV;
|
||||
if (notif_add) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), getString(R.string.notif_favourite));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), getString(R.string.notif_favourite));
|
||||
} else {
|
||||
canSendBroadCast = false;
|
||||
}
|
||||
break;
|
||||
case "follow_request":
|
||||
notifType = Helper.NotifType.FOLLLOW;
|
||||
if (notif_follow) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), getString(R.string.notif_follow_request));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), getString(R.string.notif_follow_request));
|
||||
targeted_account = notification.getAccount().getId();
|
||||
} else {
|
||||
canSendBroadCast = false;
|
||||
}
|
||||
break;
|
||||
case "follow":
|
||||
notifType = Helper.NotifType.FOLLLOW;
|
||||
if (notif_follow) {
|
||||
if (notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0)
|
||||
message = String.format("%s %s", notification.getAccount().getDisplay_name(), getString(R.string.notif_follow));
|
||||
else
|
||||
message = String.format("@%s %s", notification.getAccount().getAcct(), getString(R.string.notif_follow));
|
||||
targeted_account = notification.getAccount().getId();
|
||||
} else {
|
||||
canSendBroadCast = false;
|
||||
}
|
||||
break;
|
||||
case "poll":
|
||||
notifType = Helper.NotifType.POLL;
|
||||
if (notif_poll) {
|
||||
if (notification.getAccount().getId() != null && notification.getAccount().getId().equals(userId))
|
||||
message = getString(R.string.notif_poll_self);
|
||||
else
|
||||
message = getString(R.string.notif_poll);
|
||||
} else {
|
||||
canSendBroadCast = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
//Some others notification
|
||||
final Intent intent = new Intent(LiveNotificationDelayedService.this, MainActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(Helper.INTENT_ACTION, Helper.NOTIFICATION_INTENT);
|
||||
intent.putExtra(Helper.PREF_KEY_ID, account.getId());
|
||||
intent.putExtra(Helper.PREF_INSTANCE, account.getInstance());
|
||||
if (targeted_account != null) {
|
||||
intent.putExtra(Helper.INTENT_TARGETED_ACCOUNT, targeted_account);
|
||||
}
|
||||
final String finalMessage = message;
|
||||
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
Helper.NotifType finalNotifType = notifType;
|
||||
Runnable myRunnable = () -> {
|
||||
if (finalMessage != null) {
|
||||
Glide.with(LiveNotificationDelayedService.this)
|
||||
.asBitmap()
|
||||
.load(notification.getAccount().getAvatar())
|
||||
.listener(new RequestListener<Bitmap>() {
|
||||
@Override
|
||||
public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
|
||||
Helper.notify_user(LiveNotificationDelayedService.this, account, intent, BitmapFactory.decodeResource(getResources(),
|
||||
getMainLogo(LiveNotificationDelayedService.this)), finalNotifType, "@" + notification.getAccount().getAcct(), finalMessage);
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.into(new CustomTarget<Bitmap>() {
|
||||
@Override
|
||||
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
|
||||
|
||||
Helper.notify_user(LiveNotificationDelayedService.this, account, intent, resource, finalNotifType, "@" + notification.getAccount().getAcct(), finalMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadCleared(@Nullable Drawable placeholder) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
mainHandler.post(myRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
if (canSendBroadCast) {
|
||||
b.putString("userIdService", account.getId());
|
||||
Intent intentBC = new Intent(Helper.RECEIVE_DATA);
|
||||
intentBC.putExtra("eventStreaming", event);
|
||||
intentBC.putExtras(b);
|
||||
b.putParcelable("data", notification);
|
||||
LocalBroadcastManager.getInstance(LiveNotificationDelayedService.this).sendBroadcast(intentBC);
|
||||
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||
editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId() + account.getInstance(), notification.getId());
|
||||
editor.apply();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package app.fedilab.android.services;
|
||||
/* Copyright 2017 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Fedilab 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 Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import app.fedilab.android.helper.Helper;
|
||||
import app.fedilab.android.jobs.NotificationsSyncJob;
|
||||
|
||||
import static app.fedilab.android.helper.BaseHelper.startStreaming;
|
||||
|
||||
/**
|
||||
* Created by Thomas on 22/09/2017.
|
||||
* BroadcastReceiver for restarting the service
|
||||
*/
|
||||
|
||||
public class RestartLiveNotificationReceiver extends BroadcastReceiver {
|
||||
|
||||
|
||||
@SuppressLint("UnsafeProtectedBroadcastReceiver")
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
int type = Helper.liveNotifType(context);
|
||||
if (type == Helper.NOTIF_DELAYED || type == Helper.NOTIF_LIVE) {
|
||||
startStreaming(context);
|
||||
} else {
|
||||
NotificationsSyncJob.schedule(false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package app.fedilab.android.services;
|
||||
/* Copyright 2019 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Fedilab 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 Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Thomas on 18/10/2019.
|
||||
* BroadcastReceiver for restarting delayed notification service
|
||||
*/
|
||||
|
||||
public class StopDelayedNotificationReceiver extends BroadcastReceiver {
|
||||
|
||||
@SuppressLint("UnsafeProtectedBroadcastReceiver")
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Intent streamingServiceIntent = new Intent(context, LiveNotificationDelayedService.class);
|
||||
streamingServiceIntent.putExtra("stop", true);
|
||||
try {
|
||||
context.startService(streamingServiceIntent);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package app.fedilab.android.services;
|
||||
/* Copyright 2017 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Fedilab 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 Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
|
||||
/**
|
||||
* Created by Thomas on 22/09/2017.
|
||||
* BroadcastReceiver for restarting the service
|
||||
*/
|
||||
|
||||
public class StopLiveNotificationReceiver extends BroadcastReceiver {
|
||||
|
||||
@SuppressLint("UnsafeProtectedBroadcastReceiver")
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Intent streamingServiceIntent = new Intent(context, LiveNotificationService.class);
|
||||
streamingServiceIntent.putExtra("stop", true);
|
||||
try {
|
||||
context.startService(streamingServiceIntent);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package app.fedilab.android.services;
|
||||
/* Copyright 2021 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Fedilab 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 Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.unifiedpush.android.connector.MessagingReceiver;
|
||||
import org.unifiedpush.android.connector.MessagingReceiverHandler;
|
||||
|
||||
|
||||
import app.fedilab.android.client.Entities.Account;
|
||||
import app.fedilab.android.helper.ECDH;
|
||||
import app.fedilab.android.helper.NotificationsHelper;
|
||||
import app.fedilab.android.helper.PushNotifications;
|
||||
import app.fedilab.android.sqlite.AccountDAO;
|
||||
import app.fedilab.android.sqlite.Sqlite;
|
||||
|
||||
|
||||
class handler implements MessagingReceiverHandler {
|
||||
|
||||
|
||||
@Override
|
||||
public void onMessage(@Nullable Context context, @NotNull String s, @NotNull String slug) {
|
||||
|
||||
new Thread(() -> {
|
||||
if (context != null) {
|
||||
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||
String[] slugArray = slug.split("@");
|
||||
ECDH ecdh = ECDH.getInstance();
|
||||
//ecdh.uncryptMessage(context, s, slug);
|
||||
Account account = new AccountDAO(context, db).getUniqAccountUsernameInstance(slugArray[0], slugArray[1]);
|
||||
NotificationsHelper.task(context, account);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewEndpoint(@Nullable Context context, @NotNull String endpoint, @NotNull String slug) {
|
||||
if (context != null) {
|
||||
new PushNotifications()
|
||||
.registerPushNotifications(context, endpoint, slug);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistrationFailed(@Nullable Context context, @NotNull String s) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistrationRefused(@Nullable Context context, @NotNull String s) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnregistered(@Nullable Context context, @NotNull String s) {
|
||||
}
|
||||
}
|
||||
|
||||
public class UnifiedPushService extends MessagingReceiver {
|
||||
public UnifiedPushService() {
|
||||
super(new handler());
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package app.fedilab.android.services;
|
||||
/* Copyright 2020 Thomas Schneider
|
||||
*
|
||||
* This file is a part of Fedilab
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Fedilab 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 Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
|
||||
public class UpgradeReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
||||
if (intent.getAction() != null && intent.getAction().compareTo(Intent.ACTION_MY_PACKAGE_REPLACED) == 0) {
|
||||
Intent streamingServiceIntent = new Intent(context, LiveNotificationDelayedService.class);
|
||||
streamingServiceIntent.putExtra("stop", true);
|
||||
try {
|
||||
context.startService(streamingServiceIntent);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
streamingServiceIntent = new Intent(context, LiveNotificationService.class);
|
||||
streamingServiceIntent.putExtra("stop", true);
|
||||
try {
|
||||
context.startService(streamingServiceIntent);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -278,6 +278,22 @@ public class AccountDAO {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all Account in db
|
||||
*
|
||||
* @return Account List<Account>
|
||||
*/
|
||||
public List<Account> getPushNotificationAccounts() {
|
||||
|
||||
try {
|
||||
Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, "(" + Sqlite.COL_SOCIAL + " = 'MASTODON' OR " + Sqlite.COL_SOCIAL + " = 'PLEROMA') AND " + Sqlite.COL_OAUTHTOKEN + " != 'null'", null, null, null, Sqlite.COL_INSTANCE + " ASC", null);
|
||||
return cursorToListUser(c);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an Account by token
|
||||
*
|
||||
|
@ -311,6 +327,22 @@ public class AccountDAO {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Account by token
|
||||
*
|
||||
* @param userName String
|
||||
* @param instance String
|
||||
* @return Account
|
||||
*/
|
||||
public Account getUniqAccountUsernameInstance(String userName, String instance) {
|
||||
|
||||
try {
|
||||
Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, Sqlite.COL_USERNAME + " = \"" + userName + "\" AND " + Sqlite.COL_INSTANCE + " = \"" + instance + "\"", null, null, null, null, "1");
|
||||
return cursorToUser(c);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the current user is already stored in data base
|
||||
|
|
|
@ -89,102 +89,6 @@
|
|||
android:layout_marginBottom="10dp"
|
||||
android:textColor="@color/mastodonC2" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/live_notif_per_account"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/settings_option_margin"
|
||||
android:layout_marginBottom="@dimen/settings_option_margin"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/set_allow_live_notifications_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/set_allow_live_notifications"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/set_allow_live_notifications_indication"
|
||||
android:textColor="@color/mastodonC2"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/set_allow_live_notifications"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/set_allow_live_notifications_others"
|
||||
style="@style/colored_button"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="end"
|
||||
android:contentDescription="@string/live_notif"
|
||||
android:padding="5dp"
|
||||
android:src="@drawable/ic_account_circle_acct" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/set_hide_status_bar_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/settings_option_margin"
|
||||
android:layout_marginBottom="@dimen/settings_option_margin"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/set_hide_status_bar"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/set_hide_status_bar_indication"
|
||||
android:textColor="@color/mastodonC2"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/set_hide_status_bar"
|
||||
style="@style/colored_button"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:contentDescription="@string/bookmark_add"
|
||||
android:padding="5dp"
|
||||
android:src="@drawable/ic_hide_status_bar" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/notification_settings"
|
||||
|
|
|
@ -1038,6 +1038,9 @@
|
|||
<string name="set_bibliogram">استبدال Instagram بـ Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram هو بديل مفتوح المصدر لـ Instagram يركز على الخصوصية.</string>
|
||||
<string name="set_bibliogram_host">أدخل مضيفك المخصص أو اتركه فارغًا لاستخدام bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">أخفِ شريط إشعارات Fedilab</string>
|
||||
<string name="set_hide_status_bar_indication">لإخفاء الإشعارات المتبقية على شريط الحالة ، اضغط على زر أيقونة العين ثم قم بإلغاء تحديد: \"العرض على شريط الحالة\"</string>
|
||||
<string name="set_live_type_indication">سيتم تأجيل الإشعارات كل 30 ثانية. الشيء الذي من شأنه أن يسمح باستنزاف أقل للبطارية.</string>
|
||||
|
|
|
@ -1005,6 +1005,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1007,6 +1007,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1031,6 +1031,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -997,6 +997,9 @@ Ara ja pots connectar-te al compte escrivint <b>%1$s</b> en el primer camp i fen
|
|||
<string name="set_bibliogram">Substituir Instagram per Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram és un frontal alternatiu de codi obert per Instagram que protegeix la privacitat.</string>
|
||||
<string name="set_bibliogram_host">Introdueix aquí el teu servidor personalitzat o deixa-ho en blanc i s\'usarà bibliogram.art</string>
|
||||
<string name="set_libreddit">Substituïu Reddit per Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit és una interfície alternativa a Reddit, és de codi obert i prioritza la privadesa.</string>
|
||||
<string name="set_libreddit_host">Introduïu el vostre servidor personalitzat o deixeu-ho em blanc per utilitzar libredd.it</string>
|
||||
<string name="set_hide_status_bar">Amaga la barra de notificacions de Fedilab</string>
|
||||
<string name="set_hide_status_bar_indication">Per amagar la notificació que queda a la barra d\'estat, toca la icona de l\'ull i després desactiva: \"Mostra a la barra d\'estat\"</string>
|
||||
<string name="set_live_type_indication">S\'ajornaran les notificacions cada 30 segons. Això permet gastar menys la pila.</string>
|
||||
|
|
|
@ -1021,6 +1021,9 @@ Uživatelské jméno a heslo nejsou nikdy ukládány. Jsou použity pouze během
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1038,6 +1038,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1007,6 +1007,9 @@
|
|||
<string name="set_bibliogram">Erstat Instagram med Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram er et open source Instagram front-end alternativt fokuseret på datafortrolighed.</string>
|
||||
<string name="set_bibliogram_host">Angiv din tilpassede vært eller lad stå tomt for at benytte bibliogram.art</string>
|
||||
<string name="set_libreddit">Erstat Reddit med Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit er en fortrolighedorienteret, alternativ open-source Twitter front-end.</string>
|
||||
<string name="set_libreddit_host">Angiv din tilpassede vært eller lad stå tomt for brug af libredd.it</string>
|
||||
<string name="set_hide_status_bar">Skjul Fedilab-notifikationsbjælke</string>
|
||||
<string name="set_hide_status_bar_indication">For at skjule den resterende notifikation på statusbjælken, så klik på øjeikonknappen og fjern derefter markeringen: \"Vises på statusbjælke\"</string>
|
||||
<string name="set_live_type_indication">Notifikationer udskydes hvert 30. sekund. Dette vil betyde lavere batteridræning.</string>
|
||||
|
|
|
@ -995,6 +995,9 @@ Sobald du die ersten Buchstaben eintippst, werden Namensvorschläge angezeigt\n\
|
|||
<string name="set_bibliogram">Instagram durch Bibliogram ersetzen</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram ist ein alternatives Open-Source-Frontend von Instagram, das sich auf den Datenschutz konzentriert.</string>
|
||||
<string name="set_bibliogram_host">Geben Sie Ihren benutzerdefinierten Host ein oder lassen Sie das Feld für die Verwendung von bibliogram.art frei</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Fedilab Benachrichtigungsleiste ausblenden</string>
|
||||
<string name="set_hide_status_bar_indication">Um die verbleibende Benachrichtigung in der Statusleiste zu verstecken, klicken Sie auf die Augensymbol-Schaltfläche und deaktivieren Sie: \"In Statusleiste anzeigen\"</string>
|
||||
<string name="set_live_type_indication">Benachrichtigungen werden 30 Sekunden verzögert. Dadurch wird weniger Akku verbraucht.</string>
|
||||
|
|
|
@ -1007,6 +1007,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Απόκρυψη της γραμμής ειδοποιήσεων του Φέντιλαμπ</string>
|
||||
<string name="set_hide_status_bar_indication">Για απόκρυψη της υπόλοιπης ειδοποίησης στη γραμμή κατάστασης, πίεσε στο πλήκτρο με το μάτι, και μετά απο-επέλεξε την...: «Προβολή στη γραμμή κατάστασης»</string>
|
||||
<string name="set_live_type_indication">Οι ειδοποιήσεις θα καθυστερούν κάθε 30 δευτερόλεπτα. Αυτό θα βοηθήσει στην χαμηλότερη απομύζηση της μπαταρίας.</string>
|
||||
|
|
|
@ -1007,6 +1007,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1001,6 +1001,9 @@ https://yandex.ru/legal/confidential/?lang=en </string>
|
|||
<string name="set_bibliogram">Reemplazar Instagram con Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram es una interfaz alternativa de Instagram de código abierto centrada en la privacidad.</string>
|
||||
<string name="set_bibliogram_host">Introduzca su servidor personalizado o déjelo en blanco para usar bibliogram.art</string>
|
||||
<string name="set_libreddit">Reemplazar Reddit con Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit es una interfaz alternativa de código abierto de Reddit enfocada en la privacidad.</string>
|
||||
<string name="set_libreddit_host">Introduce tu servidor personalizado o déjalo en blanco para usar libredd.it</string>
|
||||
<string name="set_hide_status_bar">Ocultar la barra de notificaciones de Fedilab</string>
|
||||
<string name="set_hide_status_bar_indication">Para ocultar el resto de la notificación en la barra de estado, presione en el botón con el ícono de un ojo y luego desactivar \"Mostrar en la barra de estado\"</string>
|
||||
<string name="set_live_type_indication">Las notificaciones se retrasarán cada 30 segundos. Esto permitirá gastar menos batería.</string>
|
||||
|
|
|
@ -1006,6 +1006,9 @@
|
|||
<string name="set_bibliogram">Ordeztu Instagram Bibliogram-ekin</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram pribatutasuna aintzat duen Instagram interfaze libre bat da.</string>
|
||||
<string name="set_bibliogram_host">Sartu zure ostalari pertsonalizatua edo laga hutsik bibliogram.art erabiltzeko</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Ezkutatu Fedilab jakinarazpen-barra</string>
|
||||
<string name="set_hide_status_bar_indication">Egoera barran geratzen den jakinarazpena ezkutatzeko, sakatu begiaren ikonoa eta desmarkatu \"Erakutsi egoera-barran\"</string>
|
||||
<string name="set_live_type_indication">Jakinarazpenak 30 segundo atzeratuko dira. Honela bateria gutxiago erabiliko da.</string>
|
||||
|
|
|
@ -1007,6 +1007,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1004,6 +1004,9 @@ Le bouton de connexion s’activera une fois qu’un domaine valide sera renseig
|
|||
<string name="set_bibliogram">Remplacer Instagram par Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram est une interface ouverte en alternative à Instagram axée sur la confidentialité.</string>
|
||||
<string name="set_bibliogram_host">Entrez votre hôte personnalisé ou laissez vide pour utiliser bibliogram.art</string>
|
||||
<string name="set_libreddit">Remplacer Reddit par Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit est une alternative ouverte à l\'interface de Reddit axée sur la protection de la vie privée.</string>
|
||||
<string name="set_libreddit_host">Entrez votre hôte personnalisé ou laissez vide pour utiliser libredd.it</string>
|
||||
<string name="set_hide_status_bar">Masquer la barre de notification de Fedilab</string>
|
||||
<string name="set_hide_status_bar_indication">Pour cacher le reste de la notification dans la barre de statut, cliquez sur le bouton en forme d\'œil, puis décochez: \"Afficher dans la barre de statut\"</string>
|
||||
<string name="set_live_type_indication">Les notifications seront mises à jour toutes les 30 secondes. Cela permet de réduire la consommation de la batterie.</string>
|
||||
|
|
|
@ -1007,6 +1007,9 @@
|
|||
<string name="set_bibliogram">Substituír Instagram con Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram é unha alternativa de código aberto centrada na privacidade a interface de Instagram.</string>
|
||||
<string name="set_bibliogram_host">Escribe o servidor personalizado ou deixa en branco para usar bibliogram.art</string>
|
||||
<string name="set_libreddit">Substituir Reddit por Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit é unha alternativa de código aberto á interface de Reddit centrada na privacidade.</string>
|
||||
<string name="set_libreddit_host">Escribe o teu servidor personalizado ou deixa baleiro para usar libredd.it</string>
|
||||
<string name="set_hide_status_bar">Ocultar barra de notificacións de Fedilab</string>
|
||||
<string name="set_hide_status_bar_indication">Para ocultar as notificacións remanentes na barra de estado, pulsa na icona do ollo e desmarca: \"Mostrar en barra de estado\"</string>
|
||||
<string name="set_live_type_indication">As notificacións retrasaranse cada 30 segundos. Esto permite aforrar batería.</string>
|
||||
|
|
|
@ -999,6 +999,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1005,6 +1005,9 @@ A Yandexnek megvan a saját adatvédelmi szabályzata, ami itt található: http
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1006,6 +1006,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1001,6 +1001,9 @@ https://yandex.ru/legal/confidential/?lang=en
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1007,6 +1007,9 @@
|
|||
<string name="set_bibliogram">Sostituisci Instagram con Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram è un\'interfaccia alternativa e open source per Instagram, focalizzata sulla privacy.</string>
|
||||
<string name="set_bibliogram_host">Inserisci il tuo host personalizzato o lascia vuoto per usare bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Nascondi barra di notifica di Fedilab</string>
|
||||
<string name="set_hide_status_bar_indication">Per nascondere le notifiche rimanenti nella barra di stato, premi il pulsante con l\'icona di un occhio e deseleziona: \"Mostra nella barra di stato\"</string>
|
||||
<string name="set_live_type_indication">Le notifiche saranno differite ogni 30 secondi. Questo consentirà un minore consumo di batteria.</string>
|
||||
|
|
|
@ -990,6 +990,9 @@
|
|||
<string name="set_bibliogram">InstagramをBibliogramに置き換える</string>
|
||||
<string name="set_bibliogram_indication">Bibliogramはプライバシーを重視したオープンソースのInstagramの代替フロントエンドです。</string>
|
||||
<string name="set_bibliogram_host">カスタムホストを入力してください。空欄にするとbibliogram.artが使用されます。</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">通知バーからFedilabを非表示にする</string>
|
||||
<string name="set_hide_status_bar_indication">ステータスバー上の通知を非表示にするには、目のアイコンボタンをタップして「ステータスバーに表示する」のチェックを外します</string>
|
||||
<string name="set_live_type_indication">通知は30秒ごとに遅延が発生します。バッテリーの持ちが改善します。</string>
|
||||
|
|
|
@ -1007,6 +1007,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -999,6 +999,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1005,6 +1005,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1006,6 +1006,9 @@ Je kunt beginnen met typen en er zullen namen gesuggereerd worden.\n\n
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -997,6 +997,9 @@ Adresser vil bli foreslått når du begynner å skrive.\n\n
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1007,6 +1007,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1020,6 +1020,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1007,6 +1007,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1013,6 +1013,9 @@ Aceste date sunt strict confidențiale și pot fi folosite doar de aplicație.
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1023,6 +1023,9 @@
|
|||
<string name="set_bibliogram">Заменить Instagram на Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram это альтернативный front-end для Instagram с открытым исходным кодом и сфокусированный на приватности.</string>
|
||||
<string name="set_bibliogram_host">Введите свой собственный хост или оставьте пустым для использования bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Скрыть панель уведомлений Fedilab</string>
|
||||
<string name="set_hide_status_bar_indication">Чтобы скрыть уведомление в строке состояния, нажмите на кнопку с пиктограммой глаза и снимите флажок: \"Показывать в строке состояния\"</string>
|
||||
<string name="set_live_type_indication">Уведомления будут появляться на 30 секунд позже. Это позволит уменьшить влияние на батарею.</string>
|
||||
|
|
|
@ -970,6 +970,9 @@
|
|||
<string name="set_bibliogram">Remplasa Instagram cun Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram est un\'interfache alternativa pro Instagram, a mitza aberta e progetada pro sa riservadesa.</string>
|
||||
<string name="set_bibliogram_host">Inserta s\'istrangiadore (host) personalizadu tuo o lassa bòidu pro bibliogram.art</string>
|
||||
<string name="set_libreddit">Remplasa Reddit cun Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit est un\'interfache alternativa a mitza aberta pro Reddit progetada pro sa riservadesa.</string>
|
||||
<string name="set_libreddit_host">Inserta s\'istrangiadore (host) personalizadu tuo o lassa bòidu pro impreare libredd.it</string>
|
||||
<string name="set_hide_status_bar">Cua sa barra de is notìficas de Fedilab</string>
|
||||
<string name="set_hide_status_bar_indication">Pro cuare is notìficas chi abarrant in sa barra de istadu toca in su butone cun s\'icona a forma de ogru e boga sa seletzione a: \"Ammustra in sa barra de istadu\"</string>
|
||||
<string name="set_live_type_indication">Is notìficas ant a èssere perlongadas cada 30 segundos. Custu at a permìtere de consumire de mancu sa bateria.</string>
|
||||
|
|
|
@ -1007,6 +1007,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1023,6 +1023,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1015,6 +1015,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -1007,6 +1007,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Dölj Fedilab meddelandefält</string>
|
||||
<string name="set_hide_status_bar_indication">För att dölja kvarvarande notifieringar i statusbaren, klicka på ögonikons knappen och kryssa ur: \"Visa i statusbar\"</string>
|
||||
<string name="set_live_type_indication">Notifieringar kommer fördröjas var 30:e sekund. Detta kommer att dra mindre ström.</string>
|
||||
|
|
|
@ -1021,6 +1021,9 @@
|
|||
<string name="set_bibliogram">Zamiyń Instagram na Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram to ôtwartozdrzōdłowo, skupiōno na prywatności alternatywa do przodnich funkcyji Instagrama.</string>
|
||||
<string name="set_bibliogram_host">Wkludź swōj włosny host abo ôstow prōzne, żeby używać bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Skryj posek powiadōmiyń Fedilab</string>
|
||||
<string name="set_hide_status_bar_indication">Żeby skryć permanyntne powiadōmiynie we posku statusu, tyknij knefel z ôkym i ôdznacz „Pokoż we posku statusu”</string>
|
||||
<string name="set_live_type_indication">Powiadōmiynia bydōm ôpōźniōne co 30 sekund. To przizwoli na myńsze wyużycie bateryje.</string>
|
||||
|
|
|
@ -1001,6 +1001,9 @@
|
|||
<string name="set_bibliogram">Instagram\'ı Bibliogram ile Değiştirin</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram, mahremiyete odaklanan açık kaynaklı bir alternatif Instagram arayüzdür.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Reddit yerine Libreddit kullan</string>
|
||||
<string name="set_libreddit_indication">Libreddit, mahremiyete odaklanan açık kaynaklı bir alternatif Reddit arayüzüdür.</string>
|
||||
<string name="set_libreddit_host">Sunucu adresi girin ya da libredd.it sunucusunu kullanmak için boş bırakın</string>
|
||||
<string name="set_hide_status_bar">Fedilab bildirim çubuğunu gizle</string>
|
||||
<string name="set_hide_status_bar_indication">Durum çubuğunda kalan bildirimi gizlemek için, göz simgesine dokunun ve ardından \"Durum çubuğunda göster\" seçeneğinin işaretini kaldırın</string>
|
||||
<string name="set_live_type_indication">Bildirimler 30 saniye geciktirilecek. Bu sayede pil harcaması azalacak.</string>
|
||||
|
|
|
@ -1013,6 +1013,9 @@
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -996,6 +996,9 @@ và %d thông báo khác</item>
|
|||
<string name="set_bibliogram">Replace Instagram with Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram is an open source alternative Instagram front-end focused on privacy.</string>
|
||||
<string name="set_bibliogram_host">Enter your custom host or leave blank for using bibliogram.art</string>
|
||||
<string name="set_libreddit">Replace Reddit with Libreddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit is an open source alternative Reddit front-end focused on privacy.</string>
|
||||
<string name="set_libreddit_host">Enter your custom host or leave blank for using libredd.it</string>
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
|
|
|
@ -999,6 +999,9 @@
|
|||
<string name="set_bibliogram">将 Instagram 替换成 Bibliogram</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram 是个注重隐私保护的 Instagram 开源替代前端。</string>
|
||||
<string name="set_bibliogram_host">输入你的自定义主机或者留空以使用 bibliogram.art</string>
|
||||
<string name="set_libreddit">用 Libreddit 替换 Reddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit 是一个开源的 Reddit 替代前端,专注于隐私。</string>
|
||||
<string name="set_libreddit_host">输入您的自定义主机或留空使用 libredd.it</string>
|
||||
<string name="set_hide_status_bar">隐藏 Fedilab 通知栏</string>
|
||||
<string name="set_hide_status_bar_indication">要隐藏留在状态栏里的通知,请点击眼睛图标的按钮,然后取消勾选:“在状态栏内显示”</string>
|
||||
<string name="set_live_type_indication">通知将被延迟为每30秒产生。这将允许耗尽较少电池。</string>
|
||||
|
|
|
@ -998,6 +998,9 @@ Yandex 有適當的隱私權政策,可以在這裡找到:https://yandex.ru/l
|
|||
<string name="set_bibliogram">將 Instagram 以 Bibliogram 取代</string>
|
||||
<string name="set_bibliogram_indication">Bibliogram 是一個尊重隱私的開放原始碼替代 Instagram 前端。</string>
|
||||
<string name="set_bibliogram_host">輸入您的自訂主機或留空以使用 bibliogram.art</string>
|
||||
<string name="set_libreddit">用 Libreddit 取代 Reddit</string>
|
||||
<string name="set_libreddit_indication">Libreddit 是一個尊重隱私的開放原始碼替代 Reddit 前端。</string>
|
||||
<string name="set_libreddit_host">輸入您的自訂主機或留空以使用 libredd.it</string>
|
||||
<string name="set_hide_status_bar">隱藏 Fedilab 通知列</string>
|
||||
<string name="set_hide_status_bar_indication">要隱藏狀態列中的其餘通知,點擊眼睛圖示的按鈕上然後取消勾選「在狀態列中顯示」</string>
|
||||
<string name="set_live_type_indication">通知將會每30秒被延遲一次。這將會降低電池的消耗。</string>
|
||||
|
|
|
@ -130,4 +130,13 @@
|
|||
|
||||
<color name="dark_icon_theme">#f3f3f3</color>
|
||||
<color name="black_icon_theme">#606984</color>
|
||||
|
||||
|
||||
<color name="rainbow_1">#ff0000</color>
|
||||
<color name="rainbow_2">#ffa500</color>
|
||||
<color name="rainbow_3">#ffff00</color>
|
||||
<color name="rainbow_4">#008000</color>
|
||||
<color name="rainbow_5">#0000ff</color>
|
||||
<color name="rainbow_6">#4b0082</color>
|
||||
<color name="rainbow_7">#ee82ee</color>
|
||||
</resources>
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
<string name="disclaimer_full">Information below may reflect the user\'s profile incompletely.</string>
|
||||
<string name="insert_emoji">Insert emoji</string>
|
||||
<string name="no_emoji">The app did not collect custom emojis for the moment.</string>
|
||||
<string name="live_notif">Live notifications</string>
|
||||
<string name="push_notif">Push notifications</string>
|
||||
<string name="logout_confirmation">Are you sure you want to logout?</string>
|
||||
<string name="logout_account_confirmation">Are you sure you want to logout @%1$s@%2$s?</string>
|
||||
<!-- Status -->
|
||||
|
@ -984,7 +984,6 @@
|
|||
<string name="set_blur_sensitive_indication">If disabled, sensitive media will be hidden with a button</string>
|
||||
<string name="set_long_press_media_indication">Store media in full size with a long press on previews</string>
|
||||
<string name="set_display_timeline_in_list_indication">Add an ellipse button at the top right for listing all tags/instances/lists</string>
|
||||
<string name="live_notif_indication">Keep an open connection to the streaming API for live notifications.</string>
|
||||
<string name="set_enable_time_slot_indication">During the time slot, the app will send notifications. You can reverse (ie: silent) this time slot with the right spinner.</string>
|
||||
<string name="set_display_fedilab_features_button_indication">Display a Fedilab button below profile picture. It is a shortcut for accessing in-app features.</string>
|
||||
<string name="set_quick_reply_indication">Allow to reply directly in timelines below statuses</string>
|
||||
|
@ -1121,8 +1120,7 @@
|
|||
|
||||
<string name="set_hide_status_bar">Hide Fedilab notification bar</string>
|
||||
<string name="set_hide_status_bar_indication">For hiding the remaining notification in the status bar, tap on the eye icon button then uncheck: \"Display in status bar\"</string>
|
||||
<string name="set_live_type_indication">Notifications will be delayed every 30 seconds. That will allow to drain less battery.</string>
|
||||
<string name="live_delayed">Live notifications delayed</string>
|
||||
<string name="set_push_notifications">Use a push notifications system for getting notifications in real time.</string>
|
||||
<string name="no_live_notif">No live notifications</string>
|
||||
<string name="no_live_indication">Notifications will be fetched every 15 minutes.</string>
|
||||
<string name="action_add_notes">Add notes</string>
|
||||
|
@ -1243,5 +1241,8 @@
|
|||
<string name="set_video_cache">Video cache in MB, zero means no cache.</string>
|
||||
<string name="set_watermark">Watermarks</string>
|
||||
<string name="set_watermark_indication">Automatically add a watermark at the bottom of pictures. The text can be customized for each account.</string>
|
||||
<string name="no_distributors_found">No distributors found!</string>
|
||||
<string name="no_distributors_explanation">You need a distributor for receiving push notifications.\nYou will find more details at %1$s.\n\nYou can also disable push notifications in settings for ignoring that message.</string>
|
||||
<string name="select_distributors">Select a distributor</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2017 Thomas Schneider
|
||||
|
||||
This file is a part of Fedilab
|
||||
|
||||
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.
|
||||
|
||||
Fedilab 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 Fedilab; if not,
|
||||
see <http://www.gnu.org/licenses>
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="app.fedilab.android"
|
||||
android:installLocation="auto">
|
||||
|
||||
|
||||
<application
|
||||
android:name="app.fedilab.android.activities.MainApplication"
|
||||
android:allowBackup="false"
|
||||
android:hardwareAccelerated="true"
|
||||
android:icon="@mipmap/ic_launcher_bubbles"
|
||||
android:label="@string/app_name"
|
||||
android:largeHeap="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:roundIcon="@mipmap/ic_launcher_bubbles_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppThemeDark"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:replace="android:allowBackup">
|
||||
|
||||
|
||||
<activity-alias
|
||||
android:name=".activities.MainActivity.Bubbles"
|
||||
android:enabled="true"
|
||||
android:icon="@mipmap/ic_launcher_bubbles"
|
||||
android:roundIcon="@mipmap/ic_launcher_bubbles_round"
|
||||
android:targetActivity=".activities.MainActivity">
|
||||
<meta-data
|
||||
android:name="icon"
|
||||
android:value="bubbles" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
|
||||
<activity
|
||||
android:name="app.fedilab.android.activities.PhotoEditorActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:label="@string/app_name" />
|
||||
<activity
|
||||
android:name="app.fedilab.android.activities.OwnerStatusActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/AppThemeDark_NoActionBar"
|
||||
android:windowSoftInputMode="stateAlwaysHidden" />
|
||||
<activity
|
||||
android:name="app.fedilab.android.activities.OwnerNotificationChartsActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:windowSoftInputMode="stateAlwaysHidden" />
|
||||
<activity
|
||||
android:name="app.fedilab.android.activities.OwnerChartsActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:windowSoftInputMode="stateAlwaysHidden" />
|
||||
<activity
|
||||
android:name="app.fedilab.android.activities.OwnerNotificationActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/AppThemeDark_NoActionBar"
|
||||
android:windowSoftInputMode="stateAlwaysHidden" />
|
||||
|
||||
<activity
|
||||
android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
|
||||
android:theme="@style/Base.Theme.AppCompat" />
|
||||
|
||||
|
||||
<receiver
|
||||
android:name=".services.UnifiedPushService"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="org.unifiedpush.android.connector.MESSAGE" />
|
||||
<action android:name="org.unifiedpush.android.connector.UNREGISTERED" />
|
||||
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT" />
|
||||
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED" />
|
||||
<action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".services.FakeDistributor"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="org.unifiedpush.android.distributor.REGISTER" />
|
||||
<action android:name="org.unifiedpush.android.distributor.UNREGISTER" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
</manifest>
|
|
@ -0,0 +1,50 @@
|
|||
package app.fedilab.android.helper;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import org.unifiedpush.android.connector_fcm_added.RegistrationFCM;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import app.fedilab.android.client.Entities.Account;
|
||||
import app.fedilab.android.jobs.ApplicationJob;
|
||||
import app.fedilab.android.jobs.NotificationsSyncJob;
|
||||
import app.fedilab.android.sqlite.AccountDAO;
|
||||
import app.fedilab.android.sqlite.Sqlite;
|
||||
|
||||
import static app.fedilab.android.helper.BaseHelper.NOTIF_NONE;
|
||||
import static app.fedilab.android.helper.BaseHelper.NOTIF_PUSH;
|
||||
import static app.fedilab.android.helper.BaseHelper.liveNotifType;
|
||||
|
||||
|
||||
public class PushHelper {
|
||||
|
||||
|
||||
public static void startStreaming(Context context) {
|
||||
int liveNotifications = liveNotifType(context);
|
||||
ApplicationJob.cancelAllJob(NotificationsSyncJob.NOTIFICATION_REFRESH);
|
||||
NotificationsSyncJob.schedule(false);
|
||||
switch (liveNotifications) {
|
||||
case NOTIF_PUSH:
|
||||
new Thread(() -> {
|
||||
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||
List<Account> accounts = new AccountDAO(context, db).getPushNotificationAccounts();
|
||||
((Activity) context).runOnUiThread(() -> {
|
||||
RegistrationFCM registration = new RegistrationFCM();
|
||||
for (Account account : accounts) {
|
||||
registration.registerAppWithDialog(context, account.getUsername() + "@" + account.getInstance());
|
||||
}
|
||||
});
|
||||
}).start();
|
||||
break;
|
||||
case NOTIF_NONE:
|
||||
new RegistrationFCM().unregisterApp(context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package app.fedilab.android.services;
|
||||
|
||||
import org.unifiedpush.android.connector_fcm_added.FakeDistributorReceiver;
|
||||
|
||||
public class FakeDistributor extends FakeDistributorReceiver {
|
||||
public FakeDistributor() {
|
||||
super(new HandlerFCM());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package app.fedilab.android.services;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.unifiedpush.android.connector_fcm_added.GetEndpointHandler;
|
||||
|
||||
public class HandlerFCM implements GetEndpointHandler {
|
||||
|
||||
@Override
|
||||
public @NotNull String getEndpoint(@Nullable Context context, @NotNull String token, @NotNull String instance) {
|
||||
return "https://gotify.fedilab.app/FCM?token=" + token + "&instance=" + instance;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -6,7 +6,8 @@ buildscript {
|
|||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.1'
|
||||
classpath 'com.android.tools.build:gradle:4.1.2'
|
||||
classpath 'com.google.gms:google-services:4.3.5'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
@ -17,6 +18,7 @@ allprojects {
|
|||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue