Sponsor: GhostCloud labeling, curved gloss streak, support link & top sponsors
- Bulletin now reads 'Sponsor GhostCloud' + granted for 444+ rubles (en/ru) - Heart highlight reworked: thin curved streak sweeping diagonally instead of an orbiting blob; palette tuned closer to the reference - SponsorHelper.loadTopSponsors() hits mobile donors/leaderboard via X-Tg-Id - New FoxSponsorsActivity lists the top sponsors - Neko settings: 'Support the project' (t.me/vpnghostbot) + 'Top sponsors'
This commit is contained in:
parent
9b81a463a6
commit
47f9aef160
6 changed files with 263 additions and 39 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
package tw.nekomimi.nekogram.helpers;
|
package tw.nekomimi.nekogram.helpers;
|
||||||
|
|
||||||
|
import android.graphics.BlurMaskFilter;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.ColorFilter;
|
import android.graphics.ColorFilter;
|
||||||
import android.graphics.LinearGradient;
|
import android.graphics.LinearGradient;
|
||||||
|
|
@ -16,9 +17,9 @@ import androidx.annotation.NonNull;
|
||||||
import org.telegram.messenger.AndroidUtilities;
|
import org.telegram.messenger.AndroidUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A glossy 3D-looking heart badge with a soft multicolor gradient (purple/blue
|
* A glossy 3D-looking heart badge with a soft multicolor gradient (purple →
|
||||||
* to orange) and a moving glossy highlight ("blik") sweeping across it, like an
|
* blue → orange) and a thin curved glossy streak ("blik") that slowly sweeps
|
||||||
* iridescent emoji sticker.
|
* diagonally across it, like light reflecting off a shiny sticker.
|
||||||
*
|
*
|
||||||
* It self-invalidates each frame, so when attached to a view via
|
* It self-invalidates each frame, so when attached to a view via
|
||||||
* {@code setRightDrawable(...)} / {@code setRightDrawable2(...)} the host keeps
|
* {@code setRightDrawable(...)} / {@code setRightDrawable2(...)} the host keeps
|
||||||
|
|
@ -29,14 +30,16 @@ public class ShimmerHeartDrawable extends Drawable {
|
||||||
|
|
||||||
private final Paint basePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
private final Paint basePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
private final Paint tintPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
private final Paint tintPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
private final Paint highlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
||||||
private final Paint shadePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
private final Paint shadePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
private final Paint glossDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
private final Paint streakPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
private final Path heart = new Path();
|
private final Path heart = new Path();
|
||||||
|
private final Path streak = new Path();
|
||||||
|
|
||||||
private int lastWidth = -1;
|
private int lastWidth = -1;
|
||||||
private int lastHeight = -1;
|
private int lastHeight = -1;
|
||||||
|
|
||||||
private static final long CYCLE_MS = 3200L;
|
private static final long CYCLE_MS = 3600L;
|
||||||
private final int size;
|
private final int size;
|
||||||
|
|
||||||
public ShimmerHeartDrawable() {
|
public ShimmerHeartDrawable() {
|
||||||
|
|
@ -47,8 +50,10 @@ public class ShimmerHeartDrawable extends Drawable {
|
||||||
this.size = sizePx;
|
this.size = sizePx;
|
||||||
basePaint.setStyle(Paint.Style.FILL);
|
basePaint.setStyle(Paint.Style.FILL);
|
||||||
tintPaint.setStyle(Paint.Style.FILL);
|
tintPaint.setStyle(Paint.Style.FILL);
|
||||||
highlightPaint.setStyle(Paint.Style.FILL);
|
|
||||||
shadePaint.setStyle(Paint.Style.FILL);
|
shadePaint.setStyle(Paint.Style.FILL);
|
||||||
|
glossDotPaint.setStyle(Paint.Style.FILL);
|
||||||
|
streakPaint.setStyle(Paint.Style.STROKE);
|
||||||
|
streakPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildHeart(Rect b) {
|
private void buildHeart(Rect b) {
|
||||||
|
|
@ -72,25 +77,35 @@ public class ShimmerHeartDrawable extends Drawable {
|
||||||
float l = b.left;
|
float l = b.left;
|
||||||
float t = b.top;
|
float t = b.top;
|
||||||
|
|
||||||
// Base diagonal gradient: deep purple (top-left) -> blue -> warm orange (bottom-right).
|
// Base diagonal gradient matching the reference: violet (top-left) →
|
||||||
|
// blue → cyan → warm orange (bottom-right).
|
||||||
basePaint.setShader(new LinearGradient(
|
basePaint.setShader(new LinearGradient(
|
||||||
l, t, l + w, t + h,
|
l + w * 0.15f, t, l + w * 0.9f, t + h,
|
||||||
new int[]{0xFF7A3CE0, 0xFF5B6BFF, 0xFF4FA8FF, 0xFFFF9A4D, 0xFFFF7A3C},
|
new int[]{0xFF8E2DE2, 0xFF5B5BFF, 0xFF2F9BFF, 0xFFFFB24D, 0xFFFF8A3D},
|
||||||
new float[]{0f, 0.32f, 0.55f, 0.82f, 1f},
|
new float[]{0f, 0.30f, 0.55f, 0.85f, 1f},
|
||||||
Shader.TileMode.CLAMP));
|
Shader.TileMode.CLAMP));
|
||||||
|
|
||||||
// A soft purple glow blob on the upper-left lobe for depth.
|
// Soft violet glow on the upper-left lobe for depth.
|
||||||
tintPaint.setShader(new RadialGradient(
|
tintPaint.setShader(new RadialGradient(
|
||||||
l + w * 0.30f, t + h * 0.30f, w * 0.55f,
|
l + w * 0.32f, t + h * 0.28f, w * 0.6f,
|
||||||
new int[]{0xCC8A4DFF, 0x00000000},
|
new int[]{0x999B30FF, 0x00000000},
|
||||||
null, Shader.TileMode.CLAMP));
|
null, Shader.TileMode.CLAMP));
|
||||||
|
|
||||||
// Soft inner shade at the bottom tip for a rounded 3D feel.
|
// Inner shade at the bottom tip for a rounded 3D feel.
|
||||||
shadePaint.setShader(new RadialGradient(
|
shadePaint.setShader(new RadialGradient(
|
||||||
l + w * 0.5f, t + h * 0.95f, w * 0.6f,
|
l + w * 0.55f, t + h * 0.92f, w * 0.55f,
|
||||||
new int[]{0x66351A6B, 0x00000000},
|
new int[]{0x55401E7A, 0x00000000},
|
||||||
null, Shader.TileMode.CLAMP));
|
null, Shader.TileMode.CLAMP));
|
||||||
|
|
||||||
|
// Fixed small specular dot, top-left (glossy sticker look).
|
||||||
|
glossDotPaint.setShader(new RadialGradient(
|
||||||
|
l + w * 0.33f, t + h * 0.27f, w * 0.16f,
|
||||||
|
new int[]{0xE6FFFFFF, 0x00FFFFFF},
|
||||||
|
null, Shader.TileMode.CLAMP));
|
||||||
|
|
||||||
|
streakPaint.setStrokeWidth(Math.max(1f, w * 0.10f));
|
||||||
|
streakPaint.setMaskFilter(new BlurMaskFilter(Math.max(1f, w * 0.06f), BlurMaskFilter.Blur.NORMAL));
|
||||||
|
|
||||||
lastWidth = b.width();
|
lastWidth = b.width();
|
||||||
lastHeight = b.height();
|
lastHeight = b.height();
|
||||||
}
|
}
|
||||||
|
|
@ -115,34 +130,46 @@ public class ShimmerHeartDrawable extends Drawable {
|
||||||
|
|
||||||
float w = b.width();
|
float w = b.width();
|
||||||
float h = b.height();
|
float h = b.height();
|
||||||
float phase = (System.currentTimeMillis() % CYCLE_MS) / (float) CYCLE_MS;
|
float l = b.left;
|
||||||
double ang = phase * 2 * Math.PI;
|
float t = b.top;
|
||||||
|
|
||||||
int save = canvas.save();
|
int save = canvas.save();
|
||||||
canvas.clipPath(heart);
|
canvas.clipPath(heart);
|
||||||
|
|
||||||
// Base colors + purple glow + bottom shade.
|
// Base colors + violet glow + bottom shade.
|
||||||
canvas.drawPath(heart, basePaint);
|
canvas.drawPath(heart, basePaint);
|
||||||
canvas.drawPath(heart, tintPaint);
|
canvas.drawPath(heart, tintPaint);
|
||||||
canvas.drawPath(heart, shadePaint);
|
canvas.drawPath(heart, shadePaint);
|
||||||
|
|
||||||
// Moving glossy highlight: a soft bright blob orbiting inside the heart.
|
// Moving curved glossy streak sweeping diagonally across the heart.
|
||||||
float hx = b.left + w * (0.5f + 0.28f * (float) Math.cos(ang));
|
float phase = (System.currentTimeMillis() % CYCLE_MS) / (float) CYCLE_MS;
|
||||||
float hy = b.top + h * (0.42f + 0.24f * (float) Math.sin(ang));
|
// travel from top-left (off-screen) to bottom-right (off-screen)
|
||||||
float hr = w * 0.45f;
|
float p = -0.4f + phase * 1.8f;
|
||||||
highlightPaint.setShader(new RadialGradient(
|
float dx = w * p; // horizontal offset of the streak
|
||||||
hx, hy, hr,
|
streak.reset();
|
||||||
new int[]{0xCCFFFFFF, 0x33FFFFFF, 0x00FFFFFF},
|
// A gently curved (bowed) vertical-ish line, slanted diagonally.
|
||||||
new float[]{0f, 0.4f, 1f},
|
float x0 = l + dx + w * 0.10f;
|
||||||
Shader.TileMode.CLAMP));
|
float y0 = t - h * 0.10f;
|
||||||
canvas.drawPath(heart, highlightPaint);
|
float xc = l + dx + w * 0.45f;
|
||||||
|
float yc = t + h * 0.50f;
|
||||||
|
float x1 = l + dx + w * 0.30f;
|
||||||
|
float y1 = t + h * 1.10f;
|
||||||
|
streak.moveTo(x0, y0);
|
||||||
|
streak.quadTo(xc, yc, x1, y1);
|
||||||
|
|
||||||
// A small fixed top-left specular dot for a glossy sticker look.
|
// Fade the streak in/out at the edges of the sweep.
|
||||||
highlightPaint.setShader(new RadialGradient(
|
float edge = Math.min(1f, Math.min(phase, 1f - phase) * 3f);
|
||||||
b.left + w * 0.34f, b.top + h * 0.28f, w * 0.18f,
|
int alpha = (int) (200 * Math.max(0f, edge));
|
||||||
new int[]{0xE6FFFFFF, 0x00FFFFFF},
|
int whiteCore = (alpha << 24) | 0x00FFFFFF;
|
||||||
null, Shader.TileMode.CLAMP));
|
streakPaint.setShader(new LinearGradient(
|
||||||
canvas.drawPath(heart, highlightPaint);
|
x0, y0, x1, y1,
|
||||||
|
new int[]{0x00FFFFFF, whiteCore, 0x00FFFFFF},
|
||||||
|
new float[]{0f, 0.5f, 1f},
|
||||||
|
Shader.TileMode.CLAMP));
|
||||||
|
canvas.drawPath(streak, streakPaint);
|
||||||
|
|
||||||
|
// Fixed specular dot.
|
||||||
|
canvas.drawPath(heart, glossDotPaint);
|
||||||
|
|
||||||
canvas.restoreToCount(save);
|
canvas.restoreToCount(save);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package tw.nekomimi.nekogram.helpers;
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.telegram.messenger.AndroidUtilities;
|
import org.telegram.messenger.AndroidUtilities;
|
||||||
import org.telegram.messenger.ApplicationLoader;
|
import org.telegram.messenger.ApplicationLoader;
|
||||||
|
|
@ -13,6 +14,8 @@ import org.telegram.messenger.Utilities;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the "sponsor" status of the current Telegram user against the
|
* Resolves the "sponsor" status of the current Telegram user against the
|
||||||
|
|
@ -142,4 +145,93 @@ public final class SponsorHelper {
|
||||||
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.sponsorStatusUpdated));
|
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.sponsorStatusUpdated));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────
|
||||||
|
// Top sponsors leaderboard
|
||||||
|
// ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public static class Sponsor {
|
||||||
|
public int rank;
|
||||||
|
public String name;
|
||||||
|
public double totalAmount;
|
||||||
|
public boolean isMe;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface SponsorsCallback {
|
||||||
|
/** Called on the UI thread. {@code sponsors} is null on error. */
|
||||||
|
void onResult(List<Sponsor> sponsors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the top-sponsors leaderboard from the backend. Authenticated by the
|
||||||
|
* current account's Telegram ID. The callback runs on the UI thread.
|
||||||
|
*/
|
||||||
|
public static void loadTopSponsors(SponsorsCallback callback) {
|
||||||
|
var user = UserConfig.getInstance(UserConfig.selectedAccount).getCurrentUser();
|
||||||
|
final long telegramId = user != null ? user.id : 0;
|
||||||
|
Utilities.globalQueue.postRunnable(() -> {
|
||||||
|
List<Sponsor> result = fetchTopSponsors(telegramId);
|
||||||
|
AndroidUtilities.runOnUIThread(() -> callback.onResult(result));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// @WorkerThread
|
||||||
|
private static List<Sponsor> fetchTopSponsors(long telegramId) {
|
||||||
|
HttpURLConnection connection = null;
|
||||||
|
try {
|
||||||
|
URL url = new URL(BASE_URL + "/api/auth/mobile/donors/leaderboard");
|
||||||
|
connection = (HttpURLConnection) url.openConnection();
|
||||||
|
connection.setInstanceFollowRedirects(true);
|
||||||
|
connection.setConnectTimeout(15000);
|
||||||
|
connection.setReadTimeout(15000);
|
||||||
|
if (telegramId != 0) {
|
||||||
|
connection.setRequestProperty("X-Tg-Id", String.valueOf(telegramId));
|
||||||
|
}
|
||||||
|
connection.setRequestProperty("Accept", "application/json");
|
||||||
|
|
||||||
|
int code = connection.getResponseCode();
|
||||||
|
if (code != HttpURLConnection.HTTP_OK) {
|
||||||
|
FileLog.d(TAG + ": donors/leaderboard returned HTTP " + code);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
try (InputStream in = connection.getInputStream()) {
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int read;
|
||||||
|
while ((read = in.read(buffer)) >= 0) {
|
||||||
|
sb.append(new String(buffer, 0, read, "UTF-8"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject json = new JSONObject(sb.toString());
|
||||||
|
if (!json.optBoolean("success", false)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JSONArray board = json.optJSONArray("leaderboard");
|
||||||
|
List<Sponsor> list = new ArrayList<>();
|
||||||
|
if (board != null) {
|
||||||
|
for (int i = 0; i < board.length(); i++) {
|
||||||
|
JSONObject o = board.optJSONObject(i);
|
||||||
|
if (o == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Sponsor s = new Sponsor();
|
||||||
|
s.rank = o.optInt("rank", i + 1);
|
||||||
|
s.name = o.optString("name", "");
|
||||||
|
s.totalAmount = o.optDouble("total_amount", 0);
|
||||||
|
s.isMe = o.optBoolean("is_me", false);
|
||||||
|
list.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
} catch (Exception e) {
|
||||||
|
FileLog.e(e);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
if (connection != null) {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
package tw.nekomimi.nekogram.settings;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.telegram.messenger.LocaleController;
|
||||||
|
import org.telegram.messenger.R;
|
||||||
|
import org.telegram.ui.Components.UItem;
|
||||||
|
import org.telegram.ui.Components.UniversalAdapter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import tw.nekomimi.nekogram.helpers.SponsorHelper;
|
||||||
|
|
||||||
|
public class FoxSponsorsActivity extends BaseNekoSettingsActivity {
|
||||||
|
|
||||||
|
private final int headerRow = rowId++;
|
||||||
|
private final int firstSponsorRow = 1000;
|
||||||
|
|
||||||
|
private List<SponsorHelper.Sponsor> sponsors = null;
|
||||||
|
private boolean loading = true;
|
||||||
|
private boolean failed = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onFragmentCreate() {
|
||||||
|
super.onFragmentCreate();
|
||||||
|
load();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void load() {
|
||||||
|
loading = true;
|
||||||
|
failed = false;
|
||||||
|
SponsorHelper.loadTopSponsors(result -> {
|
||||||
|
loading = false;
|
||||||
|
if (result == null) {
|
||||||
|
failed = true;
|
||||||
|
sponsors = null;
|
||||||
|
} else {
|
||||||
|
failed = false;
|
||||||
|
sponsors = result;
|
||||||
|
}
|
||||||
|
if (listView != null) {
|
||||||
|
listView.adapter.update(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void fillItems(ArrayList<UItem> items, UniversalAdapter adapter) {
|
||||||
|
items.add(UItem.asShadow(LocaleController.getString(R.string.FoxTopSponsorsAbout)));
|
||||||
|
if (loading) {
|
||||||
|
items.add(TextSettingsCellFactory.of(headerRow, LocaleController.getString(R.string.Loading)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (failed) {
|
||||||
|
items.add(TextSettingsCellFactory.of(headerRow, LocaleController.getString(R.string.FoxTopSponsorsError)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sponsors == null || sponsors.isEmpty()) {
|
||||||
|
items.add(TextSettingsCellFactory.of(headerRow, LocaleController.getString(R.string.FoxTopSponsorsEmpty)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
for (SponsorHelper.Sponsor s : sponsors) {
|
||||||
|
String title = s.rank + ". " + s.name + (s.isMe ? " (" + LocaleController.getString(R.string.FromYou) + ")" : "");
|
||||||
|
String amount = String.format(Locale.getDefault(), "%,.0f \u20bd", s.totalAmount);
|
||||||
|
items.add(TextSettingsCellFactory.of(firstSponsorRow + i, title, amount));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
items.add(UItem.asShadow(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onItemClick(UItem item, View view, int position, float x, float y) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getActionBarTitle() {
|
||||||
|
return LocaleController.getString(R.string.FoxTopSponsors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -68,6 +68,8 @@ public class NekoSettingsActivity extends BaseNekoSettingsActivity implements Fa
|
||||||
private final int sourceCodeRow = rowId++;
|
private final int sourceCodeRow = rowId++;
|
||||||
private final int translationRow = rowId++;
|
private final int translationRow = rowId++;
|
||||||
private final int donateRow = rowId++;
|
private final int donateRow = rowId++;
|
||||||
|
private final int supportProjectRow = rowId++;
|
||||||
|
private final int topSponsorsRow = rowId++;
|
||||||
|
|
||||||
private final int sponsorRow = 100;
|
private final int sponsorRow = 100;
|
||||||
|
|
||||||
|
|
@ -178,6 +180,10 @@ public class NekoSettingsActivity extends BaseNekoSettingsActivity implements Fa
|
||||||
items.add(UItem.asButtonSubtext(donateRow, R.drawable.msg_input_like, LocaleController.getString(R.string.Donate), LocaleController.getString(R.string.DonateAbout)).slug("donate"));
|
items.add(UItem.asButtonSubtext(donateRow, R.drawable.msg_input_like, LocaleController.getString(R.string.Donate), LocaleController.getString(R.string.DonateAbout)).slug("donate"));
|
||||||
items.add(UItem.asShadow(null));
|
items.add(UItem.asShadow(null));
|
||||||
|
|
||||||
|
items.add(UItem.asButtonSubtext(supportProjectRow, R.drawable.msg_input_like, LocaleController.getString(R.string.FoxSupportProject), LocaleController.getString(R.string.FoxSupportProjectAbout)).slug("supportProject"));
|
||||||
|
items.add(UItem.asButtonSubtext(topSponsorsRow, R.drawable.msg_premium_liststar, LocaleController.getString(R.string.FoxTopSponsors), LocaleController.getString(R.string.FoxTopSponsorsAbout)).slug("topSponsors"));
|
||||||
|
items.add(UItem.asShadow(null));
|
||||||
|
|
||||||
newsList.clear();
|
newsList.clear();
|
||||||
newsList.addAll(ConfigHelper.getNewsForSettings());
|
newsList.addAll(ConfigHelper.getNewsForSettings());
|
||||||
if (!newsList.isEmpty()) {
|
if (!newsList.isEmpty()) {
|
||||||
|
|
@ -214,6 +220,10 @@ public class NekoSettingsActivity extends BaseNekoSettingsActivity implements Fa
|
||||||
getMessagesController().openByUserName(LocaleController.getString(R.string.OfficialChannelUsername), this, 1);
|
getMessagesController().openByUserName(LocaleController.getString(R.string.OfficialChannelUsername), this, 1);
|
||||||
} else if (id == donateRow) {
|
} else if (id == donateRow) {
|
||||||
presentFragment(new NekoDonateActivity());
|
presentFragment(new NekoDonateActivity());
|
||||||
|
} else if (id == supportProjectRow) {
|
||||||
|
Browser.openUrl(getParentActivity(), "https://t.me/vpnghostbot");
|
||||||
|
} else if (id == topSponsorsRow) {
|
||||||
|
presentFragment(new FoxSponsorsActivity());
|
||||||
} else if (id == translationRow) {
|
} else if (id == translationRow) {
|
||||||
Browser.openUrl(getParentActivity(), "https://neko.crowdin.com/nekogram");
|
Browser.openUrl(getParentActivity(), "https://neko.crowdin.com/nekogram");
|
||||||
} else if (id == websiteRow) {
|
} else if (id == websiteRow) {
|
||||||
|
|
|
||||||
|
|
@ -323,6 +323,12 @@
|
||||||
<string name="StrokeOnViews">Эффекты бликов</string>
|
<string name="StrokeOnViews">Эффекты бликов</string>
|
||||||
<!-- Accessibility -->
|
<!-- Accessibility -->
|
||||||
<string name="AccessibilitySettings">Настройки специальных возможностей</string>
|
<string name="AccessibilitySettings">Настройки специальных возможностей</string>
|
||||||
<string name="FoxSponsorBadge">Спонсор</string>
|
<string name="FoxSponsorBadge">Спонсор GhostCloud</string>
|
||||||
<string name="FoxSponsorBadgeInfo">Эта отметка выдана за поддержку проекта. Спасибо!</string>
|
<string name="FoxSponsorBadgeInfo">Эта отметка выдана за поддержание проекта на 444+ рублей.</string>
|
||||||
|
<string name="FoxSupportProject">Поддержать проект</string>
|
||||||
|
<string name="FoxSupportProjectAbout">Оформите GhostCloud через нашего бота</string>
|
||||||
|
<string name="FoxTopSponsors">Топ спонсоров</string>
|
||||||
|
<string name="FoxTopSponsorsAbout">Те, кто поддерживает проект</string>
|
||||||
|
<string name="FoxTopSponsorsEmpty">Пока нет спонсоров</string>
|
||||||
|
<string name="FoxTopSponsorsError">Не удалось загрузить список</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -149,8 +149,14 @@
|
||||||
<string name="ShareNekogram">Share FoxiGram...</string>
|
<string name="ShareNekogram">Share FoxiGram...</string>
|
||||||
<string name="NekogramVersion">FoxiGram %1$s\nBased on Telegram %2$s\nDesigned by %3$s</string>
|
<string name="NekogramVersion">FoxiGram %1$s\nBased on Telegram %2$s\nDesigned by %3$s</string>
|
||||||
<string name="UpdateInstalling">Installing update...</string>
|
<string name="UpdateInstalling">Installing update...</string>
|
||||||
<string name="FoxSponsorBadge">Sponsor</string>
|
<string name="FoxSponsorBadge">Sponsor GhostCloud</string>
|
||||||
<string name="FoxSponsorBadgeInfo">You received this badge for supporting the project. Thank you!</string>
|
<string name="FoxSponsorBadgeInfo">This badge is granted for supporting the project with 444+ rubles.</string>
|
||||||
|
<string name="FoxSupportProject">Support the project</string>
|
||||||
|
<string name="FoxSupportProjectAbout">Subscribe to GhostCloud via our bot</string>
|
||||||
|
<string name="FoxTopSponsors">Top sponsors</string>
|
||||||
|
<string name="FoxTopSponsorsAbout">People who support the project</string>
|
||||||
|
<string name="FoxTopSponsorsEmpty">No sponsors yet</string>
|
||||||
|
<string name="FoxTopSponsorsError">Failed to load sponsors</string>
|
||||||
<string name="UpdateDownloading">Downloading update...</string>
|
<string name="UpdateDownloading">Downloading update...</string>
|
||||||
<string name="UpdateInstallingNotification">A notification will be shown when the update completes.</string>
|
<string name="UpdateInstallingNotification">A notification will be shown when the update completes.</string>
|
||||||
<string name="UpdateInstallingRelaunch">The app will relaunch when the update completes.</string>
|
<string name="UpdateInstallingRelaunch">The app will relaunch when the update completes.</string>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue