diff --git a/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java index 9e83939a..4c12f16e 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/HashtagPostFetchService.java @@ -14,7 +14,8 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService { private final TagsService tagsService; private final HashtagModel hashtagModel; private String nextMaxId; - private boolean moreAvailable, isLoggedIn; + private boolean moreAvailable; + private final boolean isLoggedIn; public HashtagPostFetchService(final HashtagModel hashtagModel, final boolean isLoggedIn) { this.hashtagModel = hashtagModel; @@ -24,7 +25,7 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService { @Override public void fetch(final FetchListener> fetchListener) { - if (isLoggedIn) tagsService.fetchPosts(hashtagModel.getName().toLowerCase(), nextMaxId, new ServiceCallback() { + final ServiceCallback cb = new ServiceCallback() { @Override public void onSuccess(final TagPostsFetchResponse result) { if (result == null) return; @@ -42,26 +43,9 @@ public class HashtagPostFetchService implements PostFetcher.PostFetchService { fetchListener.onFailure(t); } } - }); - else tagsService.fetchGraphQLPosts(hashtagModel.getName().toLowerCase(), nextMaxId, new ServiceCallback() { - @Override - public void onSuccess(final TagPostsFetchResponse result) { - if (result == null) return; - nextMaxId = result.getNextMaxId(); - moreAvailable = result.isMoreAvailable(); - if (fetchListener != null) { - fetchListener.onResult(result.getItems()); - } - } - - @Override - public void onFailure(final Throwable t) { - // Log.e(TAG, "onFailure: ", t); - if (fetchListener != null) { - fetchListener.onFailure(t); - } - } - }); + }; + if (isLoggedIn) tagsService.fetchPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb); + else tagsService.fetchGraphQLPosts(hashtagModel.getName().toLowerCase(), nextMaxId, cb); } @Override diff --git a/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java b/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java index 124b3f3f..2b504c50 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java +++ b/app/src/main/java/awais/instagrabber/asyncs/LocationPostFetchService.java @@ -15,15 +15,17 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService { private final LocationModel locationModel; private String nextMaxId; private boolean moreAvailable; + private final boolean isLoggedIn; - public LocationPostFetchService(final LocationModel locationModel) { + public LocationPostFetchService(final LocationModel locationModel, final boolean isLoggedIn) { this.locationModel = locationModel; + this.isLoggedIn = isLoggedIn; locationService = LocationService.getInstance(); } @Override public void fetch(final FetchListener> fetchListener) { - locationService.fetchPosts(locationModel.getId(), nextMaxId, new ServiceCallback() { + final ServiceCallback cb = new ServiceCallback() { @Override public void onSuccess(final LocationPostsFetchResponse result) { if (result == null) return; @@ -41,7 +43,9 @@ public class LocationPostFetchService implements PostFetcher.PostFetchService { fetchListener.onFailure(t); } } - }); + }; + if (isLoggedIn) locationService.fetchPosts(locationModel.getId(), nextMaxId, cb); + else locationService.fetchGraphQLPosts(locationModel.getId(), nextMaxId, cb); } @Override diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java index 7ed7dbd7..bd32ab5c 100644 --- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java @@ -44,6 +44,7 @@ import awais.instagrabber.activities.MainActivity; import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.asyncs.HashtagFetcher; import awais.instagrabber.asyncs.HashtagPostFetchService; +import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.customviews.PrimaryActionModeCallback; import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout; import awais.instagrabber.databinding.FragmentHashtagBinding; @@ -81,6 +82,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe private NestedCoordinatorLayout root; private boolean shouldRefresh = true; private boolean hasStories = false; + private boolean opening = false; private String hashtag; private HashtagModel hashtagModel; private ActionMode actionMode; @@ -199,16 +201,37 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe final View profilePicView, final View mainPostImage, final int position) { - final PostViewV2Fragment.Builder builder = PostViewV2Fragment - .builder(feedModel); - if (position >= 0) { - builder.setPosition(position); + if (opening) return; + else if (TextUtils.isEmpty(feedModel.getProfileModel().getUsername())) { + opening = true; + new PostFetcher(feedModel.getShortCode(), newFeedModel -> { + final PostViewV2Fragment.Builder builder = PostViewV2Fragment + .builder(newFeedModel); + if (position >= 0) { + builder.setPosition(position); + } + final PostViewV2Fragment fragment = builder + .setSharedProfilePicElement(profilePicView) + .setSharedMainPostElement(mainPostImage) + .build(); + fragment.show(getChildFragmentManager(), "post_view"); + opening = false; + }).execute(); + } + else { + opening = true; + final PostViewV2Fragment.Builder builder = PostViewV2Fragment + .builder(feedModel); + if (position >= 0) { + builder.setPosition(position); + } + final PostViewV2Fragment fragment = builder + .setSharedProfilePicElement(profilePicView) + .setSharedMainPostElement(mainPostImage) + .build(); + fragment.show(getChildFragmentManager(), "post_view"); + opening = false; } - final PostViewV2Fragment fragment = builder - .setSharedProfilePicElement(profilePicView) - .setSharedMainPostElement(mainPostImage) - .build(); - fragment.show(getChildFragmentManager(), "post_view"); } }; private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() { diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java index 2992f9b2..3fb1c6e3 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java @@ -47,6 +47,7 @@ import awais.instagrabber.activities.MainActivity; import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.asyncs.LocationFetcher; import awais.instagrabber.asyncs.LocationPostFetchService; +import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.customviews.PrimaryActionModeCallback; import awais.instagrabber.customviews.helpers.NestedCoordinatorLayout; import awais.instagrabber.databinding.FragmentLocationBinding; @@ -81,6 +82,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR private NestedCoordinatorLayout root; private boolean shouldRefresh = true; private boolean hasStories = false; + private boolean opening = false; private String locationId; private LocationModel locationModel; private ActionMode actionMode; @@ -197,16 +199,37 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR final View profilePicView, final View mainPostImage, final int position) { - final PostViewV2Fragment.Builder builder = PostViewV2Fragment - .builder(feedModel); - if (position >= 0) { - builder.setPosition(position); + if (opening) return; + else if (TextUtils.isEmpty(feedModel.getProfileModel().getUsername())) { + opening = true; + new PostFetcher(feedModel.getShortCode(), newFeedModel -> { + final PostViewV2Fragment.Builder builder = PostViewV2Fragment + .builder(newFeedModel); + if (position >= 0) { + builder.setPosition(position); + } + final PostViewV2Fragment fragment = builder + .setSharedProfilePicElement(profilePicView) + .setSharedMainPostElement(mainPostImage) + .build(); + fragment.show(getChildFragmentManager(), "post_view"); + opening = false; + }).execute(); + } + else { + opening = true; + final PostViewV2Fragment.Builder builder = PostViewV2Fragment + .builder(feedModel); + if (position >= 0) { + builder.setPosition(position); + } + final PostViewV2Fragment fragment = builder + .setSharedProfilePicElement(profilePicView) + .setSharedMainPostElement(mainPostImage) + .build(); + fragment.show(getChildFragmentManager(), "post_view"); + opening = false; } - final PostViewV2Fragment fragment = builder - .setSharedProfilePicElement(profilePicView) - .setSharedMainPostElement(mainPostImage) - .build(); - fragment.show(getChildFragmentManager(), "post_view"); } }; private final FeedAdapterV2.SelectionModeCallback selectionModeCallback = new FeedAdapterV2.SelectionModeCallback() { @@ -335,7 +358,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR private void setupPosts() { binding.posts.setViewModelStoreOwner(this) .setLifeCycleOwner(this) - .setPostFetchService(new LocationPostFetchService(locationModel)) + .setPostFetchService(new LocationPostFetchService(locationModel, isLoggedIn)) .setLayoutPreferences(PostsLayoutPreferences.fromJson(settingsHelper.getString(Constants.PREF_LOCATION_POSTS_LAYOUT))) .addFetchStatusChangeListener(fetching -> updateSwipeRefreshState()) .setFeedItemCallback(feedItemCallback) diff --git a/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java b/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java index 7cb5ca41..2237254d 100644 --- a/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java @@ -12,4 +12,7 @@ public interface LocationRepository { @GET("/api/v1/feed/location/{location}/") Call fetchPosts(@Path("location") final String locationId, @QueryMap Map queryParams); + + @GET("/graphql/query/") + Call fetchGraphQLPosts(@QueryMap(encoded = true) Map queryParams); } diff --git a/app/src/main/java/awais/instagrabber/utils/Constants.java b/app/src/main/java/awais/instagrabber/utils/Constants.java index 49f5aef9..24f6f914 100644 --- a/app/src/main/java/awais/instagrabber/utils/Constants.java +++ b/app/src/main/java/awais/instagrabber/utils/Constants.java @@ -58,9 +58,9 @@ public final class Constants { // spoof public static final String USER_AGENT = "Mozilla/5.0 (Linux; Android 8.1.0; motorola one Build/OPKS28.63-18-3; wv) " + "AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36 " + - "Instagram 165.1.0.29.119 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 253447818)"; + "Instagram 166.1.0.42.245 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 256099205)"; public static final String I_USER_AGENT = - "Instagram 165.1.0.29.119 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 253447818)"; + "Instagram 166.1.0.42.245 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 256099205)"; public static final String A_USER_AGENT = "https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me"; // see https://github.com/dilame/instagram-private-api/blob/master/src/core/constants.ts public static final String SUPPORTED_CAPABILITIES = "[ { \"name\": \"SUPPORTED_SDK_VERSIONS\", \"value\":" + diff --git a/app/src/main/java/awais/instagrabber/utils/LocaleUtils.java b/app/src/main/java/awais/instagrabber/utils/LocaleUtils.java index 01b52bd3..0ccfecbc 100755 --- a/app/src/main/java/awais/instagrabber/utils/LocaleUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/LocaleUtils.java @@ -62,7 +62,7 @@ public final class LocaleUtils { if (appLanguageIndex == 1) return "en"; if (appLanguageIndex == 2) return "fr"; if (appLanguageIndex == 3) return "es"; - if (appLanguageIndex == 4) return "zh"; + if (appLanguageIndex == 4) return "zh-rCN"; if (appLanguageIndex == 5) return "in"; if (appLanguageIndex == 6) return "it"; if (appLanguageIndex == 7) return "de"; @@ -72,7 +72,9 @@ public final class LocaleUtils { if (appLanguageIndex == 11) return "fa"; if (appLanguageIndex == 12) return "mk"; if (appLanguageIndex == 13) return "vi"; - if (appLanguageIndex == 14) return "hi"; + if (appLanguageIndex == 14) return "zh-rTW"; + if (appLanguageIndex == 15) return "hi"; + if (appLanguageIndex == 16) return "cs"; return null; } diff --git a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java index d14c34a5..9d552796 100644 --- a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java @@ -671,8 +671,6 @@ public final class ResponseBodyUtils { } final JSONObject feedItem = itemJson.getJSONObject("node"); final String mediaType = feedItem.optString("__typename"); - if (mediaType.isEmpty() || "GraphSuggestedUserFeedUnit".equals(mediaType)) - return null; final boolean isVideo = feedItem.optBoolean("is_video"); final long videoViews = feedItem.optLong("video_view_count", 0); diff --git a/app/src/main/java/awais/instagrabber/webservices/LocationService.java b/app/src/main/java/awais/instagrabber/webservices/LocationService.java index bb7f21c8..241453be 100644 --- a/app/src/main/java/awais/instagrabber/webservices/LocationService.java +++ b/app/src/main/java/awais/instagrabber/webservices/LocationService.java @@ -12,7 +12,9 @@ import org.json.JSONObject; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import awais.instagrabber.models.FeedModel; @@ -27,7 +29,7 @@ import retrofit2.Retrofit; public class LocationService extends BaseService { private static final String TAG = "LocationService"; - private final LocationRepository repository; + private final LocationRepository repository, webRepository; private static LocationService instance; @@ -36,6 +38,10 @@ public class LocationService extends BaseService { .baseUrl("https://i.instagram.com") .build(); repository = retrofit.create(LocationRepository.class); + final Retrofit webRetrofit = getRetrofitBuilder() + .baseUrl("https://www.instagram.com") + .build(); + webRepository = webRetrofit.create(LocationRepository.class); } public static LocationService getInstance() { @@ -48,7 +54,7 @@ public class LocationService extends BaseService { public void fetchPosts(@NonNull final String locationId, final String maxId, final ServiceCallback callback) { - final ImmutableMap.Builder builder = ImmutableMap.builder(); + final ImmutableMap.Builder builder = ImmutableMap.builder(); if (!TextUtils.isEmpty(maxId)) { builder.put("max_id", maxId); } @@ -117,6 +123,82 @@ public class LocationService extends BaseService { return feedModels; } + public void fetchGraphQLPosts(@NonNull final String locationId, + final String maxId, + final ServiceCallback callback) { + final Map queryMap = new HashMap<>(); + queryMap.put("query_hash", "36bd0f2bf5911908de389b8ceaa3be6d"); + queryMap.put("variables", "{" + + "\"id\":\"" + locationId + "\"," + + "\"first\":25," + + "\"after\":\"" + (maxId == null ? "" : maxId) + "\"" + + "}"); + final Call request = webRepository.fetchGraphQLPosts(queryMap); + request.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + try { + if (callback == null) { + return; + } + final String body = response.body(); + if (TextUtils.isEmpty(body)) { + callback.onSuccess(null); + return; + } + final LocationPostsFetchResponse tagPostsFetchResponse = parseGraphQLResponse(body); + callback.onSuccess(tagPostsFetchResponse); + } catch (JSONException e) { + Log.e(TAG, "onResponse", e); + callback.onFailure(e); + } + } + + @Override + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + if (callback != null) { + callback.onFailure(t); + } + } + }); + } + + private LocationPostsFetchResponse parseGraphQLResponse(@NonNull final String body) throws JSONException { + final JSONObject rootroot = new JSONObject(body); + final JSONObject root = rootroot.getJSONObject("data").getJSONObject("location").getJSONObject("edge_location_to_media"); + final boolean moreAvailable = root.getJSONObject("page_info").optBoolean("has_next_page"); + final String nextMaxId = root.getJSONObject("page_info").optString("end_cursor"); + final int numResults = root.optInt("count"); + final String status = rootroot.optString("status"); + final JSONArray itemsJson = root.optJSONArray("edges"); + final List items = parseGraphQLItems(itemsJson); + return new LocationPostsFetchResponse( + moreAvailable, + nextMaxId, + numResults, + status, + items + ); + } + + private List parseGraphQLItems(final JSONArray items) throws JSONException { + if (items == null) { + return Collections.emptyList(); + } + final List feedModels = new ArrayList<>(); + for (int i = 0; i < items.length(); i++) { + final JSONObject itemJson = items.optJSONObject(i); + if (itemJson == null) { + continue; + } + final FeedModel feedModel = ResponseBodyUtils.parseGraphQLItem(itemJson); + if (feedModel != null) { + feedModels.add(feedModel); + } + } + return feedModels; + } + public static class LocationPostsFetchResponse { private boolean moreAvailable; private String nextMaxId; diff --git a/app/src/main/res/layout/fragment_hashtag.xml b/app/src/main/res/layout/fragment_hashtag.xml index 32aaf043..2861987b 100644 --- a/app/src/main/res/layout/fragment_hashtag.xml +++ b/app/src/main/res/layout/fragment_hashtag.xml @@ -41,7 +41,7 @@ android:gravity="center" android:padding="8dp" android:textAppearance="@style/TextAppearance.AppCompat" - app:layout_constraintBottom_toTopOf="@id/btnFollowTag" + app:layout_constraintBottom_toTopOf="@id/fav_chip" app:layout_constraintStart_toEndOf="@id/mainHashtagImage" app:layout_constraintTop_toTopOf="@id/mainHashtagImage" tools:text="35 Posts" /> diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml deleted file mode 100755 index bfe80844..00000000 --- a/app/src/main/res/values-zh/strings.xml +++ /dev/null @@ -1,323 +0,0 @@ - - - 关于 - 私聊 - 设定 - 下载 - 搜索用户名… - 比较 - 复制文字时出错 - 已复制到剪贴板! - 报告 - 密码 (最多32字符) - 设置密码 (最多32个字符) - 密码 - 好的 - - 取消 - - 确定 - 往上 - 不再显示 - 当前目录 - 最爱 - 发现 - 留言 - 通知 - 精彩:%s - 用户快拍 - 日志 - 下方显示工具栏 - 启动时检查更新 - 下载帖子到用户名文件夹 - 自动加载用户所有帖子 - 查看快拍后将其标记为已读 - 快拍作者会知道您已看过 - 查看私信后将其标记为已读 - 其他成员会知道你看过了 - 启用活动通知 - 载入主页时出错!\n请尝试再次登录并搜索。 - 创建下载文件夹时出错 - 显示用户动态 (只在登录后有效) - 保存至自定义文件夹 - 选择文件夹 - 主题 - 仅影响登录用户: - 仅影响匿名用户: - 隐私 - 使用 Instadp 获取高清头像 - 使用 storiesig 获取快拍和精彩 - 快拍查看工具 - 导入/导出 - 语言 - 怎么办? - %s\n帖子 - %s 个帖子 - %s\n粉丝 - %s\n已关注 - 视频帖 - 图片帖 - 自动播放视频 - 视频默认静音 - %s - %s - 注: 当前[帖子] 键将默认下载您说看到的那一张照片,直到按返回键退出本帖为止。 - 选择要下载的 - 当前 [帖子] - 当前 - 整个图集 - 显示快拍 - 到底啦! - 请耐心等待! - 查看帖子 - 查看帖子 - Spotify - 匿名用户不能使用此功能! - 投票 - 投票成功! - 您已投票过了! - 回复 - 回答… - 回答成功! - 回复快拍 - 回复… - 测验 - 您已回答过了! - 提及 - 私密账户 - 取消关注后,您将无法访问帖子!您确定吗? - 你可以通过右下角的 更多-> 账户 来登录,或者您无须登录即可查看公开账户! - 您可以向左/向右滑动查看探索/订阅,或在下方进行搜索! - 暂未发帖 - 无此类帖! - 目前版本: v%s - 阅读更多… - 登录 - 退出 - 隐身浏览Instagram - 删除所有帐户 - 这将从应用中移除所有已添加的账户!\n若要移除其中一个账户,长按账户切换对话框中的那个账户即可。\n您想要继续吗? - 发送调试日志 - 日期格式 - 参观项目页面 - 加入 Telegram 群组 - 加入 Matrix 群组 - 已赞 - 已保存 - 已标记 - 消息 - 赞 (%s) - - 取消赞 (%s) - 取消点赞 - 加入收藏 - 解除收藏 - 关注 - 脱粉 - 加入收藏 - 取消收藏 - 拉黑 - 解禁 - 限制 - 放开 - 地图 - 导出 - 导入 - 导出登录 - 帐户 - 设置 - 收藏 - 导入设置 - 导入登录 - 导入账户 - 导入收藏 - 成功导入! - 导入失败! - 成功导出! - 导出失败! - 密码为空! - 刷新 - 获取 cookies - 桌面版 - 使用自定义格式 - 分界 - 时间格式 - 日期格式 - 预览 - 互换时间和日期位置 - 最爱面板让您添加您最爱的标签和用户。\n\n而快捷通道是用来迅速切换账户的。\n\n注1: 请确保各个账户已登录 [设定 > 登录] 来添加账户!\n\n注2: 登出当前账户之后再登录另一个账户。 - 无法删除正在使用的账户 - 您确定要删除\"%s\"吗? - 宽: %d\n高: %d - \n色深: - 选择高清头像服务\n(不影响标签) - 打开主页 - 查看头像 - - 分享了 - 分享了媒体 - 分享了定时消息 - 回复了快拍 - 对快拍留下了心情 - 在快拍中提到了某人 - 不支持此类消息 - 打开链接 - 复制文本 - 下载附件 - 点赞消息 - 取消点赞 - 撤回消息 - 查看作者资料 - 分享了 %s 的帖子 - 媒体格式不明 - 消息已过期! - 已送达 - 已发送 - 已打开 - 已回放 - 发送中… - 已屏蔽 - 为您推荐 - 已截屏 - 无法发送 - 非常成功! - 离开 - 离开此聊天吗? - 移除成员 - 已离开成员 - 直接下载 - 直接下载至手机! - 读取帖子 - 请给予权限再尝试下载! - 下载开始 - 下载完成 - 帖子下载中… - 媒体下载中 - 大头贴下载中 - 帖子以下载至下载文件夹! - 出现不详错误!!! - 创建文件夹时出错! - 下载文件时出错 - 您一次只能下载100个帖子。切勿贪得无厌! - 复制用户名 - 复制评论 - 回复评论 - 赞评论 - 取消赞 - 删除评论 - 评论要写字的! - 要搜索用户名吗? - 要搜索标签吗? - 要搜索地点吗? - 粉丝 - 关注 - 比较粉丝和关注 - 互粉 - 未关注 %s - %s 未关注 - 载入 cookies 时出错 - 成功载入 cookies!\n若你仍不能查看私密页面/帖子,重新登录! - 撰写一条新评论… - 撰写一条新消息… - 赞了你的帖子 - 评论了你的帖子 - 关注了您 - 提到了您 - 在一个帖子中标记了你 - 请求关注您 - 同意请求 - 拒绝请求 - 分享这个公开帖子给… - 这是一个私密帖!分享给那些可以查看他们的人! - 这个类别有点空… - 检测到有新版本! (%s) - 注意:若您下载的是F-Droid版本,那您还得在F-Droid更新!GitHub版本也是如此。 - 感谢阁下更新 Barinsta! - 应用崩溃了 - 糟糕.. 应用崩溃了,不过别担心,你可以向开发者发送错误报告来帮助他修复问题。(: - 使用 AMOLED 夜间主题 - 动态 - 选择图片 - 上传中... - 您有: - %d 位新粉丝 - %d 个评论回复 - %d 个评论点赞 - %d 个帖子提到了您 - %d 个赞 - 您在点击此通知前登出了?! - 动态 - 用户 - 更多 - 私信 - 已选择 %d 个 - 重新登录 - 如果遇到问题,请刷新您的 cookie - 成功退出! - 信息 - 标记为已读 - 在下次更新之前不再显示 - 版本 - 初始界面 - 常规设置 - 主题 - 下载 - 本地化 - 账户 - 当前登录不管用?添加同一个账户即可修复。 - 添加帐户 - 许可证(仅英文) - 访问我们的网站 - 获取支持、谈天说地、结交好友。祝愉快! - 在 GitHub 上查看我们的源代码 - 围观、加星、报错、贡献,(再次)祝愉快! - 通过电子邮件发送反馈 - 第三方库 - 本应用使用了以下第三方开源库: - 提醒 - 请合理利用此工具,并请合法使用下载内容。 - 白色 - 黑色 - 浅色主题 - 深色主题 - 咖啡棕 - Material 黑 - 已添加至收藏夹 - 添加到收藏 - 帐户 - 话题 - 地点 - 未知 - 已从收藏夹中删除 - 备份 & 还原 - 创建 - 还原 - 文件: - 输入密码 - 选择备份文件(.zaai/.backup) - 应用 - 保存 - 字幕 - 视频播放器时间线 - 正在赞… - 点赞失败 - 取消点赞失败 - 正取消点赞… - 操控 - 正保存… - 正在删除…… - 保存失败 - 删除失败 - 下载中... - 下载项 %d 中的 %d - 删除 - 评论 - 布局 - 正在打开帖子... - 分享 - - %d 个赞 - - - %d 条评论 - - diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 49623228..0da39041 100755 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -15,7 +15,8 @@ Persian [Thanks to @farzadx] Macedonian [Thanks to @snajdovski] Vietnamese [Thanks to @fouze555] - Chinese Traditional + Chinese Traditional [Thanks to @Still34] + Hindi 0 @@ -30,6 +31,8 @@ 9 10 11 + 12 + 13 Auto / Follow System diff --git a/fastlane/metadata/android/en-US/changelogs/53.txt b/fastlane/metadata/android/en-US/changelogs/53.txt new file mode 100644 index 00000000..ea880748 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/53.txt @@ -0,0 +1,21 @@ +* Resolve F-Droid compliance issues +* Restore anonymous access to hashtags and locations +* Various bug fixes + +Because F-Droid skipped v19.0.0, the previous changelog is also provided here: + +## Features +* We're now (officially) Barinsta! New name, new icon! +* Total revamp of UI. Specifically, the profile/hashtag/location, feed, and discover tabs have been redone. Explore our new look! +* You can now pick a feed layout and a theme! +* You can now DM a user from their profile page! + +## Improvements +* You can now skip to another user in story feed by clicking the skip buttons +* URLs and emails in descrptions are now tappable +* Downloaded file names now use shortcode instead of post ID +* Support `FELIX_SHARE` DM message type + +## Fixes +* Fix file corruption bug +* Fix video autoplay & auto-mute issues \ No newline at end of file