this commit fixes #544 and makes fastScroller to paginate as per #556 & preparing for 3.0.0

This commit is contained in:
Kosh 2017-06-04 03:42:15 +08:00
parent b1403e912c
commit 500efcf8e6
27 changed files with 311 additions and 351 deletions

View File

@ -28,8 +28,8 @@ android {
applicationId "com.fastaccess.github"
minSdkVersion 21
targetSdkVersion 26
versionCode 253
versionName "2.5.3"
versionCode 300
versionName "3.0.0"
signingConfig signingConfigs.signing
buildConfigString "GITHUB_CLIENT_ID", (buildProperties.secrets['github_client_id'] | buildProperties.notThere['github_client_id']).string
buildConfigString "GITHUB_SECRET", (buildProperties.secrets['github_secret'] | buildProperties.notThere['github_secret']).string

View File

@ -1,144 +0,0 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.vending.billing;
import android.os.Bundle;
/**
* InAppBillingService is the service that provides in-app billing version 3 and beyond.
* This service provides the following features:
* 1. Provides a new API to get details of in-app items published for the app including
* price, type, title and description.
* 2. The purchase flow is synchronous and purchase information is available immediately
* after it completes.
* 3. Purchase information of in-app purchases is maintained within the Google Play system
* till the purchase is consumed.
* 4. An API to consume a purchase of an inapp item. All purchases of one-time
* in-app items are consumable and thereafter can be purchased again.
* 5. An API to get current purchases of the user immediately. This will not contain any
* consumed purchases.
*
* All calls will give a response code with the following possible values
* RESULT_OK = 0 - success
* RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog
* RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested
* RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase
* RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API
* RESULT_ERROR = 6 - Fatal error during the API action
* RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
* RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
*/
interface IInAppBillingService {
/**
* Checks support for the requested billing API version, package and in-app type.
* Minimum API version supported by this interface is 3.
* @param apiVersion the billing version which the app is using
* @param packageName the package name of the calling app
* @param type type of the in-app item being purchased "inapp" for one-time purchases
* and "subs" for subscription.
* @return RESULT_OK(0) on success, corresponding result code on failures
*/
int isBillingSupported(int apiVersion, String packageName, String type);
/**
* Provides details of a list of SKUs
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
* with a list JSON strings containing the productId, price, title and description.
* This API can be called with a maximum of 20 SKUs.
* @param apiVersion billing API version that the Third-party is using
* @param packageName the package name of the calling app
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "DETAILS_LIST" with a StringArrayList containing purchase information
* in JSON format similar to:
* '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00",
* "title : "Example Title", "description" : "This is an example description" }'
*/
Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle);
/**
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
* the type, a unique purchase token and an optional developer payload.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type the type of the in-app item ("inapp" for one-time purchases
* and "subs" for subscription).
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
* TODO: change this to app-specific keys.
*/
Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type,
String developerPayload);
/**
* Returns the current SKUs owned by the user of the type and package name specified along with
* purchase information and a signature of the data to be validated.
* This will return all SKUs that have been purchased in V3 and managed items purchased using
* V1 and V2 that have not been consumed.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param type the type of the in-app items being requested
* ("inapp" for one-time purchases and "subs" for subscription).
* @param continuationToken to be set as null for the first call, if the number of owned
* skus are too many, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken);
/**
* Consume the last purchase of the given SKU. This will result in this item being removed
* from all subsequent responses to getPurchases() and allow re-purchase of this item.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param purchaseToken token in the purchase information JSON that identifies the purchase
* to be consumed
* @return 0 if consumption succeeded. Appropriate error values for failures.
*/
int consumePurchase(int apiVersion, String packageName, String purchaseToken);
}

View File

@ -47,6 +47,9 @@ public interface UserRestService {
@GET("users/{username}/starred") Observable<Pageable<Repo>>
getStarred(@Path("username") @NonNull String username, @Query("page") int page);
@GET("users/{username}/starred?per_page=1") Observable<Pageable<Repo>>
getStarredCount(@Path("username") @NonNull String username);
@GET("users/{username}/following")
Observable<Pageable<User>> getFollowing(@Path("username") @NonNull String username, @Query("page") int page);

View File

@ -21,7 +21,6 @@ public class RxHelper {
public static <T> Observable<T> safeObservable(@NonNull Observable<T> observable) {
return getObserver(observable)
.onErrorReturn(throwable -> (T) new Object())
.doOnError(Throwable::printStackTrace);
}

View File

@ -4,7 +4,6 @@ import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.text.SpannableStringBuilder;
import com.fastaccess.helper.Logger;
import com.fastaccess.ui.widgets.SpannableBuilder;
import net.nightwhistler.htmlspanner.TagNodeHandler;
@ -47,7 +46,6 @@ import lombok.NoArgsConstructor;
TodoItems todoItem = null;
if (node.getChildTags() != null && node.getChildTags().length > 0) {
for (TagNode tagNode : node.getChildTags()) {
Logger.e(tagNode.getName(), tagNode.getAttributes(), tagNode.getText());
if (tagNode.getName() != null && tagNode.getName().equals("input")) {
todoItem = new TodoItems();
todoItem.isChecked = tagNode.getAttributeByName("checked") != null;

View File

@ -16,12 +16,19 @@ import java.util.ArrayList;
public class FeedsAdapter extends BaseRecyclerAdapter<Event, FeedsViewHolder, BaseViewHolder.OnItemClickListener<Event>> {
public FeedsAdapter(@NonNull ArrayList<Event> Events) {
super(Events);
private boolean noImage;
public FeedsAdapter(@NonNull ArrayList<Event> events) {
this(events, false);
}
public FeedsAdapter(@NonNull ArrayList<Event> events, boolean noImage) {
super(events);
this.noImage = noImage;
}
@Override protected FeedsViewHolder viewHolder(ViewGroup parent, int viewType) {
return new FeedsViewHolder(FeedsViewHolder.getView(parent), this);
return new FeedsViewHolder(FeedsViewHolder.getView(parent, noImage), this);
}
@Override protected void onBindView(FeedsViewHolder holder, int position) {

View File

@ -29,7 +29,7 @@ import butterknife.BindView;
public class FeedsViewHolder extends BaseViewHolder<Event> {
@BindView(R.id.avatarLayout) AvatarLayout avatar;
@Nullable @BindView(R.id.avatarLayout) AvatarLayout avatar;
@BindView(R.id.description) FontTextView description;
@BindView(R.id.title) FontTextView title;
@BindView(R.id.date) FontTextView date;
@ -40,15 +40,21 @@ public class FeedsViewHolder extends BaseViewHolder<Event> {
super(itemView, adapter);
}
public static View getView(@NonNull ViewGroup viewGroup) {
return getView(viewGroup, R.layout.feeds_row_item);
public static View getView(@NonNull ViewGroup viewGroup, boolean noImage) {
if (noImage) {
return getView(viewGroup, R.layout.feeds_row_no_image_item);
} else {
return getView(viewGroup, R.layout.feeds_row_item);
}
}
@Override public void bind(@NonNull Event eventsModel) {
if (eventsModel.getActor() != null) {
avatar.setUrl(eventsModel.getActor().getAvatarUrl(), eventsModel.getActor().getLogin(), eventsModel.getActor().isOrganizationType());
} else {
avatar.setUrl(null, null);
if (avatar != null) {
if (eventsModel.getActor() != null) {
avatar.setUrl(eventsModel.getActor().getAvatarUrl(), eventsModel.getActor().getLogin(), eventsModel.getActor().isOrganizationType());
} else {
avatar.setUrl(null, null);
}
}
SpannableBuilder spannableBuilder = SpannableBuilder.builder();
spannableBuilder.append(eventsModel.getActor() != null ? eventsModel.getActor().getLogin() : "N/A").append(" ");

View File

@ -114,13 +114,9 @@ public abstract class BaseFragment<V extends BaseMvp.FAView, P extends BasePrese
callback.onRequireLogin();
}
@Override public void onMessageDialogActionClicked(boolean isOk, @Nullable Bundle bundle) {
@Override public void onMessageDialogActionClicked(boolean isOk, @Nullable Bundle bundle) {}
}
@Override public void onDialogDismissed() {
}
@Override public void onDialogDismissed() {}
@Override public void onLogoutPressed() {
callback.onLogoutPressed();

View File

@ -6,6 +6,7 @@ import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.text.Editable;
import android.view.KeyEvent;
@ -15,6 +16,7 @@ import android.view.inputmethod.EditorInfo;
import android.widget.PopupWindow;
import android.widget.Toast;
import com.evernote.android.state.State;
import com.fastaccess.R;
import com.fastaccess.data.dao.LabelModel;
import com.fastaccess.data.dao.MilestoneModel;
@ -49,7 +51,6 @@ import butterknife.OnClick;
import butterknife.OnEditorAction;
import butterknife.OnTextChanged;
import es.dmoral.toasty.Toasty;
import com.evernote.android.state.State;
/**
* Created by Kosh on 09 Apr 2017, 6:23 PM
@ -174,52 +175,37 @@ public class FilterIssuesActivity extends BaseActivity<FilterIssuesActivityMvp.V
}
@SuppressLint("InflateParams") @OnClick(R.id.labels) public void onLabelsClicked() {
if (hideWindow()) return;
if (labels.getTag() != null) return;
labels.setTag(true);
ViewHolder viewHolder = new ViewHolder(LayoutInflater.from(this).inflate(R.layout.simple_list_dialog, null));
popupWindow = new PopupWindow(this);
popupWindow.setContentView(viewHolder.view);
popupWindow.setOutsideTouchable(true);
popupWindow.setBackgroundDrawable(new ColorDrawable(ViewHelper.getWindowBackground(this)));
popupWindow.setElevation(getResources().getDimension(R.dimen.spacing_normal));
setupPopupWindow(viewHolder);
viewHolder.recycler.setAdapter(getLabelsAdapter());
AnimHelper.revealPopupWindow(popupWindow, labels);
}
@SuppressLint("InflateParams") @OnClick(R.id.milestone) public void onMilestoneClicked() {
if (hideWindow()) return;
if (milestone.getTag() != null) return;
milestone.setTag(true);
ViewHolder viewHolder = new ViewHolder(LayoutInflater.from(this).inflate(R.layout.simple_list_dialog, null));
popupWindow = new PopupWindow(this);
popupWindow.setContentView(viewHolder.view);
popupWindow.setOutsideTouchable(true);
popupWindow.setElevation(getResources().getDimension(R.dimen.spacing_micro));
popupWindow.setBackgroundDrawable(new ColorDrawable(ViewHelper.getWindowBackground(this)));
popupWindow.setElevation(getResources().getDimension(R.dimen.spacing_normal));
setupPopupWindow(viewHolder);
viewHolder.recycler.setAdapter(getMilestonesAdapter());
AnimHelper.revealPopupWindow(popupWindow, milestone);
}
@SuppressLint("InflateParams") @OnClick(R.id.assignee) public void onAssigneeClicked() {
if (hideWindow()) return;
if (assignee.getTag() != null) return;
assignee.setTag(true);
ViewHolder viewHolder = new ViewHolder(LayoutInflater.from(this).inflate(R.layout.simple_list_dialog, null));
popupWindow = new PopupWindow(this);
popupWindow.setContentView(viewHolder.view);
popupWindow.setOutsideTouchable(true);
popupWindow.setElevation(getResources().getDimension(R.dimen.spacing_micro));
popupWindow.setBackgroundDrawable(new ColorDrawable(ViewHelper.getWindowBackground(this)));
popupWindow.setElevation(getResources().getDimension(R.dimen.spacing_normal));
setupPopupWindow(viewHolder);
viewHolder.recycler.setAdapter(getAssigneesAdapter());
AnimHelper.revealPopupWindow(popupWindow, assignee);
}
@SuppressLint("InflateParams") @OnClick(R.id.sort) public void onSortClicked() {
if (hideWindow()) return;
if (sort.getTag() != null) return;
sort.setTag(true);
ViewHolder viewHolder = new ViewHolder(LayoutInflater.from(this).inflate(R.layout.simple_list_dialog, null));
popupWindow = new PopupWindow(this);
popupWindow.setContentView(viewHolder.view);
popupWindow.setOutsideTouchable(true);
popupWindow.setElevation(getResources().getDimension(R.dimen.spacing_micro));
popupWindow.setBackgroundDrawable(new ColorDrawable(ViewHelper.getWindowBackground(this)));
popupWindow.setElevation(getResources().getDimension(R.dimen.spacing_normal));
setupPopupWindow(viewHolder);
ArrayList<String> lists = new ArrayList<>();
Collections.addAll(lists, getResources().getStringArray(R.array.sort_prs_issues));
lists.add(CommentsHelper.getThumbsUp());
@ -301,12 +287,23 @@ public class FilterIssuesActivity extends BaseActivity<FilterIssuesActivityMvp.V
}
}
private boolean hideWindow() {
if (popupWindow != null && popupWindow.isShowing()) {
popupWindow.dismiss();
return true;
private void setupPopupWindow(@NonNull ViewHolder viewHolder) {
if (popupWindow == null) {
popupWindow = new PopupWindow(this);
popupWindow.setElevation(getResources().getDimension(R.dimen.spacing_micro));
popupWindow.setOutsideTouchable(true);
popupWindow.setBackgroundDrawable(new ColorDrawable(ViewHelper.getWindowBackground(this)));
popupWindow.setElevation(getResources().getDimension(R.dimen.spacing_normal));
popupWindow.setOnDismissListener(() -> new Handler().postDelayed(() -> {
//hacky way to dismiss on re-selecting tab.
if (assignee == null || milestone == null || sort == null || labels == null) return;
assignee.setTag(null);
milestone.setTag(null);
sort.setTag(null);
labels.setTag(null);
}, 100));
}
return false;
popupWindow.setContentView(viewHolder.view);
}
private void onSearch() {
@ -470,7 +467,7 @@ public class FilterIssuesActivity extends BaseActivity<FilterIssuesActivityMvp.V
onSearch();
}
private void appendSort(String item) {
private void appendSort(String item) {
dismissPopup();
appendIfEmpty();
Resources resources = getResources();

View File

@ -28,7 +28,7 @@ open class ProfileEventsFragment : BaseFragment<ProfileEvents.View, ProfileEvent
val recycler by lazy { view!!.findViewById(R.id.recycler) as DynamicRecyclerView }
val refresh by lazy { view!!.findViewById(R.id.refresh) as SwipeRefreshLayout }
val stateLayout by lazy { view!!.findViewById(R.id.stateLayout) as StateLayout }
val adapter by lazy { FeedsAdapter(presenter.getEvents()) }
val adapter by lazy { FeedsAdapter(presenter.getEvents(), true) }
private var onLoadMore: OnLoadMore<String>? = null
override fun fragmentLayout(): Int {
@ -44,6 +44,7 @@ open class ProfileEventsFragment : BaseFragment<ProfileEvents.View, ProfileEvent
getLoadMore().setCurrent_page(presenter.currentPage, presenter.previousTotal)
recycler.adapter = adapter
recycler.addOnScrollListener(getLoadMore())
recycler.addDivider()
if (presenter.getEvents().isEmpty() && !presenter.isApiCalled) {
onRefresh()
}
@ -117,6 +118,11 @@ open class ProfileEventsFragment : BaseFragment<ProfileEvents.View, ProfileEvent
SchemeParser.launchUri(context, Uri.parse(item.item))
}
override fun onScrollTop(index: Int) {
super.onScrollTop(index)
recycler.scrollToPosition(0)
}
private fun showReload() {
hideProgress()
stateLayout.showReload(adapter.itemCount)

View File

@ -73,16 +73,8 @@ class ProfileEventsPresenter : BasePresenter<ProfileEvents.View>(), ProfileEvent
}
override fun onWorkOffline() {
if (eventsModels.isEmpty()) {
manageDisposable(RxHelper.getObserver(Event.getEvents().toObservable())
.subscribe({ modelList ->
if (modelList != null) {
sendToView { view -> view.onNotifyAdapter(modelList, 1) }
}
}, { it.printStackTrace() }))
} else {
sendToView({ it.hideProgress() })
}
//TODO
sendToView({ it.hideProgress() })
}
override fun onItemClick(position: Int, v: View, item: Event) {

View File

@ -1,5 +1,6 @@
package com.fastaccess.ui.modules.profile.starred;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -14,6 +15,7 @@ import com.fastaccess.helper.Bundler;
import com.fastaccess.provider.rest.loadmore.OnLoadMore;
import com.fastaccess.ui.adapter.ReposAdapter;
import com.fastaccess.ui.base.BaseFragment;
import com.fastaccess.ui.modules.repos.RepoPagerMvp;
import com.fastaccess.ui.widgets.StateLayout;
import com.fastaccess.ui.widgets.recyclerview.DynamicRecyclerView;
@ -32,6 +34,7 @@ public class ProfileStarredFragment extends BaseFragment<ProfileStarredMvp.View,
@BindView(R.id.stateLayout) StateLayout stateLayout;
private OnLoadMore<String> onLoadMore;
private ReposAdapter adapter;
private RepoPagerMvp.TabsBadgeListener tabsBadgeListener;
public static ProfileStarredFragment newInstance(@NonNull String username) {
ProfileStarredFragment view = new ProfileStarredFragment();
@ -39,6 +42,21 @@ public class ProfileStarredFragment extends BaseFragment<ProfileStarredMvp.View,
return view;
}
@Override public void onAttach(Context context) {
super.onAttach(context);
if (getParentFragment() instanceof RepoPagerMvp.TabsBadgeListener) {
tabsBadgeListener = (RepoPagerMvp.TabsBadgeListener) getParentFragment();
} else if (context instanceof RepoPagerMvp.TabsBadgeListener) {
tabsBadgeListener = (RepoPagerMvp.TabsBadgeListener) context;
}
}
@Override public void onDetach() {
tabsBadgeListener = null;
super.onDetach();
}
@Override public void onNotifyAdapter(@Nullable List<Repo> items, int page) {
hideProgress();
if (items == null || items.isEmpty()) {
@ -81,9 +99,7 @@ public class ProfileStarredFragment extends BaseFragment<ProfileStarredMvp.View,
}
@Override public void showProgress(@StringRes int resId) {
refresh.setRefreshing(true);
stateLayout.showProgress();
}
@ -109,6 +125,12 @@ public class ProfileStarredFragment extends BaseFragment<ProfileStarredMvp.View,
return onLoadMore;
}
@Override public void onUpdateCount(int starredCount) {
if (tabsBadgeListener != null) {
tabsBadgeListener.onSetBadge(3, starredCount);
}
}
@Override public void onRefresh() {
getPresenter().onCallApi(1, getArguments().getString(BundleConstant.EXTRA));
}

View File

@ -22,6 +22,8 @@ interface ProfileStarredMvp {
void onNotifyAdapter(@Nullable List<Repo> items, int page);
@NonNull OnLoadMore<String> getLoadMore();
void onUpdateCount(int starredCount);
}
interface Presenter extends BaseMvp.FAPresenter,

View File

@ -5,6 +5,7 @@ import android.support.annotation.Nullable;
import android.view.View;
import com.fastaccess.data.dao.NameParser;
import com.fastaccess.data.dao.Pageable;
import com.fastaccess.data.dao.model.Repo;
import com.fastaccess.helper.RxHelper;
import com.fastaccess.provider.rest.RestProvider;
@ -13,12 +14,15 @@ import com.fastaccess.ui.modules.repos.RepoPagerActivity;
import java.util.ArrayList;
import io.reactivex.Observable;
/**
* Created by Kosh on 03 Dec 2016, 3:48 PM
*/
class ProfileStarredPresenter extends BasePresenter<ProfileStarredMvp.View> implements ProfileStarredMvp.Presenter {
@com.evernote.android.state.State int starredCount = -1;
private ArrayList<Repo> repos = new ArrayList<>();
private int page;
private int previousTotal;
@ -62,14 +66,28 @@ class ProfileStarredPresenter extends BasePresenter<ProfileStarredMvp.View> impl
sendToView(ProfileStarredMvp.View::hideProgress);
return;
}
makeRestCall(RestProvider.getUserService().getStarred(parameter, page),
repoModelPageable -> {
lastPage = repoModelPageable.getLast();
if (getCurrentPage() == 1) {
manageObservable(Repo.saveStarred(repoModelPageable.getItems(), parameter));
}
sendToView(view -> view.onNotifyAdapter(repoModelPageable.getItems(), page));
});
Observable<Pageable<Repo>> observable;
if (starredCount == -1) {
observable = Observable.zip(RestProvider.getUserService().getStarred(parameter, page),
RestProvider.getUserService().getStarredCount(parameter), (repoPageable, count) -> {
if (count != null) {
starredCount = count.getLast();
}
return repoPageable;
});
} else {
observable = RestProvider.getUserService().getStarred(parameter, page);
}
makeRestCall(observable, repoModelPageable -> {
lastPage = repoModelPageable.getLast();
if (getCurrentPage() == 1) {
manageObservable(Repo.saveStarred(repoModelPageable.getItems(), parameter));
}
sendToView(view -> {
view.onUpdateCount(starredCount);
view.onNotifyAdapter(repoModelPageable.getItems(), page);
});
});
}
@NonNull @Override public ArrayList<Repo> getRepos() {
@ -79,7 +97,11 @@ class ProfileStarredPresenter extends BasePresenter<ProfileStarredMvp.View> impl
@Override public void onWorkOffline(@NonNull String login) {
if (repos.isEmpty()) {
manageDisposable(RxHelper.getObserver(Repo.getStarred(login).toObservable()).subscribe(repoModels ->
sendToView(view -> view.onNotifyAdapter(repoModels, 1))));
sendToView(view -> {
starredCount = -1;
view.onUpdateCount(repoModels != null ? repoModels.size() : 0);
view.onNotifyAdapter(repoModels, 1);
})));
} else {
sendToView(ProfileStarredMvp.View::hideProgress);
}

View File

@ -93,14 +93,14 @@ class RepoCommitsPresenter extends BasePresenter<RepoCommitsMvp.View> implements
RestProvider.getRepoService().getTags(login, repoId),
(branchPageable, tags) -> {
ArrayList<BranchesModel> branchesModels = new ArrayList<>();
if (branchPageable.getItems() != null) {
if (branchPageable != null && branchPageable.getItems() != null) {
branchesModels.addAll(Stream.of(branchPageable.getItems())
.map(branchesModel -> {
branchesModel.setTag(false);
return branchesModel;
}).collect(Collectors.toList()));
}
if (tags != null) {
if (tags != null && tags.getItems() != null) {
branchesModels.addAll(Stream.of(tags.getItems())
.map(branchesModel -> {
branchesModel.setTag(true);
@ -112,12 +112,11 @@ class RepoCommitsPresenter extends BasePresenter<RepoCommitsMvp.View> implements
}));
manageDisposable(observable
.doOnSubscribe(disposable -> sendToView(RepoCommitsMvp.View::showBranchesProgress))
.doOnNext(branchesModels -> {
.subscribe(branchesModels -> {
branches.clear();
branches.addAll(branchesModels);
sendToView(view -> view.setBranchesData(branches, true));
})
.subscribe(branchesModels -> {/**/}, throwable -> sendToView(view -> view.setBranchesData(branches, true))));
}, throwable -> sendToView(view -> view.setBranchesData(branches, true))));
}
if (!InputHelper.isEmpty(login) && !InputHelper.isEmpty(repoId)) {
onCallApi(1, null);
@ -166,6 +165,6 @@ class RepoCommitsPresenter extends BasePresenter<RepoCommitsMvp.View> implements
if (response != null) {
sendToView(view -> view.onShowCommitCount(response.getLast()));
}
}));
}, Throwable::printStackTrace));
}
}

View File

@ -49,29 +49,27 @@ class CommitPagerPresenter extends BasePresenter<CommitPagerMvp.View> implements
return;
} else if (!InputHelper.isEmpty(sha) && !InputHelper.isEmpty(login) && !InputHelper.isEmpty(repoId)) {
makeRestCall(RestProvider.getRepoService()
.getCommit(login, repoId, sha)
.flatMap(commit -> {
if (commit.getGitCommit() != null && commit.getGitCommit().getMessage() != null) {
MarkdownModel markdownModel = new MarkdownModel();
markdownModel.setContext(login + "/" + repoId);
markdownModel.setText(commit.getGitCommit().getMessage());
return RestProvider.getRepoService().convertReadmeToHtml(markdownModel)
.onErrorReturn(throwable -> null);
}
return Observable.just(commit);
}, (commit, u) -> {
if (!InputHelper.isEmpty(u) && u instanceof String) {
commit.getGitCommit().setMessage(u.toString());
}
return commit;
}),
commit -> {
commitModel = commit;
commitModel.setRepoId(repoId);
commitModel.setLogin(login);
sendToView(CommitPagerMvp.View::onSetup);
manageObservable(commitModel.save(commitModel).toObservable());
});
.getCommit(login, repoId, sha)
.flatMap(commit -> {
if (commit.getGitCommit() != null && commit.getGitCommit().getMessage() != null) {
MarkdownModel markdownModel = new MarkdownModel();
markdownModel.setContext(login + "/" + repoId);
markdownModel.setText(commit.getGitCommit().getMessage());
return RestProvider.getRepoService().convertReadmeToHtml(markdownModel);
}
return Observable.just(commit);
}, (commit, u) -> {
if (!InputHelper.isEmpty(u) && u instanceof String) {
commit.getGitCommit().setMessage(u.toString());
}
return commit;
}), commit -> {
commitModel = commit;
commitModel.setRepoId(repoId);
commitModel.setLogin(login);
sendToView(CommitPagerMvp.View::onSetup);
manageObservable(commitModel.save(commitModel).toObservable());
});
return;
}
}

View File

@ -59,10 +59,9 @@ class RepoFilesPresenter extends BasePresenter<RepoFilesMvp.View> implements Rep
})
.toList()
.subscribe(models -> {
files.addAll(models);
sendToView(RepoFilesMvp.View::onNotifyAdapter);
}
));
files.addAll(models);
sendToView(RepoFilesMvp.View::onNotifyAdapter);
}));
}
@Override public void onCallApi(@Nullable RepoFile toAppend) {

View File

@ -103,45 +103,44 @@ class ViewerPresenter extends BasePresenter<ViewerMvp.View> implements ViewerMvp
? RestProvider.getRepoService(true).getFileAsHtmlStream(url)
: RestProvider.getRepoService(true).getFileAsStream(url);
makeRestCall(isRepo ? RestProvider.getRepoService(true).getReadmeHtml(url)
: streamObservable,
content -> {
downloadedStream = content;
ViewerFile fileModel = new ViewerFile();
fileModel.setContent(downloadedStream);
fileModel.setFullUrl(url);
fileModel.setRepo(isRepo);
if (isRepo) {
fileModel.setMarkdown(true);
isMarkdown = true;
isRepo = true;
sendToView(view -> view.onSetMdText(downloadedStream, url));
: streamObservable, content -> {
downloadedStream = content;
ViewerFile fileModel = new ViewerFile();
fileModel.setContent(downloadedStream);
fileModel.setFullUrl(url);
fileModel.setRepo(isRepo);
if (isRepo) {
fileModel.setMarkdown(true);
isMarkdown = true;
isRepo = true;
sendToView(view -> view.onSetMdText(downloadedStream, url));
} else {
isMarkdown = MarkDownProvider.isMarkdown(url);
if (isMarkdown) {
MarkdownModel model = new MarkdownModel();
model.setText(downloadedStream);
NameParser parser = new NameParser(url);
if (parser.getUsername() != null && parser.getName() != null) {
model.setContext(parser.getUsername() + "/" + parser.getName());
} else {
isMarkdown = MarkDownProvider.isMarkdown(url);
if (isMarkdown) {
MarkdownModel model = new MarkdownModel();
model.setText(downloadedStream);
NameParser parser = new NameParser(url);
if (parser.getUsername() != null && parser.getName() != null) {
model.setContext(parser.getUsername() + "/" + parser.getName());
} else {
model.setContext("");
}
Logger.e(model.getContext());
makeRestCall(RestProvider.getRepoService().convertReadmeToHtml(model), string -> {
isMarkdown = true;
downloadedStream = string;
fileModel.setMarkdown(true);
fileModel.setContent(downloadedStream);
manageObservable(fileModel.save(fileModel).toObservable());
sendToView(view -> view.onSetMdText(downloadedStream, url));
});
return;
}
fileModel.setMarkdown(false);
sendToView(view -> view.onSetCode(downloadedStream));
model.setContext("");
}
manageObservable(fileModel.save(fileModel).toObservable());
});
Logger.e(model.getContext());
makeRestCall(RestProvider.getRepoService().convertReadmeToHtml(model), string -> {
isMarkdown = true;
downloadedStream = string;
fileModel.setMarkdown(true);
fileModel.setContent(downloadedStream);
manageObservable(fileModel.save(fileModel).toObservable());
sendToView(view -> view.onSetMdText(downloadedStream, url));
});
return;
}
fileModel.setMarkdown(false);
sendToView(view -> view.onSetCode(downloadedStream));
}
manageObservable(fileModel.save(fileModel).toObservable());
});
}
@Override public boolean isRepo() {

View File

@ -71,14 +71,6 @@ class RepoReleasesPresenter extends BasePresenter<RepoReleasesMvp.View> implemen
}
private void onResponse(Pageable<Release> response) {
lastPage = response.getLast();
if (getCurrentPage() == 1) {
manageObservable(Release.save(response.getItems(), repoId, login));
}
sendToView(view -> view.onNotifyAdapter(response.getItems(), getCurrentPage()));
}
@Override public void onFragmentCreated(@NonNull Bundle bundle) {
repoId = bundle.getString(BundleConstant.ID);
login = bundle.getString(BundleConstant.EXTRA);
@ -110,4 +102,12 @@ class RepoReleasesPresenter extends BasePresenter<RepoReleasesMvp.View> implemen
}
@Override public void onItemLongClick(int position, View v, Release item) {}
private void onResponse(Pageable<Release> response) {
lastPage = response.getLast();
if (getCurrentPage() == 1) {
manageObservable(Release.save(response.getItems(), repoId, login));
}
sendToView(view -> view.onNotifyAdapter(response.getItems(), getCurrentPage()));
}
}

View File

@ -116,6 +116,7 @@ public class IssueTimelineFragment extends BaseFragment<IssueTimelineMvp.View, I
stateLayout.setOnReloadListener(this);
adapter.setListener(getPresenter());
recycler.setAdapter(adapter);
fastScroller.setOnLoadMore(getLoadMore());
fastScroller.setVisibility(View.VISIBLE);
fastScroller.attachRecyclerView(recycler);
recycler.addDivider(TimelineCommentsViewHolder.class);

View File

@ -100,6 +100,7 @@ public class PullRequestTimelineFragment extends BaseFragment<PullRequestTimelin
stateLayout.setOnReloadListener(this);
adapter.setListener(getPresenter());
recycler.setAdapter(adapter);
fastScroller.setOnLoadMore(getLoadMore());
fastScroller.setVisibility(View.VISIBLE);
fastScroller.attachRecyclerView(recycler);
recycler.addDivider(TimelineCommentsViewHolder.class);

View File

@ -15,6 +15,7 @@ import com.fastaccess.helper.BundleConstant
import com.fastaccess.helper.Bundler
import com.fastaccess.helper.Logger
import com.fastaccess.ui.base.BaseActivity
import com.fastaccess.ui.modules.main.MainActivity
import com.fastaccess.ui.modules.trending.fragment.TrendingFragment
@ -104,11 +105,18 @@ class TrendingActivity : BaseActivity<TrendingMvp.View, TrendingPresenter>(), Tr
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
if (item?.itemId == R.id.menu) {
drawerLayout.openDrawer(Gravity.END)
return true
when (item?.itemId) {
R.id.menu -> {
drawerLayout.openDrawer(Gravity.END)
return true
}
android.R.id.home -> {
startActivity(Intent(this, MainActivity::class.java))
finish()
return true
}
else -> return super.onOptionsItemSelected(item)
}
return super.onOptionsItemSelected(item)
}
override fun onAppend(title: String) {

View File

@ -25,13 +25,12 @@ class TrendingFragment : BaseFragment<TrendingFragmentMvp.View, TrendingFragment
@State var lang: String = ""
@State var since: String = ""
override fun providePresenter(): TrendingFragmentPresenter {
return TrendingFragmentPresenter()
}
override fun fragmentLayout(): Int {
return R.layout.micro_grid_refresh_list
return R.layout.small_grid_refresh_list
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {

View File

@ -8,22 +8,29 @@ import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.widget.TextView;
import com.annimon.stream.Stream;
import com.evernote.android.state.State;
import com.fastaccess.R;
import com.fastaccess.data.dao.FragmentPagerAdapterModel;
import com.fastaccess.data.dao.TabsCountStateModel;
import com.fastaccess.data.dao.model.Login;
import com.fastaccess.helper.BundleConstant;
import com.fastaccess.helper.Bundler;
import com.fastaccess.helper.InputHelper;
import com.fastaccess.helper.ViewHelper;
import com.fastaccess.ui.adapter.FragmentsPagerAdapter;
import com.fastaccess.ui.base.BaseActivity;
import com.fastaccess.ui.base.BaseFragment;
import com.fastaccess.ui.modules.main.MainActivity;
import com.fastaccess.ui.modules.profile.org.repos.OrgReposFragment;
import com.fastaccess.ui.modules.profile.repos.ProfileReposFragment;
import com.fastaccess.ui.widgets.SpannableBuilder;
import com.fastaccess.ui.widgets.ViewPagerView;
import java.util.HashSet;
import butterknife.BindView;
import butterknife.OnClick;
import shortbread.Shortcut;
@ -41,6 +48,7 @@ public class UserPagerActivity extends BaseActivity<UserPagerMvp.View, UserPager
@BindView(R.id.fab) FloatingActionButton fab;
@State String login;
@State boolean isOrg;
@State HashSet<TabsCountStateModel> counts = new HashSet<>();
public static void startActivity(@NonNull Context context, @NonNull String login) {
startActivity(context, login, false);
@ -130,6 +138,11 @@ public class UserPagerActivity extends BaseActivity<UserPagerMvp.View, UserPager
hideShowFab(position);
}
});
if (!isOrg) {
if (savedInstanceState != null && !counts.isEmpty()) {
Stream.of(counts).forEach(this::updateCount);
}
}
hideShowFab(pager.getCurrentItem());
}
@ -146,11 +159,11 @@ public class UserPagerActivity extends BaseActivity<UserPagerMvp.View, UserPager
}
@Override public void onNavigateToFollowers() {
pager.setCurrentItem(4);
pager.setCurrentItem(5);
}
@Override public void onNavigateToFollowing() {
pager.setCurrentItem(5);
pager.setCurrentItem(6);
}
@Override public void onInitOrg(boolean isMember) {
@ -163,6 +176,16 @@ public class UserPagerActivity extends BaseActivity<UserPagerMvp.View, UserPager
tabs.setupWithViewPager(pager);
}
@Override public void onSetBadge(int tabIndex, int count) {
TabsCountStateModel model = new TabsCountStateModel();
model.setTabIndex(tabIndex);
model.setCount(count);
counts.add(model);
if (tabs != null) {
updateCount(model);
}
}
@OnClick(R.id.fab) public void onRepoFilterClicked() {
if (isOrg) {
OrgReposFragment fragment = ((OrgReposFragment) pager.getAdapter().instantiateItem(pager, 1));
@ -188,4 +211,14 @@ public class UserPagerActivity extends BaseActivity<UserPagerMvp.View, UserPager
}
}
}
private void updateCount(@NonNull TabsCountStateModel model) {
TextView tv = ViewHelper.getTabTextView(tabs, model.getTabIndex());
tv.setText(SpannableBuilder.builder()
.append(getString(R.string.starred))
.append(" ")
.append("(")
.bold(String.valueOf(model.getCount()))
.append(")"));
}
}

View File

@ -4,6 +4,7 @@ import android.support.annotation.NonNull;
import com.fastaccess.ui.base.mvp.BaseMvp;
import com.fastaccess.ui.modules.profile.ProfilePagerMvp;
import com.fastaccess.ui.modules.repos.RepoPagerMvp;
/**
* Created by Kosh on 04 Dec 2016, 1:11 PM
@ -11,7 +12,7 @@ import com.fastaccess.ui.modules.profile.ProfilePagerMvp;
public interface UserPagerMvp {
interface View extends BaseMvp.FAView, ProfilePagerMvp.View {
interface View extends BaseMvp.FAView, ProfilePagerMvp.View, RepoPagerMvp.TabsBadgeListener {
void onInitOrg(boolean isMember);
}

View File

@ -23,6 +23,7 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.fastaccess.R;
import com.fastaccess.provider.rest.loadmore.OnLoadMore;
/**
* Created by thermatk on 17/04/2017.
@ -51,7 +52,7 @@ public class RecyclerFastScroller extends FrameLayout {
private int mBarColor;
private int mTouchTargetWidth;
private int mBarInset;
private OnLoadMore onLoadMore;
private boolean mHideOverride;
private RecyclerView.Adapter mAdapter;
private RecyclerView.AdapterDataObserver mAdapterObserver = new RecyclerView.AdapterDataObserver() {
@ -148,7 +149,10 @@ public class RecyclerFastScroller extends FrameLayout {
if (mRecyclerView != null) {
try {
mRecyclerView.scrollBy(0, dY);
} catch (Throwable t) {
if (onLoadMore != null) {
onLoadMore.onScrollStateChanged(mRecyclerView, RecyclerView.SCROLL_STATE_DRAGGING);
}
} catch (Exception t) {
t.printStackTrace();
}
}
@ -170,6 +174,34 @@ public class RecyclerFastScroller extends FrameLayout {
setTranslationX(mHiddenTranslationX);
}
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mRecyclerView == null) return;
int scrollOffset = mRecyclerView.computeVerticalScrollOffset();
int verticalScrollRange = mRecyclerView.computeVerticalScrollRange() + mRecyclerView.getPaddingBottom();
int barHeight = mBar.getHeight();
float ratio = (float) scrollOffset / (verticalScrollRange - barHeight);
int calculatedHandleHeight = (int) ((float) barHeight / verticalScrollRange * barHeight);
if (calculatedHandleHeight < mMinScrollHandleHeight) {
calculatedHandleHeight = mMinScrollHandleHeight;
}
if (calculatedHandleHeight >= barHeight) {
setTranslationX(mHiddenTranslationX);
mHideOverride = true;
return;
}
mHideOverride = false;
float y = ratio * (barHeight - calculatedHandleHeight);
mHandle.layout(mHandle.getLeft(), (int) y, mHandle.getRight(), (int) y + calculatedHandleHeight);
}
private void updateHandleColorsAndInset() {
StateListDrawable drawable = new StateListDrawable();
@ -202,8 +234,7 @@ public class RecyclerFastScroller extends FrameLayout {
public void attachRecyclerView(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
@Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
RecyclerFastScroller.this.show();
}
@ -217,9 +248,6 @@ public class RecyclerFastScroller extends FrameLayout {
}
}
/**
* Show the fast scroller and hide after delay
*/
public void show() {
requestLayout();
@ -262,48 +290,22 @@ public class RecyclerFastScroller extends FrameLayout {
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mRecyclerView == null) return;
int scrollOffset = mRecyclerView.computeVerticalScrollOffset();
int verticalScrollRange = mRecyclerView.computeVerticalScrollRange() + mRecyclerView.getPaddingBottom();
int barHeight = mBar.getHeight();
float ratio = (float) scrollOffset / (verticalScrollRange - barHeight);
int calculatedHandleHeight = (int) ((float) barHeight / verticalScrollRange * barHeight);
if (calculatedHandleHeight < mMinScrollHandleHeight) {
calculatedHandleHeight = mMinScrollHandleHeight;
}
if (calculatedHandleHeight >= barHeight) {
setTranslationX(mHiddenTranslationX);
mHideOverride = true;
return;
}
mHideOverride = false;
float y = ratio * (barHeight - calculatedHandleHeight);
mHandle.layout(mHandle.getLeft(), (int) y, mHandle.getRight(), (int) y + calculatedHandleHeight);
}
private static boolean isRTL(Context context) {
return context.getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
}
@ColorInt
public static int resolveColor(Context context, @AttrRes int color) {
@ColorInt public static int resolveColor(Context context, @AttrRes int color) {
TypedArray a = context.obtainStyledAttributes(new int[]{color});
int resId = a.getColor(0, 0);
a.recycle();
return resId;
}
private static boolean isRTL(Context context) {
return context.getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
}
public static int convertDpToPx(Context context, float dp) {
return (int) (dp * context.getResources().getDisplayMetrics().density + 0.5f);
}
public void setOnLoadMore(OnLoadMore onLoadMore) {
this.onLoadMore = onLoadMore;
}
}

View File

@ -28,13 +28,27 @@
android:transitionName="@string/title_transition"
tools:text="When one acquires music and afterlife, one is able to capture heaven."/>
<com.fastaccess.ui.widgets.FontTextView
android:id="@+id/description"
style="@style/TextAppearance.AppCompat.Small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="2"
android:textColor="?android:attr/textColorSecondary"
android:visibility="gone"
tools:text="Hello World"
tools:visibility="visible"/>
<com.fastaccess.ui.widgets.FontTextView
android:id="@+id/date"
style="@style/TextAppearance.AppCompat.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary"
android:drawablePadding="@dimen/spacing_normal"
android:textColor="?android:attr/textColorTertiary"
tools:drawableStart="@drawable/ic_notification"
tools:text="50 minutes ago"/>
</LinearLayout>
</com.fastaccess.ui.widgets.ForegroundRelativeLayout>