(), table.isDrawBorder());
+ drawable.setBounds(0, 0, tableWidth, 1);
+ builder.setSpan(new ImageSpan(drawable), builder.length() - 1, builder.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ builder.setSpan((AlignmentSpan) () -> Alignment.ALIGN_CENTER, start, builder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ builder.append("\n");
+ }
+
public void setTableWidth(int tableWidth) {
this.tableWidth = tableWidth;
}
- /**
- * Sets the text colour to use.
- *
- * Default is black.
- *
- * @param textColor
- */
public void setTextColor(int textColor) {
this.textColor = textColor;
}
- /**
- * Sets the font size to use.
- *
- * Default is 16f.
- *
- * @param textSize
- */
- public void setTextSize(float textSize) {
- this.textSize = textSize;
- }
-
- /**
- * Sets the TypeFace to use.
- *
- * Default is Typeface.DEFAULT
- *
- * @param typeFace
- */
- public void setTypeFace(Typeface typeFace) {
- this.typeFace = typeFace;
- }
-
- @Override
- public boolean rendersContent() {
- return true;
- }
-
private void readNode(Object node, Table table) {
if (node instanceof TagNode) {
TagNode tagNode = (TagNode) node;
@@ -144,44 +127,19 @@ public class TableHandler extends TagNodeHandler {
int rowHeight = 0;
- if (columnWidth > 0) {
- for (Spanned cell : row) {
- StaticLayout layout = new StaticLayout(cell, textPaint, columnWidth
- - 2 * PADDING, Alignment.ALIGN_NORMAL, 1f, 0f, true);
- if (layout.getHeight() > rowHeight) {
- rowHeight = layout.getHeight();
- }
+ for (Spanned cell : row) {
+
+ StaticLayout layout = new StaticLayout(cell, textPaint, columnWidth
+ - 2 * PADDING, Alignment.ALIGN_NORMAL, 1.5f, 0.5f, true);
+
+ if (layout.getHeight() > rowHeight) {
+ rowHeight = layout.getHeight();
}
}
return rowHeight;
}
- @Override public void handleTagNode(TagNode node, SpannableStringBuilder builder, int start, int end) {
- Table table = getTable(node);
- for (int i = 0; i < table.getRows().size(); i++) {
- List row = table.getRows().get(i);
- builder.append("\uFFFC");
- TableRowDrawable drawable = new TableRowDrawable(row, table.isDrawBorder());
- drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
- drawable.getIntrinsicHeight());
- builder.setSpan(new ImageSpan(drawable), start + i, builder.length(), 33);
- }
- builder.append("\uFFFC");
- Drawable drawable = new TableRowDrawable(new ArrayList<>(), table.isDrawBorder());
- drawable.setBounds(0, 0, tableWidth, 1);
- builder.setSpan(new ImageSpan(drawable), builder.length() - 1, builder.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- builder.setSpan((AlignmentSpan) () -> Alignment.ALIGN_CENTER, start, builder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- builder.setSpan(new CenterSpan(), start, builder.length(), 33);
- builder.append("\n");
- }
-
- /**
- * Drawable of the table, which does the actual rendering.
- *
- * @author Alex Kuiper.
- */
private class TableRowDrawable extends Drawable {
private List tableRow;
@@ -222,7 +180,7 @@ public class TableHandler extends TagNodeHandler {
StaticLayout layout = new StaticLayout(tableRow.get(i),
getTextPaint(), (columnWidth - 2 * PADDING),
- Alignment.ALIGN_NORMAL, 1f, 0f, true);
+ Alignment.ALIGN_NORMAL, 1.5f, 0.5f, true);
canvas.translate(offset + PADDING, 0);
layout.draw(canvas);
diff --git a/app/src/main/java/com/fastaccess/provider/timeline/handler/drawable/DrawableGetter.java b/app/src/main/java/com/fastaccess/provider/timeline/handler/drawable/DrawableGetter.java
index 759fe787..ed2c9188 100644
--- a/app/src/main/java/com/fastaccess/provider/timeline/handler/drawable/DrawableGetter.java
+++ b/app/src/main/java/com/fastaccess/provider/timeline/handler/drawable/DrawableGetter.java
@@ -21,11 +21,13 @@ import java.util.Set;
public class DrawableGetter implements Html.ImageGetter, Drawable.Callback {
private WeakReference container;
private final Set cachedTargets;
+ private final int width;
- public DrawableGetter(TextView tv) {
+ public DrawableGetter(TextView tv, int width) {
tv.setTag(R.id.drawable_callback, this);
this.container = new WeakReference<>(tv);
this.cachedTargets = new HashSet<>();
+ this.width = width;
}
@Override public Drawable getDrawable(@NonNull String url) {
@@ -35,7 +37,7 @@ public class DrawableGetter implements Html.ImageGetter, Drawable.Callback {
final GenericRequestBuilder load = Glide.with(context)
.load(url)
.dontAnimate();
- final GlideDrawableTarget target = new GlideDrawableTarget(urlDrawable, container);
+ final GlideDrawableTarget target = new GlideDrawableTarget(urlDrawable, container, width);
load.into(target);
cachedTargets.add(target);
}
diff --git a/app/src/main/java/com/fastaccess/provider/timeline/handler/drawable/GlideDrawableTarget.java b/app/src/main/java/com/fastaccess/provider/timeline/handler/drawable/GlideDrawableTarget.java
index 35ff6c95..12c6cd67 100644
--- a/app/src/main/java/com/fastaccess/provider/timeline/handler/drawable/GlideDrawableTarget.java
+++ b/app/src/main/java/com/fastaccess/provider/timeline/handler/drawable/GlideDrawableTarget.java
@@ -15,17 +15,28 @@ import java.lang.ref.WeakReference;
class GlideDrawableTarget extends SimpleTarget {
private final UrlDrawable urlDrawable;
private final WeakReference container;
+ private final int width;
- GlideDrawableTarget(UrlDrawable urlDrawable, WeakReference container) {
+ GlideDrawableTarget(UrlDrawable urlDrawable, WeakReference container, int width) {
this.urlDrawable = urlDrawable;
this.container = container;
+ this.width = width;
}
@Override public void onResourceReady(GlideDrawable resource, GlideAnimation super GlideDrawable> glideAnimation) {
if (container != null && container.get() != null) {
TextView textView = container.get();
- float width = (float) (resource.getIntrinsicWidth() / 1.3);
- float height = (float) (resource.getIntrinsicHeight() / 1.3);
+ float width;
+ float height;
+ if (resource.getIntrinsicWidth() >= this.width) {
+ float downScale = (float) resource.getIntrinsicWidth() / this.width;
+ width = (float) (resource.getIntrinsicWidth() / downScale / 1.3);
+ height = (float) (resource.getIntrinsicHeight() / downScale / 1.3);
+ } else {
+ float multiplier = (float) this.width / resource.getIntrinsicWidth();
+ width = (float) resource.getIntrinsicWidth() * multiplier;
+ height = (float) resource.getIntrinsicHeight() * multiplier;
+ }
Rect rect = new Rect(0, 0, Math.round(width), Math.round(height));
resource.setBounds(rect);
urlDrawable.setBounds(rect);
diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/IssueDetailsViewHolder.java b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/IssueDetailsViewHolder.java
index 628e854e..440776b4 100644
--- a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/IssueDetailsViewHolder.java
+++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/IssueDetailsViewHolder.java
@@ -1,5 +1,6 @@
package com.fastaccess.ui.adapter.viewholder;
+import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.transition.ChangeBounds;
@@ -10,6 +11,7 @@ import android.view.ViewGroup;
import android.widget.TextView;
import com.fastaccess.R;
+import com.fastaccess.data.dao.LabelModel;
import com.fastaccess.data.dao.ReactionsModel;
import com.fastaccess.data.dao.TimelineModel;
import com.fastaccess.data.dao.model.Issue;
@@ -17,6 +19,7 @@ import com.fastaccess.data.dao.model.PullRequest;
import com.fastaccess.data.dao.model.User;
import com.fastaccess.helper.InputHelper;
import com.fastaccess.helper.ParseDateFormat;
+import com.fastaccess.helper.ViewHelper;
import com.fastaccess.provider.scheme.LinkParserHelper;
import com.fastaccess.provider.timeline.CommentsHelper;
import com.fastaccess.provider.timeline.HtmlHelper;
@@ -25,11 +28,13 @@ import com.fastaccess.ui.adapter.callback.OnToggleView;
import com.fastaccess.ui.adapter.callback.ReactionsCallback;
import com.fastaccess.ui.widgets.AvatarLayout;
import com.fastaccess.ui.widgets.FontTextView;
+import com.fastaccess.ui.widgets.LabelSpan;
import com.fastaccess.ui.widgets.SpannableBuilder;
import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter;
import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder;
import java.util.Date;
+import java.util.List;
import butterknife.BindView;
@@ -56,6 +61,8 @@ public class IssueDetailsViewHolder extends BaseViewHolder {
@BindView(R.id.emojiesList) View emojiesList;
@BindView(R.id.reactionsText) TextView reactionsText;
@BindView(R.id.owner) TextView owner;
+ @BindView(R.id.labels) TextView labels;
+ @BindView(R.id.labelsHolder) View labelsHolder;
private OnToggleView onToggleView;
private ReactionsCallback reactionsCallback;
private ViewGroup viewGroup;
@@ -172,11 +179,13 @@ public class IssueDetailsViewHolder extends BaseViewHolder {
private void bind(@NonNull Issue issueModel) {
setup(issueModel.getUser(), issueModel.getBodyHtml(), issueModel.getReactions());
setupDate(issueModel.getCreatedAt(), issueModel.getUpdatedAt());
+ setupLabels(issueModel.getLabels());
}
private void bind(@NonNull PullRequest pullRequest) {
setup(pullRequest.getUser(), pullRequest.getBodyHtml(), pullRequest.getReactions());
setupDate(pullRequest.getCreatedAt(), pullRequest.getUpdatedAt());
+ setupLabels(pullRequest.getLabels());
}
private void setup(User user, String description, ReactionsModel reactionsModel) {
@@ -193,8 +202,8 @@ public class IssueDetailsViewHolder extends BaseViewHolder {
if (reactionsModel != null) {
appendEmojies(reactionsModel);
}
- if (description != null && !description.trim().isEmpty()) {
- HtmlHelper.htmlIntoTextView(comment, description, viewGroup.getWidth());
+ if (!InputHelper.isEmpty(description)) {
+ HtmlHelper.htmlIntoTextView(comment, description, viewGroup.getWidth() - ViewHelper.dpToPx(itemView.getContext(), 24));
} else {
comment.setText(R.string.no_description_provided);
}
@@ -204,6 +213,21 @@ public class IssueDetailsViewHolder extends BaseViewHolder {
date.setText(ParseDateFormat.getTimeAgo(createdDate));
}
+ private void setupLabels(@Nullable List labelList) {
+ if (labelList != null && !labelList.isEmpty()) {
+ SpannableBuilder builder = SpannableBuilder.builder();
+ for (LabelModel labelModel : labelList) {
+ int color = Color.parseColor("#" + labelModel.getColor());
+ builder.append(" ").append(" " + labelModel.getName() + " ", new LabelSpan(color));
+ }
+ labels.setText(builder);
+ labelsHolder.setVisibility(View.VISIBLE);
+ } else {
+ labels.setText("");
+ labelsHolder.setVisibility(View.GONE);
+ }
+ }
+
private void appendEmojies(@NonNull ReactionsModel reaction) {
SpannableBuilder spannableBuilder = SpannableBuilder.builder();
reactionsText.setText("");
diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/PullRequestEventViewHolder.kt b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/PullRequestEventViewHolder.kt
index 6b29ed9e..6e2bfb6f 100644
--- a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/PullRequestEventViewHolder.kt
+++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/PullRequestEventViewHolder.kt
@@ -402,8 +402,8 @@ class PullRequestEventViewHolder private constructor(view: View, adapter: BaseRe
if (value == null) {
return ""
}
- if (value.length <= 7) return value
- else return value.substring(0, 7)
+ return if (value.length <= 7) value
+ else value.substring(0, 7)
}
companion object {
diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/PullRequestFilesViewHolder.java b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/PullRequestFilesViewHolder.java
index 2b0077f8..105c95bf 100644
--- a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/PullRequestFilesViewHolder.java
+++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/PullRequestFilesViewHolder.java
@@ -108,8 +108,13 @@ public class PullRequestFilesViewHolder extends BaseViewHolder(view) {
+class UnknownTypeViewHolder(view: View) : BaseViewHolder(view) {
override fun bind(t: Any) {} //DO NOTHING
}
\ No newline at end of file
diff --git a/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java b/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java
index 37c6072a..a1ee3922 100644
--- a/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java
+++ b/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java
@@ -44,6 +44,7 @@ import com.fastaccess.ui.modules.gists.gist.GistActivity;
import com.fastaccess.ui.modules.login.chooser.LoginChooserActivity;
import com.fastaccess.ui.modules.main.MainActivity;
import com.fastaccess.ui.modules.main.orgs.OrgListDialogFragment;
+import com.fastaccess.ui.modules.main.playstore.PlayStoreWarningActivity;
import com.fastaccess.ui.modules.repos.code.commit.details.CommitPagerActivity;
import com.fastaccess.ui.modules.repos.issues.issue.details.IssuePagerActivity;
import com.fastaccess.ui.modules.repos.pull_requests.pull_request.details.PullRequestPagerActivity;
@@ -107,29 +108,11 @@ public abstract class BaseActivity AnimHelper.revealDialog(dialog,
getResources().getInteger(android.R.integer.config_longAnimTime)));
}
diff --git a/app/src/main/java/com/fastaccess/ui/base/MainNavDrawer.kt b/app/src/main/java/com/fastaccess/ui/base/MainNavDrawer.kt
index 03cb104e..238e55a9 100644
--- a/app/src/main/java/com/fastaccess/ui/base/MainNavDrawer.kt
+++ b/app/src/main/java/com/fastaccess/ui/base/MainNavDrawer.kt
@@ -20,6 +20,7 @@ import com.fastaccess.ui.modules.about.FastHubAboutActivity
import com.fastaccess.ui.modules.gists.GistsListActivity
import com.fastaccess.ui.modules.login.chooser.LoginChooserActivity
import com.fastaccess.ui.modules.main.MainActivity
+import com.fastaccess.ui.modules.main.playstore.PlayStoreWarningActivity
import com.fastaccess.ui.modules.main.premium.PremiumActivity
import com.fastaccess.ui.modules.notification.NotificationActivity
import com.fastaccess.ui.modules.pinned.PinnedReposActivity
@@ -38,13 +39,13 @@ class MainNavDrawer(val view: BaseActivity<*, *>, private val extraNav: Navigati
: BaseViewHolder.OnItemClickListener {
private var menusHolder: ViewGroup? = null
- private val togglePinned: View? = view.findViewById(R.id.togglePinned)
- private val pinnedList: DynamicRecyclerView? = view.findViewById(R.id.pinnedList)
+ private val togglePinned: View? = view.findViewById(R.id.togglePinned)
+ private val pinnedList: DynamicRecyclerView? = view.findViewById(R.id.pinnedList)
private val pinnedListAdapter = PinnedReposAdapter(true)
private val userModel: Login? = Login.getUser()
init {
- menusHolder = view.findViewById(R.id.menusHolder)
+ menusHolder = view.findViewById(R.id.menusHolder)
pinnedListAdapter.listener = object : BaseViewHolder.OnItemClickListener {
override fun onItemClick(position: Int, v: View?, item: PinnedRepos?) {
if (v != null && item != null) {
@@ -181,6 +182,7 @@ class MainNavDrawer(val view: BaseActivity<*, *>, private val extraNav: Navigati
item.itemId == R.id.notifications -> view.startActivity(Intent(view, NotificationActivity::class.java))
item.itemId == R.id.trending -> view.startActivity(Intent(view, TrendingActivity::class.java))
item.itemId == R.id.reportBug -> view.startActivity(CreateIssueActivity.startForResult(view))
+ item.itemId == R.id.faq -> view.startActivity(Intent(view, PlayStoreWarningActivity::class.java))
}
}
}, 250)
diff --git a/app/src/main/java/com/fastaccess/ui/modules/changelog/ChangelogBottomSheetDialog.java b/app/src/main/java/com/fastaccess/ui/modules/changelog/ChangelogBottomSheetDialog.java
index c88089a7..c3f8acae 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/changelog/ChangelogBottomSheetDialog.java
+++ b/app/src/main/java/com/fastaccess/ui/modules/changelog/ChangelogBottomSheetDialog.java
@@ -1,5 +1,6 @@
package com.fastaccess.ui.modules.changelog;
+import android.app.Dialog;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -61,6 +62,13 @@ public class ChangelogBottomSheetDialog extends BaseMvpBottomSheetDialogFragment
return new ChangelogPresenter();
}
+ @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.setCancelable(false);
+ dialog.setCanceledOnTouchOutside(false);
+ return dialog;
+ }
+
private void showChangelog(String html) {
if (prettifyWebView == null) return;
webProgress.setVisibility(View.GONE);
diff --git a/app/src/main/java/com/fastaccess/ui/modules/editor/emoji/EmojiBottomSheet.kt b/app/src/main/java/com/fastaccess/ui/modules/editor/emoji/EmojiBottomSheet.kt
index 3e46961f..4e06b396 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/editor/emoji/EmojiBottomSheet.kt
+++ b/app/src/main/java/com/fastaccess/ui/modules/editor/emoji/EmojiBottomSheet.kt
@@ -34,9 +34,9 @@ class EmojiBottomSheet : BaseMvpBottomSheetDialogFragment emojiCallback = parentFragment as EmojiMvp.EmojiCallback
- context is EmojiMvp.EmojiCallback -> emojiCallback = context
+ emojiCallback = when {
+ parentFragment is EmojiMvp.EmojiCallback -> parentFragment as EmojiMvp.EmojiCallback
+ context is EmojiMvp.EmojiCallback -> context
else -> throw IllegalArgumentException("${context.javaClass.simpleName} must implement EmojiMvp.EmojiCallback")
}
}
diff --git a/app/src/main/java/com/fastaccess/ui/modules/editor/popup/EditorLinkImageDialogFragment.java b/app/src/main/java/com/fastaccess/ui/modules/editor/popup/EditorLinkImageDialogFragment.java
index 0d0549ad..a79a2b80 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/editor/popup/EditorLinkImageDialogFragment.java
+++ b/app/src/main/java/com/fastaccess/ui/modules/editor/popup/EditorLinkImageDialogFragment.java
@@ -38,11 +38,12 @@ public class EditorLinkImageDialogFragment extends BaseDialogFragment
.put(BundleConstant.EXTRA, true).end())
.show(getSupportFragmentManager(), MessageDialogView.TAG);
return true;
+ } else if (item.getItemId() == android.R.id.home) {
+ GistsListActivity.startActivity(this);
+ finish();
+ return true;
}
return super.onOptionsItemSelected(item);
}
diff --git a/app/src/main/java/com/fastaccess/ui/modules/main/MainActivity.java b/app/src/main/java/com/fastaccess/ui/modules/main/MainActivity.java
index e3b7f20e..e50ec8a9 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/main/MainActivity.java
+++ b/app/src/main/java/com/fastaccess/ui/modules/main/MainActivity.java
@@ -34,7 +34,6 @@ import butterknife.OnClick;
import it.sephiroth.android.library.bottomnavigation.BottomNavigation;
import shortbread.Shortcut;
-@Shortcut(id = "feeds", icon = R.drawable.ic_app_shortcut_github, shortLabelRes = R.string.feeds, rank = 1)
public class MainActivity extends BaseActivity implements MainMvp.View {
@State @MainMvp.NavigationType int navType = MainMvp.FEEDS;
diff --git a/app/src/main/java/com/fastaccess/ui/modules/main/donation/CheckPurchaseActivity.kt b/app/src/main/java/com/fastaccess/ui/modules/main/donation/CheckPurchaseActivity.kt
index 2948d88c..23db135d 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/main/donation/CheckPurchaseActivity.kt
+++ b/app/src/main/java/com/fastaccess/ui/modules/main/donation/CheckPurchaseActivity.kt
@@ -38,7 +38,7 @@ class CheckPurchaseActivity : Activity() {
}
}
- override fun onBackPressed() {}
+ override fun onBackPressed() = Unit
private fun startMainActivity() {
startActivity(Intent(this, MainActivity::class.java))
diff --git a/app/src/main/java/com/fastaccess/ui/modules/main/playstore/PlayStoreWarningActivity.kt b/app/src/main/java/com/fastaccess/ui/modules/main/playstore/PlayStoreWarningActivity.kt
new file mode 100644
index 00000000..0de394a1
--- /dev/null
+++ b/app/src/main/java/com/fastaccess/ui/modules/main/playstore/PlayStoreWarningActivity.kt
@@ -0,0 +1,44 @@
+package com.fastaccess.ui.modules.main.playstore
+
+import android.os.Build
+import android.os.Bundle
+import android.text.Html
+import android.widget.TextView
+import butterknife.OnClick
+import com.fastaccess.R
+import com.fastaccess.helper.PrefGetter
+import com.fastaccess.ui.base.BaseActivity
+import com.fastaccess.ui.base.mvp.BaseMvp
+import com.fastaccess.ui.base.mvp.presenter.BasePresenter
+
+/**
+ * Created by Hashemsergani on 21.09.17.
+ */
+class PlayStoreWarningActivity : BaseActivity>() {
+
+ @OnClick(R.id.done) fun onDone() {
+ PrefGetter.setPlayStoreWarningShowed()
+ finish()
+ }
+
+ override fun layout(): Int = R.layout.playstore_review_layout_warning
+
+ override fun isTransparent(): Boolean = true
+
+ override fun canBack(): Boolean = false
+
+ override fun isSecured(): Boolean = true
+
+ override fun providePresenter(): BasePresenter = BasePresenter()
+
+ override fun onBackPressed() {}
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ findViewById(R.id.description).text = Html.fromHtml(getString(R.string.fasthub_faq_description), Html.FROM_HTML_MODE_LEGACY)
+ } else {
+ findViewById(R.id.description).text = Html.fromHtml(getString(R.string.fasthub_faq_description))
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/fastaccess/ui/modules/main/premium/GmsTaskListeners.java b/app/src/main/java/com/fastaccess/ui/modules/main/premium/GmsTaskListeners.java
new file mode 100644
index 00000000..e15fd70c
--- /dev/null
+++ b/app/src/main/java/com/fastaccess/ui/modules/main/premium/GmsTaskListeners.java
@@ -0,0 +1,48 @@
+package com.github.b3er.rxfirebase.common;
+
+import android.support.annotation.NonNull;
+import com.google.android.gms.tasks.OnCompleteListener;
+import com.google.android.gms.tasks.Task;
+import io.reactivex.CompletableEmitter;
+import io.reactivex.SingleEmitter;
+
+public final class GmsTaskListeners {
+
+ private GmsTaskListeners() {
+ throw new AssertionError("No instances");
+ }
+
+ public static OnCompleteListener listener(@NonNull final SingleEmitter emitter) {
+ return new OnCompleteListener() {
+ @Override public void onComplete(@NonNull Task task) {
+ if (!task.isSuccessful()) {
+ if (!emitter.isDisposed()) {
+ emitter.onError(task.getException());
+ }
+ return;
+ }
+
+ if (!emitter.isDisposed()) {
+ emitter.onSuccess(task.getResult());
+ }
+ }
+ };
+ }
+
+ public static OnCompleteListener listener(@NonNull final CompletableEmitter emitter) {
+ return new OnCompleteListener() {
+ @Override public void onComplete(@NonNull Task task) {
+ if (!task.isSuccessful()) {
+ if (!emitter.isDisposed()) {
+ emitter.onError(task.getException());
+ }
+ return;
+ }
+
+ if (!emitter.isDisposed()) {
+ emitter.onComplete();
+ }
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumActivity.kt b/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumActivity.kt
index 86cee4f2..8150393e 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumActivity.kt
+++ b/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumActivity.kt
@@ -16,7 +16,6 @@ import com.fastaccess.BuildConfig
import com.fastaccess.R
import com.fastaccess.helper.AppHelper
import com.fastaccess.helper.InputHelper
-import com.fastaccess.helper.PrefGetter
import com.fastaccess.helper.ViewHelper
import com.fastaccess.provider.fabric.FabricProvider
import com.fastaccess.ui.base.BaseActivity
@@ -76,7 +75,7 @@ class PremiumActivity : BaseActivity(), Premi
return true
}
- @OnClick(R.id.close) fun onClose(): Unit = finish()
+ @OnClick(R.id.close) fun onClose() = finish()
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
@@ -93,12 +92,10 @@ class PremiumActivity : BaseActivity(), Premi
override fun onSuccessfullyActivated() {
hideProgress()
successActivationHolder.visibility = View.VISIBLE
+ FabricProvider.logPurchase(InputHelper.toString(editText))
successActivationView.addAnimatorListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(p0: Animator?) {}
override fun onAnimationEnd(p0: Animator?) {
- FabricProvider.logPurchase(InputHelper.toString(editText))
- PrefGetter.setProItems()
- PrefGetter.setEnterpriseItem()
showMessage(R.string.success, R.string.success)
successResult()
}
diff --git a/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumPresenter.kt b/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumPresenter.kt
index ab1cb8f8..418ca69a 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumPresenter.kt
+++ b/app/src/main/java/com/fastaccess/ui/modules/main/premium/PremiumPresenter.kt
@@ -1,9 +1,11 @@
package com.fastaccess.ui.modules.main.premium
-import com.fastaccess.helper.Logger
+import com.fastaccess.data.dao.ProUsersModel
+import com.fastaccess.helper.PrefGetter
import com.fastaccess.helper.RxHelper
import com.fastaccess.ui.base.mvp.presenter.BasePresenter
import com.github.b3er.rxfirebase.database.data
+import com.github.b3er.rxfirebase.database.rxUpdateChildren
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.GenericTypeIndicator
import io.reactivex.Observable
@@ -15,25 +17,41 @@ import io.reactivex.Observable
class PremiumPresenter : BasePresenter(), PremiumMvp.Presenter {
override fun onCheckPromoCode(promo: String) {
val ref = FirebaseDatabase.getInstance().reference
- manageDisposable(RxHelper.getObservable(ref.child("promoCodes")
+ manageDisposable(RxHelper.getObservable(ref.child("fasthub_pro").child(promo)
.data()
.toObservable())
.doOnSubscribe { sendToView { it.showProgress(0) } }
.flatMap {
- var exists: Boolean? = false
- if (it.exists()) {
- val gti = object : GenericTypeIndicator>() {}
- val map = it.getValue(gti)
- exists = map?.contains(promo)
+ var user = ProUsersModel()
+ if (it.exists() && it.hasChildren()) {
+ val gti = object : GenericTypeIndicator() {}
+ user = it.getValue(gti) ?: ProUsersModel()
+ if (user.isAllowed) {
+ if (user.type == 1) {
+ PrefGetter.setProItems()
+ user.isAllowed = false
+ user.count = user.count + 1
+ return@flatMap RxHelper.getObservable(ref.child("fasthub_pro").rxUpdateChildren(hashMapOf(Pair(promo, user)))
+ .toObservable())
+ .map { true }
+ } else {
+ PrefGetter.setProItems()
+ PrefGetter.setEnterpriseItem()
+ user.count = user.count + 1
+ return@flatMap RxHelper.getObservable(ref.child("fasthub_pro").rxUpdateChildren(hashMapOf(Pair(promo, user)))
+ .toObservable())
+ .map { true }
+ }
+ }
}
- Logger.e(it.children, it.childrenCount, exists)
- return@flatMap Observable.just(exists)
+ return@flatMap Observable.just(user.isAllowed)
}
.doOnComplete { sendToView { it.hideProgress() } }
.subscribe({
- when (it) {
- true -> sendToView { it.onSuccessfullyActivated() }
- else -> sendToView { it.onNoMatch() }
+ if (it) {
+ sendToView { it.onSuccessfullyActivated() }
+ } else {
+ sendToView { it.onNoMatch() }
}
}, ::println))
}
diff --git a/app/src/main/java/com/fastaccess/ui/modules/profile/org/OrgProfileOverviewFragment.java b/app/src/main/java/com/fastaccess/ui/modules/profile/org/OrgProfileOverviewFragment.java
index ed2d4ed3..75b9ce33 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/profile/org/OrgProfileOverviewFragment.java
+++ b/app/src/main/java/com/fastaccess/ui/modules/profile/org/OrgProfileOverviewFragment.java
@@ -21,6 +21,7 @@ import com.fastaccess.helper.InputHelper;
import com.fastaccess.helper.ParseDateFormat;
import com.fastaccess.provider.emoji.EmojiParser;
import com.fastaccess.ui.base.BaseFragment;
+import com.fastaccess.ui.modules.profile.org.project.OrgProjectActivity;
import com.fastaccess.ui.widgets.AvatarLayout;
import com.fastaccess.ui.widgets.FontTextView;
@@ -44,6 +45,7 @@ public class OrgProfileOverviewFragment extends BaseFragment>() {
+
+ @State var org: String? = null
+
+ @BindView(R.id.appbar) lateinit var appBar: AppBarLayout
+
+ override fun layout(): Int = R.layout.activity_fragment_layout
+
+ override fun isTransparent(): Boolean = true
+
+ override fun canBack(): Boolean = true
+
+ override fun isSecured(): Boolean = false
+
+ override fun providePresenter(): BasePresenter = BasePresenter()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ appBar.elevation = 0f
+ appBar.stateListAnimator = null
+ if (savedInstanceState == null) {
+ org = intent.extras.getString(BundleConstant.ITEM)
+ val org = org
+ if (org != null) {
+ supportFragmentManager.beginTransaction()
+ .replace(R.id.container, RepoProjectsFragmentPager.newInstance(org),
+ RepoProjectsFragmentPager.TAG)
+ .commit()
+ }
+ }
+ toolbar?.apply { subtitle = org }
+ }
+
+ companion object {
+ fun startActivity(context: Context, org: String, isEnterprise: Boolean) {
+ val intent = Intent(context, OrgProjectActivity::class.java)
+ intent.putExtras(Bundler.start().put(BundleConstant.ITEM, org)
+ .put(BundleConstant.IS_ENTERPRISE, isEnterprise)
+ .end())
+ context.startActivity(intent)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/RepoPagerActivity.java b/app/src/main/java/com/fastaccess/ui/modules/repos/RepoPagerActivity.java
index 1125db19..a233f436 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/repos/RepoPagerActivity.java
+++ b/app/src/main/java/com/fastaccess/ui/modules/repos/RepoPagerActivity.java
@@ -341,7 +341,9 @@ public class RepoPagerActivity extends BaseActivity implements Rep
break;
case RepoPagerMvp.PROJECTS:
if (projectsFragmentPager == null) {
- onAddAndHide(fragmentManager, RepoProjectsFragmentPager.Companion.newInstance(repoId(), login()), currentVisible);
+ onAddAndHide(fragmentManager, RepoProjectsFragmentPager.Companion.newInstance(login(), repoId()), currentVisible);
} else {
onShowHideFragment(fragmentManager, projectsFragmentPager, currentVisible);
}
diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/details/comments/CommitCommentsFragment.java b/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/details/comments/CommitCommentsFragment.java
index 8d0a2bbe..c699ac4d 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/details/comments/CommitCommentsFragment.java
+++ b/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/details/comments/CommitCommentsFragment.java
@@ -188,9 +188,20 @@ public class CommitCommentsFragment extends BaseFragment(), Br
override fun onAttach(context: Context) {
super.onAttach(context)
- if (parentFragment is BranchesPagerListener) {
- branchCallback = parentFragment as BranchesPagerListener
- } else branchCallback = context as BranchesPagerListener
+ branchCallback = if (parentFragment is BranchesPagerListener) {
+ parentFragment as BranchesPagerListener
+ } else context as BranchesPagerListener
}
override fun onDetach() {
diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/extras/branches/pager/BranchesPagerFragment.kt b/app/src/main/java/com/fastaccess/ui/modules/repos/extras/branches/pager/BranchesPagerFragment.kt
index ecf94306..5eb08b4c 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/repos/extras/branches/pager/BranchesPagerFragment.kt
+++ b/app/src/main/java/com/fastaccess/ui/modules/repos/extras/branches/pager/BranchesPagerFragment.kt
@@ -31,9 +31,9 @@ class BranchesPagerFragment : BaseDialogFragment labelModels = new ArrayList<>();
+ @State MilestoneModel milestoneModel;
+ @State ArrayList users = new ArrayList<>();
private AlertDialog alertDialog;
private CharSequence savedText;
@@ -177,6 +201,12 @@ public class CreateIssueActivity extends BaseActivity(issue.getLabels()));
+ }
+ if (issue.getAssignees() != null) {
+ onSelectedAssignees(new ArrayList<>(issue.getAssignees()), false);
+ }
+ if (issue.getMilestone() != null) {
+ milestoneModel = issue.getMilestone();
+ onMilestoneSelected(milestoneModel);
+ }
if (!InputHelper.isEmpty(issue.getTitle())) {
if (title.getEditText() != null) title.getEditText().setText(issue.getTitle());
}
@@ -222,6 +263,17 @@ public class CreateIssueActivity extends BaseActivity(pullRequest.getLabels()));
+ }
+ if (pullRequest.getAssignees() != null) {
+ users.addAll(pullRequest.getAssignees());
+ onSelectedAssignees(new ArrayList<>(pullRequest.getAssignees()), false);
+ }
+ if (pullRequest.getMilestone() != null) {
+ milestoneModel = pullRequest.getMilestone();
+ onMilestoneSelected(milestoneModel);
+ }
if (!InputHelper.isEmpty(pullRequest.getTitle())) {
if (title.getEditText() != null) title.getEditText().setText(pullRequest.getTitle());
}
@@ -230,6 +282,7 @@ public class CreateIssueActivity extends BaseActivity labelModels) {
+ this.labelModels.clear();
+ this.labelModels.addAll(labelModels);
+ SpannableBuilder builder = SpannableBuilder.builder();
+ for (int i = 0; i < labelModels.size(); i++) {
+ LabelModel labelModel = labelModels.get(i);
+ int color = Color.parseColor("#" + labelModel.getColor());
+ if (i > 0) {
+ builder.append(" ").append(" " + labelModel.getName() + " ", new LabelSpan(color));
+ } else {
+ builder.append(labelModel.getName() + " ", new LabelSpan(color));
+ }
+ }
+ this.labels.setText(builder);
+ }
+
+ @Override public void onMilestoneSelected(@NonNull MilestoneModel milestoneModel) {
+ Logger.e(milestoneModel.getTitle(), milestoneModel.getDescription(), milestoneModel.getNumber());
+ this.milestoneModel = milestoneModel;
+ milestoneTitle.setText(milestoneModel.getTitle());
+ if (!InputHelper.isEmpty(milestoneModel.getDescription())) {
+ milestoneDescription.setText(milestoneModel.getDescription());
+ milestoneDescription.setVisibility(View.VISIBLE);
+ } else {
+ milestoneDescription.setText(null);
+ milestoneDescription.setVisibility(View.GONE);
+ }
+ }
+
+ @Override public void onSelectedAssignees(@NonNull ArrayList users, boolean isAssignees) {
+ this.users.clear();
+ this.users.addAll(users);
+ SpannableBuilder builder = SpannableBuilder.builder();
+ for (int i = 0; i < users.size(); i++) {
+ User user = users.get(i);
+ builder.append(user.getLogin());
+ if (i != users.size() - 1) {
+ builder.append(", ");
+ }
+ }
+ assignee.setText(builder);
}
}
diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/create/CreateIssueMvp.java b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/create/CreateIssueMvp.java
index 7fe572be..7740da18 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/create/CreateIssueMvp.java
+++ b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/create/CreateIssueMvp.java
@@ -4,9 +4,17 @@ import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import com.fastaccess.data.dao.LabelModel;
+import com.fastaccess.data.dao.MilestoneModel;
import com.fastaccess.data.dao.model.Issue;
import com.fastaccess.data.dao.model.PullRequest;
+import com.fastaccess.data.dao.model.User;
import com.fastaccess.ui.base.mvp.BaseMvp;
+import com.fastaccess.ui.modules.repos.extras.assignees.AssigneesMvp;
+import com.fastaccess.ui.modules.repos.extras.labels.LabelsMvp;
+import com.fastaccess.ui.modules.repos.extras.milestone.MilestoneMvp;
+
+import java.util.ArrayList;
/**
* Created by Kosh on 19 Feb 2017, 12:12 PM
@@ -14,7 +22,8 @@ import com.fastaccess.ui.base.mvp.BaseMvp;
public interface CreateIssueMvp {
- interface View extends BaseMvp.FAView {
+ interface View extends BaseMvp.FAView, LabelsMvp.SelectedLabelsListener, AssigneesMvp.SelectedAssigneesListener,
+ MilestoneMvp.OnMilestoneSelected {
void onSetCode(@NonNull CharSequence charSequence);
void onTitleError(boolean isEmptyTitle);
@@ -26,14 +35,23 @@ public interface CreateIssueMvp {
void onSuccessSubmission(PullRequest issueModel);
void onShowUpdate();
+
+ void onShowIssueMisc();
}
interface Presenter extends BaseMvp.FAPresenter {
+
+ void checkAuthority(@NonNull String login, @NonNull String repoId);
+
void onActivityForResult(int resultCode, int requestCode, Intent intent);
void onSubmit(@NonNull String title, @NonNull CharSequence description, @NonNull String login,
- @NonNull String repo, @Nullable Issue issueModel, @Nullable PullRequest pullRequestModel);
+ @NonNull String repo, @Nullable Issue issueModel, @Nullable PullRequest pullRequestModel,
+ @Nullable ArrayList labels, @Nullable MilestoneModel milestoneModel,
+ @Nullable ArrayList users);
void onCheckAppVersion();
+
+ boolean isCollaborator();
}
}
diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/create/CreateIssuePresenter.java b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/create/CreateIssuePresenter.java
index 00d5989f..4452103f 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/create/CreateIssuePresenter.java
+++ b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/create/CreateIssuePresenter.java
@@ -5,24 +5,47 @@ import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import com.annimon.stream.Collectors;
+import com.annimon.stream.Stream;
import com.fastaccess.BuildConfig;
import com.fastaccess.R;
import com.fastaccess.data.dao.CreateIssueModel;
import com.fastaccess.data.dao.IssueRequestModel;
+import com.fastaccess.data.dao.LabelListModel;
+import com.fastaccess.data.dao.LabelModel;
+import com.fastaccess.data.dao.MilestoneModel;
+import com.fastaccess.data.dao.UsersListModel;
import com.fastaccess.data.dao.model.Issue;
+import com.fastaccess.data.dao.model.Login;
import com.fastaccess.data.dao.model.PullRequest;
+import com.fastaccess.data.dao.model.User;
import com.fastaccess.helper.BundleConstant;
import com.fastaccess.helper.InputHelper;
+import com.fastaccess.helper.RxHelper;
import com.fastaccess.provider.rest.RestProvider;
import com.fastaccess.ui.base.mvp.BaseMvp;
import com.fastaccess.ui.base.mvp.presenter.BasePresenter;
+import java.util.ArrayList;
+
/**
* Created by Kosh on 19 Feb 2017, 12:18 PM
*/
public class CreateIssuePresenter extends BasePresenter implements CreateIssueMvp.Presenter {
+ @com.evernote.android.state.State boolean isCollaborator;
+
+
+ @Override public void checkAuthority(@NonNull String login, @NonNull String repoId) {
+ manageViewDisposable(RxHelper.getObservable(RestProvider.getRepoService(isEnterprise()).
+ isCollaborator(login, repoId, Login.getUser().getLogin()))
+ .subscribe(booleanResponse -> {
+ isCollaborator = booleanResponse.code() == 204;
+ sendToView(CreateIssueMvp.View::onShowIssueMisc);
+ }, Throwable::printStackTrace));
+ }
+
@Override public void onActivityForResult(int resultCode, int requestCode, Intent intent) {
if (resultCode == Activity.RESULT_OK && requestCode == BundleConstant.REQUEST_CODE) {
if (intent != null && intent.getExtras() != null) {
@@ -34,9 +57,11 @@ public class CreateIssuePresenter extends BasePresenter imp
}
}
- @Override public void onSubmit(@NonNull String title, @NonNull CharSequence description,
- @NonNull String login, @NonNull String repo,
- @Nullable Issue issue, @Nullable PullRequest pullRequestModel) {
+ @Override public void onSubmit(@NonNull String title, @NonNull CharSequence description, @NonNull String login,
+ @NonNull String repo, @Nullable Issue issue, @Nullable PullRequest pullRequestModel,
+ @Nullable ArrayList labels, @Nullable MilestoneModel milestoneModel,
+ @Nullable ArrayList users) {
+
boolean isEmptyTitle = InputHelper.isEmpty(title);
if (getView() != null) {
getView().onTitleError(isEmptyTitle);
@@ -46,6 +71,17 @@ public class CreateIssuePresenter extends BasePresenter imp
CreateIssueModel createIssue = new CreateIssueModel();
createIssue.setBody(InputHelper.toString(description));
createIssue.setTitle(title);
+ if (isCollaborator) {
+ if (labels != null && !labels.isEmpty()) {
+ createIssue.setLabels(Stream.of(labels).map(LabelModel::getName).collect(Collectors.toCollection(ArrayList::new)));
+ }
+ if (users != null && !users.isEmpty()) {
+ createIssue.setAssignees(Stream.of(users).map(User::getLogin).collect(Collectors.toCollection(ArrayList::new)));
+ }
+ if (milestoneModel != null) {
+ createIssue.setMilestone((long) milestoneModel.getNumber());
+ }
+ }
makeRestCall(RestProvider.getIssueService(isEnterprise()).createIssue(login, repo, createIssue),
issueModel -> {
if (issueModel != null) {
@@ -59,6 +95,21 @@ public class CreateIssuePresenter extends BasePresenter imp
issue.setBody(InputHelper.toString(description));
issue.setTitle(title);
int number = issue.getNumber();
+ if (isCollaborator) {
+ if (labels != null) {
+ LabelListModel labelModels = new LabelListModel();
+ labelModels.addAll(labels);
+ issue.setLabels(labelModels);
+ }
+ if (milestoneModel != null) {
+ issue.setMilestone(milestoneModel);
+ }
+ if (users != null) {
+ UsersListModel usersListModel = new UsersListModel();
+ usersListModel.addAll(users);
+ issue.setAssignees(usersListModel);
+ }
+ }
IssueRequestModel requestModel = IssueRequestModel.clone(issue, false);
makeRestCall(RestProvider.getIssueService(isEnterprise()).editIssue(login, repo, number, requestModel),
issueModel -> {
@@ -73,6 +124,21 @@ public class CreateIssuePresenter extends BasePresenter imp
int number = pullRequestModel.getNumber();
pullRequestModel.setBody(InputHelper.toString(description));
pullRequestModel.setTitle(title);
+ if (isCollaborator) {
+ if (labels != null) {
+ LabelListModel labelModels = new LabelListModel();
+ labelModels.addAll(labels);
+ pullRequestModel.setLabels(labelModels);
+ }
+ if (milestoneModel != null) {
+ pullRequestModel.setMilestone(milestoneModel);
+ }
+ if (users != null) {
+ UsersListModel usersListModel = new UsersListModel();
+ usersListModel.addAll(users);
+ pullRequestModel.setAssignees(usersListModel);
+ }
+ }
IssueRequestModel requestModel = IssueRequestModel.clone(pullRequestModel, false);
makeRestCall(RestProvider.getPullRequestService(isEnterprise()).editPullRequest(login, repo, number, requestModel)
.flatMap(pullRequest1 -> RestProvider.getIssueService(isEnterprise()).getIssue(login, repo, number),
@@ -91,6 +157,7 @@ public class CreateIssuePresenter extends BasePresenter imp
}
}
}
+
}
@Override public void onCheckAppVersion() {
@@ -105,4 +172,8 @@ public class CreateIssuePresenter extends BasePresenter imp
}
}, false);
}
+
+ @Override public boolean isCollaborator() {
+ return isCollaborator;
+ }
}
diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/projects/RepoProjectsFragmentPager.kt b/app/src/main/java/com/fastaccess/ui/modules/repos/projects/RepoProjectsFragmentPager.kt
index 61467b61..b93458eb 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/repos/projects/RepoProjectsFragmentPager.kt
+++ b/app/src/main/java/com/fastaccess/ui/modules/repos/projects/RepoProjectsFragmentPager.kt
@@ -39,7 +39,7 @@ class RepoProjectsFragmentPager : BaseFragment? = null
private val adapter by lazy { ColumnCardAdapter(presenter.getCards(), isOwner()) }
@@ -119,6 +122,9 @@ class ProjectColumnFragment : BaseFragment {
- if (!presenter.login.isBlank() && !presenter.repoId.isBlank()) {
- val nameParse = NameParser("")
- nameParse.name = presenter.repoId
- nameParse.username = presenter.login
- nameParse.isEnterprise = isEnterprise
- RepoPagerActivity.startRepoPager(this, nameParse)
+ val repoId = presenter.repoId
+ if (repoId != null && !repoId.isNullOrBlank()) {
+ if (!presenter.login.isBlank()) {
+ val nameParse = NameParser("")
+ nameParse.name = presenter.repoId
+ nameParse.username = presenter.login
+ nameParse.isEnterprise = isEnterprise
+ RepoPagerActivity.startRepoPager(this, nameParse)
+ }
+ } else if (!presenter.login.isBlank()) {
+ UserPagerActivity.startActivity(this, presenter.login, true, isEnterprise, 0)
}
finish()
true
@@ -98,10 +104,12 @@ class ProjectPagerActivity : BaseActivity(), ProjectPage
private val columns = arrayListOf()
@com.evernote.android.state.State var projectId: Long = -1
- @com.evernote.android.state.State var repoId: String = ""
+ @com.evernote.android.state.State var repoId: String? = null
@com.evernote.android.state.State var login: String = ""
@com.evernote.android.state.State var viewerCanUpdate: Boolean = false
@@ -27,23 +27,39 @@ class ProjectPagerPresenter : BasePresenter(), ProjectPage
override fun onRetrieveColumns() {
- makeRestCall(Observable.zip(RestProvider.getProjectsService(isEnterprise).getProjectColumns(projectId),
- RestProvider.getRepoService(isEnterprise).isCollaborator(login, repoId, Login.getUser().login),
- BiFunction { items: Pageable, response: Response ->
- viewerCanUpdate = response.code() == 204
- return@BiFunction items
- })
- .flatMap {
- if (it.items != null) {
- return@flatMap Observable.just(it.items)
- }
- return@flatMap Observable.just(listOf())
- },
- { t ->
- columns.clear()
- columns.addAll(t)
- sendToView { it.onInitPager(columns) }
- })
+ val repoId = repoId
+ if (repoId != null && !repoId.isNullOrBlank()) {
+ makeRestCall(Observable.zip(RestProvider.getProjectsService(isEnterprise).getProjectColumns(projectId),
+ RestProvider.getRepoService(isEnterprise).isCollaborator(login, repoId, Login.getUser().login),
+ BiFunction { items: Pageable, response: Response ->
+ viewerCanUpdate = response.code() == 204
+ return@BiFunction items
+ })
+ .flatMap {
+ if (it.items != null) {
+ return@flatMap Observable.just(it.items)
+ }
+ return@flatMap Observable.just(listOf())
+ },
+ { t ->
+ columns.clear()
+ columns.addAll(t)
+ sendToView { it.onInitPager(columns) }
+ })
+ } else {
+ makeRestCall(RestProvider.getProjectsService(isEnterprise).getProjectColumns(projectId)
+ .flatMap {
+ if (it.items != null) {
+ return@flatMap Observable.just(it.items)
+ }
+ return@flatMap Observable.just(listOf())
+ },
+ { t ->
+ columns.clear()
+ columns.addAll(t)
+ sendToView { it.onInitPager(columns) }
+ })
+ }
}
override fun onActivityCreated(intent: Intent?) {
diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/projects/list/RepoProjectFragment.kt b/app/src/main/java/com/fastaccess/ui/modules/repos/projects/list/RepoProjectFragment.kt
index 057e5a38..0deb0581 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/repos/projects/list/RepoProjectFragment.kt
+++ b/app/src/main/java/com/fastaccess/ui/modules/repos/projects/list/RepoProjectFragment.kt
@@ -134,7 +134,7 @@ class RepoProjectFragment : BaseFragment(), RepoProjectMv
private var previousTotal: Int = 0
private var lastPage = Integer.MAX_VALUE
@com.evernote.android.state.State var login: String = ""
- @com.evernote.android.state.State var repoId: String = ""
+ @com.evernote.android.state.State var repoId: String? = null
var count: Int = 0
val pages = arrayListOf()
override fun onItemClick(position: Int, v: View, item: RepoProjectsOpenQuery.Node) {
item.databaseId()?.let {
- ProjectPagerActivity.startActivity(v.context, login, repoId, it.toLong())
+ ProjectPagerActivity.startActivity(v.context, login, repoId, it.toLong(), isEnterprise)
}
}
@@ -65,72 +68,152 @@ class RepoProjectPresenter : BasePresenter(), RepoProjectMv
return false
}
currentPage = page
+ Logger.e(login)
+ val repoId = repoId
val apollo = ApolloProdivder.getApollo(isEnterprise)
- if (parameter == IssueState.open) {
- val query = RepoProjectsOpenQuery.builder()
- .name(repoId)
- .owner(login)
- .page(getPage())
- .build()
- makeRestCall(Rx2Apollo.from(apollo.query(query))
- .flatMap {
- val list = arrayListOf()
- it.data()?.repository()?.let {
- it.projects().let {
- pages.clear()
- count = it.totalCount()
- it.edges()?.let {
- pages.addAll(it.map { it.cursor() })
- }
- it.nodes()?.let {
- list.addAll(it)
- }
- }
- }
- return@flatMap Observable.just(list)
- },
- {
- sendToView({ v ->
- v.onNotifyAdapter(it, page)
- if (page == 1) v.onChangeTotalCount(count)
- })
- })
- } else {
- val query = RepoProjectsClosedQuery.builder()
- .name(repoId)
- .owner(login)
- .page(getPage())
- .build()
- makeRestCall(Rx2Apollo.from(apollo.query(query))
- .flatMap {
- val list = arrayListOf()
- it.data()?.repository()?.let {
- it.projects().let {
- pages.clear()
- count = it.totalCount()
- it.edges()?.let {
- pages.addAll(it.map { it.cursor() })
- }
- it.nodes()?.let {
- val toConvert = arrayListOf()
- it.onEach {
- val columns = RepoProjectsOpenQuery.Columns(it.columns().__typename(), it.columns().totalCount())
- val node = RepoProjectsOpenQuery.Node(it.__typename(), it.name(), it.number(), it.body(),
- it.createdAt(), it.id(), it.viewerCanUpdate(), columns, it.databaseId())
- toConvert.add(node)
+ if (repoId != null && !repoId.isNullOrBlank()) {
+ if (parameter == IssueState.open) {
+ val query = RepoProjectsOpenQuery.builder()
+ .name(repoId)
+ .owner(login)
+ .page(getPage())
+ .build()
+ makeRestCall(Rx2Apollo.from(apollo.query(query))
+ .flatMap {
+ val list = arrayListOf()
+ it.data()?.repository()?.let {
+ it.projects().let {
+ lastPage = if (it.pageInfo().hasNextPage()) Int.MAX_VALUE else 0
+ pages.clear()
+ count = it.totalCount()
+ it.edges()?.let {
+ pages.addAll(it.map { it.cursor() })
+ }
+ it.nodes()?.let {
+ list.addAll(it)
}
- list.addAll(toConvert)
}
}
- }
- return@flatMap Observable.just(list)
- },
- {
- sendToView({ v ->
- v.onNotifyAdapter(it, page)
- if (page == 1) v.onChangeTotalCount(count)
+ return@flatMap Observable.just(list)
+ },
+ {
+ sendToView({ v ->
+ v.onNotifyAdapter(it, page)
+ if (page == 1) v.onChangeTotalCount(count)
+ })
})
- })
+ } else {
+ val query = RepoProjectsClosedQuery.builder()
+ .name(repoId)
+ .owner(login)
+ .page(getPage())
+ .build()
+ makeRestCall(Rx2Apollo.from(apollo.query(query))
+ .flatMap {
+ val list = arrayListOf()
+ it.data()?.repository()?.let {
+ it.projects().let {
+ lastPage = if (it.pageInfo().hasNextPage()) Int.MAX_VALUE else 0
+ pages.clear()
+ count = it.totalCount()
+ it.edges()?.let {
+ pages.addAll(it.map { it.cursor() })
+ }
+ it.nodes()?.let {
+ val toConvert = arrayListOf()
+ it.onEach {
+ val columns = RepoProjectsOpenQuery.Columns(it.columns().__typename(), it.columns().totalCount())
+ val node = RepoProjectsOpenQuery.Node(it.__typename(), it.name(), it.number(), it.body(),
+ it.createdAt(), it.id(), it.viewerCanUpdate(), columns, it.databaseId())
+ toConvert.add(node)
+ }
+ list.addAll(toConvert)
+ }
+ }
+ }
+ return@flatMap Observable.just(list)
+ },
+ {
+ sendToView({ v ->
+ v.onNotifyAdapter(it, page)
+ if (page == 1) v.onChangeTotalCount(count)
+ })
+ })
+ }
+ } else {
+ if (parameter == IssueState.open) {
+ val query = OrgProjectsOpenQuery.builder()
+ .owner(login)
+ .page(getPage())
+ .build()
+ makeRestCall(Rx2Apollo.from(apollo.query(query))
+ .flatMap {
+ val list = arrayListOf()
+ it.data()?.organization()?.let {
+ it.projects().let {
+ lastPage = if (it.pageInfo().hasNextPage()) Int.MAX_VALUE else 0
+ pages.clear()
+ count = it.totalCount()
+ it.edges()?.let {
+ pages.addAll(it.map { it.cursor() })
+ }
+ it.nodes()?.let {
+ val toConvert = arrayListOf()
+ it.onEach {
+ val columns = RepoProjectsOpenQuery.Columns(it.columns().__typename(), it.columns().totalCount())
+ val node = RepoProjectsOpenQuery.Node(it.__typename(), it.name(), it.number(), it.body(),
+ it.createdAt(), it.id(), it.viewerCanUpdate(), columns, it.databaseId())
+ toConvert.add(node)
+ }
+ list.addAll(toConvert)
+ }
+ }
+ }
+ return@flatMap Observable.just(list)
+ },
+ {
+ sendToView({ v ->
+ v.onNotifyAdapter(it, page)
+ if (page == 1) v.onChangeTotalCount(count)
+ })
+ })
+ } else {
+ val query = OrgProjectsClosedQuery.builder()
+ .owner(login)
+ .page(getPage())
+ .build()
+ makeRestCall(Rx2Apollo.from(apollo.query(query))
+ .flatMap {
+ val list = arrayListOf()
+ it.data()?.organization()?.let {
+ it.projects().let {
+ lastPage = if (it.pageInfo().hasNextPage()) Int.MAX_VALUE else 0
+ pages.clear()
+ count = it.totalCount()
+ it.edges()?.let {
+ pages.addAll(it.map { it.cursor() })
+ }
+ it.nodes()?.let {
+ val toConvert = arrayListOf()
+ it.onEach {
+ val columns = RepoProjectsOpenQuery.Columns(it.columns().__typename(), it.columns().totalCount())
+ val node = RepoProjectsOpenQuery.Node(it.__typename(), it.name(), it.number(), it.body(),
+ it.createdAt(), it.id(), it.viewerCanUpdate(), columns, it.databaseId())
+ toConvert.add(node)
+ }
+ list.addAll(toConvert)
+ }
+ }
+ }
+ return@flatMap Observable.just(list)
+ },
+ {
+ sendToView({ v ->
+ v.onNotifyAdapter(it, page)
+ if (page == 1) v.onChangeTotalCount(count)
+ })
+ })
+ }
}
return true
}
diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/PullRequestFilesFragment.java b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/PullRequestFilesFragment.java
index cb907c98..7ef7952d 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/PullRequestFilesFragment.java
+++ b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/PullRequestFilesFragment.java
@@ -1,6 +1,8 @@
package com.fastaccess.ui.modules.repos.pull_requests.pull_request.details.files;
+import android.app.Activity;
import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -24,6 +26,7 @@ import com.fastaccess.ui.adapter.CommitFilesAdapter;
import com.fastaccess.ui.base.BaseFragment;
import com.fastaccess.ui.modules.main.premium.PremiumActivity;
import com.fastaccess.ui.modules.repos.issues.issue.details.IssuePagerMvp;
+import com.fastaccess.ui.modules.repos.pull_requests.pull_request.details.files.fullscreen.FullScreenFileChangeActivity;
import com.fastaccess.ui.modules.reviews.AddReviewDialogFragment;
import com.fastaccess.ui.widgets.FontTextView;
import com.fastaccess.ui.widgets.StateLayout;
@@ -169,6 +172,10 @@ public class PullRequestFilesFragment extends BaseFragment comments = data.getParcelableArrayListExtra(BundleConstant.ITEM);
+ if (comments != null && !comments.isEmpty()) {
+ if (viewCallback != null) {
+ for (CommentRequestModel comment : comments) {
+ viewCallback.onAddComment(comment);
+ }
+ showMessage(R.string.success, R.string.comments_added_successfully);
+ }
+ }
+ }
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
private void showReload() {
hideProgress();
stateLayout.showReload(adapter.getItemCount());
diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/PullRequestFilesMvp.java b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/PullRequestFilesMvp.java
index 24123bbd..a75bc1d1 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/PullRequestFilesMvp.java
+++ b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/PullRequestFilesMvp.java
@@ -29,6 +29,8 @@ public interface PullRequestFilesMvp {
void onNotifyAdapter(@Nullable List items, int page);
@NonNull OnLoadMore getLoadMore();
+
+ void onOpenForResult(int position, @NonNull CommitFileChanges linesModel);
}
interface Presenter extends BaseMvp.FAPresenter,
diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/PullRequestFilesPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/PullRequestFilesPresenter.java
index ab1f40bf..4f3d126e 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/PullRequestFilesPresenter.java
+++ b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/PullRequestFilesPresenter.java
@@ -104,7 +104,9 @@ class PullRequestFilesPresenter extends BasePresenter
}
@Override public void onItemClick(int position, View v, CommitFileChanges model) {
- if (v.getId() == R.id.open) {
+ if (v.getId() == R.id.patchList) {
+ sendToView(view -> view.onOpenForResult(position, model));
+ } else if (v.getId() == R.id.open) {
CommitFileModel item = model.getCommitFileModel();
PopupMenu popup = new PopupMenu(v.getContext(), v);
MenuInflater inflater = popup.getMenuInflater();
diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/fullscreen/FullScreenFileChangeActivity.kt b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/fullscreen/FullScreenFileChangeActivity.kt
new file mode 100644
index 00000000..0e46dd7c
--- /dev/null
+++ b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/fullscreen/FullScreenFileChangeActivity.kt
@@ -0,0 +1,168 @@
+package com.fastaccess.ui.modules.repos.pull_requests.pull_request.details.files.fullscreen
+
+import android.app.Activity
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.support.v4.app.Fragment
+import android.support.v4.widget.SwipeRefreshLayout
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import android.widget.TextView
+import butterknife.BindView
+import com.fastaccess.R
+import com.fastaccess.data.dao.CommentRequestModel
+import com.fastaccess.data.dao.CommitFileChanges
+import com.fastaccess.data.dao.CommitLinesModel
+import com.fastaccess.helper.BundleConstant
+import com.fastaccess.helper.Bundler
+import com.fastaccess.helper.PrefGetter
+import com.fastaccess.ui.adapter.CommitLinesAdapter
+import com.fastaccess.ui.base.BaseActivity
+import com.fastaccess.ui.modules.main.premium.PremiumActivity
+import com.fastaccess.ui.modules.reviews.AddReviewDialogFragment
+import com.fastaccess.ui.widgets.StateLayout
+import com.fastaccess.ui.widgets.recyclerview.DynamicRecyclerView
+import com.fastaccess.ui.widgets.recyclerview.scroll.RecyclerViewFastScroller
+
+/**
+ * Created by Hashemsergani on 24.09.17.
+ */
+
+class FullScreenFileChangeActivity : BaseActivity(), FullScreenFileChangeMvp.View {
+
+ @BindView(R.id.recycler) lateinit var recycler: DynamicRecyclerView
+ @BindView(R.id.refresh) lateinit var refresh: SwipeRefreshLayout
+ @BindView(R.id.stateLayout) lateinit var stateLayout: StateLayout
+ @BindView(R.id.fastScroller) lateinit var fastScroller: RecyclerViewFastScroller
+ @BindView(R.id.changes) lateinit var changes: TextView
+ @BindView(R.id.deletion) lateinit var deletion: TextView
+ @BindView(R.id.addition) lateinit var addition: TextView
+
+ val commentList = arrayListOf()
+
+ private val adapter by lazy { CommitLinesAdapter(arrayListOf(), this) }
+
+ override fun layout(): Int = R.layout.full_screen_file_changes_layout
+
+ override fun isTransparent(): Boolean = false
+
+ override fun canBack(): Boolean = true
+
+ override fun isSecured(): Boolean = false
+
+ override fun providePresenter(): FullScreenFileChangePresenter = FullScreenFileChangePresenter()
+
+ override fun onNotifyAdapter(model: CommitLinesModel) {
+ adapter.addItem(model)
+ }
+
+ override fun showMessage(titleRes: Int, msgRes: Int) {
+ hideProgress()
+ super.showMessage(titleRes, msgRes)
+ }
+
+ override fun showMessage(titleRes: String, msgRes: String) {
+ hideProgress()
+ super.showMessage(titleRes, msgRes)
+ }
+
+ override fun showErrorMessage(msgRes: String) {
+ hideProgress()
+ super.showErrorMessage(msgRes)
+ }
+
+ override fun showProgress(resId: Int) {
+ stateLayout.showProgress()
+ refresh.isRefreshing = true
+ }
+
+ override fun hideProgress() {
+ stateLayout.hideProgress()
+ refresh.isRefreshing = false
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ refresh.isEnabled = false
+ recycler.adapter = adapter
+ val padding = resources.getDimensionPixelSize(R.dimen.spacing_normal)
+ recycler.setPadding(padding, 0, padding, 0)
+ fastScroller.attachRecyclerView(recycler)
+ presenter.onLoad(intent)
+ presenter.model?.let { model ->
+ title = Uri.parse(model.commitFileModel.filename).lastPathSegment
+ addition.text = model.commitFileModel?.additions.toString()
+ deletion.text = model.commitFileModel?.deletions.toString()
+ changes.text = model.commitFileModel?.changes.toString()
+ }
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+ menuInflater.inflate(R.menu.done_menu, menu)
+ menu?.findItem(R.id.submit)?.setIcon(R.drawable.ic_done)
+ return super.onCreateOptionsMenu(menu)
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem?): Boolean {
+ return when (item?.itemId) {
+ R.id.submit -> {
+ val intent = Intent()
+ intent.putExtras(Bundler.start().putParcelableArrayList(BundleConstant.ITEM, commentList).end())
+ setResult(Activity.RESULT_OK, intent)
+ finish()
+ true
+ }
+ else -> super.onOptionsItemSelected(item)
+ }
+ }
+
+ override fun onItemClick(position: Int, v: View, item: CommitLinesModel) {
+ if (item.text.startsWith("@@")) return
+ val commit = presenter.model?.commitFileModel ?: return
+ if (PrefGetter.isProEnabled()) {
+ AddReviewDialogFragment.newInstance(item, Bundler.start()
+ .put(BundleConstant.ITEM, commit.filename)
+ .put(BundleConstant.EXTRA_TWO, presenter.position)
+ .put(BundleConstant.EXTRA_THREE, position)
+ .end())
+ .show(supportFragmentManager, "AddReviewDialogFragment")
+ } else {
+ PremiumActivity.startActivity(this)
+ }
+ }
+
+ override fun onItemLongClick(position: Int, v: View?, item: CommitLinesModel?) {
+
+ }
+
+ override fun onCommentAdded(comment: String, item: CommitLinesModel, bundle: Bundle?) {
+ if (bundle != null) {
+ val path = bundle.getString(BundleConstant.ITEM) ?: return
+ val commentRequestModel = CommentRequestModel()
+ commentRequestModel.body = comment
+ commentRequestModel.path = path
+ commentRequestModel.position = item.position
+ commentList.add(commentRequestModel)
+ val childPosition = bundle.getInt(BundleConstant.EXTRA_THREE)
+ val current = adapter.getItem(childPosition)
+ if (current != null) {
+ current.isHasCommentedOn = true
+ adapter.swapItem(current, childPosition)
+ }
+ }
+ }
+
+ companion object {
+ val FOR_RESULT_CODE = 1002
+ fun startActivityForResult(fragment: Fragment, model: CommitFileChanges, position: Int) {
+ val intent = Intent(fragment.context, FullScreenFileChangeActivity::class.java)
+ intent.putExtras(Bundler.start()
+ .put(BundleConstant.EXTRA, model)
+ .put(BundleConstant.ITEM, position)
+ .end())
+ fragment.startActivityForResult(intent, FOR_RESULT_CODE)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/fullscreen/FullScreenFileChangeMvp.kt b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/fullscreen/FullScreenFileChangeMvp.kt
new file mode 100644
index 00000000..398eedff
--- /dev/null
+++ b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/fullscreen/FullScreenFileChangeMvp.kt
@@ -0,0 +1,21 @@
+package com.fastaccess.ui.modules.repos.pull_requests.pull_request.details.files.fullscreen
+
+import android.content.Intent
+import com.fastaccess.data.dao.CommitLinesModel
+import com.fastaccess.ui.base.mvp.BaseMvp
+import com.fastaccess.ui.modules.reviews.callback.ReviewCommentListener
+import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder
+
+/**
+ * Created by Hashemsergani on 24.09.17.
+ */
+
+interface FullScreenFileChangeMvp {
+ interface View : BaseMvp.FAView, BaseViewHolder.OnItemClickListener, ReviewCommentListener {
+ fun onNotifyAdapter(model: CommitLinesModel)
+ }
+
+ interface Presenter {
+ fun onLoad(intent: Intent)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/fullscreen/FullScreenFileChangePresenter.kt b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/fullscreen/FullScreenFileChangePresenter.kt
new file mode 100644
index 00000000..6c60e576
--- /dev/null
+++ b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/files/fullscreen/FullScreenFileChangePresenter.kt
@@ -0,0 +1,37 @@
+package com.fastaccess.ui.modules.repos.pull_requests.pull_request.details.files.fullscreen
+
+import android.content.Intent
+import com.fastaccess.data.dao.CommitFileChanges
+import com.fastaccess.helper.BundleConstant
+import com.fastaccess.helper.RxHelper
+import com.fastaccess.ui.base.mvp.presenter.BasePresenter
+import io.reactivex.Observable
+
+/**
+ * Created by Hashemsergani on 24.09.17.
+ */
+class FullScreenFileChangePresenter : BasePresenter(), FullScreenFileChangeMvp.Presenter {
+
+ var model: CommitFileChanges? = null
+ var position: Int = -1
+
+ override fun onLoad(intent: Intent) {
+ intent.extras?.let {
+ position = it.getInt(BundleConstant.ITEM)
+ model = it.getParcelable(BundleConstant.EXTRA)
+ }
+ model?.let {
+ manageDisposable(RxHelper.getObservable(Observable.fromIterable(it.linesModel))
+ .doOnSubscribe({ sendToView { it.showProgress(0) } })
+ .flatMap { Observable.just(it) }
+ .subscribe
+ ({
+ sendToView { v -> v.onNotifyAdapter(it) }
+ }, {
+ onError(it)
+ }, {
+ sendToView { it.hideProgress() }
+ }))
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/fastaccess/ui/modules/reviews/AddReviewDialogFragment.kt b/app/src/main/java/com/fastaccess/ui/modules/reviews/AddReviewDialogFragment.kt
index ea599c0d..8996204d 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/reviews/AddReviewDialogFragment.kt
+++ b/app/src/main/java/com/fastaccess/ui/modules/reviews/AddReviewDialogFragment.kt
@@ -39,10 +39,10 @@ class AddReviewDialogFragment : BaseDialogFragment implements SearchMvp
}
@Override public void onSearchClicked(@NonNull ViewPager viewPager, @NonNull AutoCompleteTextView editText) {
- boolean isEmpty = InputHelper.isEmpty(editText) || InputHelper.toString(editText).length() < 3;
+ boolean isEmpty = InputHelper.isEmpty(editText) || InputHelper.toString(editText).length() < 2;
editText.setError(isEmpty ? editText.getResources().getString(R.string.minimum_three_chars) : null);
if (!isEmpty) {
editText.dismissDropDown();
diff --git a/app/src/main/java/com/fastaccess/ui/modules/search/repos/files/SearchFilePresenter.java b/app/src/main/java/com/fastaccess/ui/modules/search/repos/files/SearchFilePresenter.java
index 8c88c8e8..fd974684 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/search/repos/files/SearchFilePresenter.java
+++ b/app/src/main/java/com/fastaccess/ui/modules/search/repos/files/SearchFilePresenter.java
@@ -19,7 +19,7 @@ public class SearchFilePresenter extends BasePresenter imple
}
@Override public void onSearchClicked(@NonNull FontEditText editText, boolean inPath) {
- boolean isEmpty = InputHelper.isEmpty(editText) || InputHelper.toString(editText).length() < 3;
+ boolean isEmpty = InputHelper.isEmpty(editText) || InputHelper.toString(editText).length() < 2;
editText.setError(isEmpty ? editText.getResources().getString(R.string.minimum_three_chars) : null);
if (!isEmpty) {
AppHelper.hideKeyboard(editText);
diff --git a/app/src/main/java/com/fastaccess/ui/modules/settings/sound/NotificationSoundBottomSheet.kt b/app/src/main/java/com/fastaccess/ui/modules/settings/sound/NotificationSoundBottomSheet.kt
index 7b8babf0..a57f6838 100644
--- a/app/src/main/java/com/fastaccess/ui/modules/settings/sound/NotificationSoundBottomSheet.kt
+++ b/app/src/main/java/com/fastaccess/ui/modules/settings/sound/NotificationSoundBottomSheet.kt
@@ -35,10 +35,10 @@ class NotificationSoundBottomSheet : BaseMvpBottomSheetDialogFragment(), Tr
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
- when (item?.itemId) {
+ return when (item?.itemId) {
R.id.menu -> {
drawerLayout.openDrawer(Gravity.END)
- return true
+ true
}
android.R.id.home -> {
startActivity(Intent(this, MainActivity::class.java))
finish()
- return true
+ true
}
- else -> return super.onOptionsItemSelected(item)
+ else -> super.onOptionsItemSelected(item)
}
}
@@ -156,9 +156,9 @@ class TrendingActivity : BaseActivity(), Tr
}
private fun onItemClicked(item: MenuItem?): Boolean {
- when (item?.title.toString()) {
- "All Language" -> selectedTitle = ""
- else -> selectedTitle = item?.title.toString()
+ selectedTitle = when (item?.title.toString()) {
+ "All Language" -> ""
+ else -> item?.title.toString()
}
Logger.e(selectedTitle)
setValues()
@@ -176,11 +176,11 @@ class TrendingActivity : BaseActivity(), Tr
}
private fun getSince(): String {
- when {
- daily.isSelected -> return "daily"
- weekly.isSelected -> return "weekly"
- monthly.isSelected -> return "monthly"
- else -> return "daily"
+ return when {
+ daily.isSelected -> "daily"
+ weekly.isSelected -> "weekly"
+ monthly.isSelected -> "monthly"
+ else -> "daily"
}
}
diff --git a/app/src/main/java/com/fastaccess/ui/widgets/dialog/ProgressDialogFragment.java b/app/src/main/java/com/fastaccess/ui/widgets/dialog/ProgressDialogFragment.java
index 19048f83..3869c8dd 100644
--- a/app/src/main/java/com/fastaccess/ui/widgets/dialog/ProgressDialogFragment.java
+++ b/app/src/main/java/com/fastaccess/ui/widgets/dialog/ProgressDialogFragment.java
@@ -9,6 +9,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.view.View;
+import android.view.Window;
import com.fastaccess.R;
import com.fastaccess.helper.Bundler;
@@ -23,6 +24,10 @@ import net.grandcentrix.thirtyinch.TiPresenter;
public class ProgressDialogFragment extends BaseDialogFragment {
+ public ProgressDialogFragment() {
+ suppressAnimation = true;
+ }
+
public static final String TAG = ProgressDialogFragment.class.getSimpleName();
@NonNull public static ProgressDialogFragment newInstance(@NonNull Resources resources, @StringRes int msgId, boolean isCancelable) {
@@ -50,7 +55,11 @@ public class ProgressDialogFragment extends BaseDialogFragment {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.setCancelable(false);
setCancelable(false);
- if (dialog.getWindow() != null) dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ Window window = dialog.getWindow();
+ if (window != null) {
+ window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ window.setDimAmount(0);
+ }
return dialog;
}
diff --git a/app/src/main/java/com/fastaccess/ui/widgets/markdown/MarkDownLayout.kt b/app/src/main/java/com/fastaccess/ui/widgets/markdown/MarkDownLayout.kt
index b3715652..835e3579 100644
--- a/app/src/main/java/com/fastaccess/ui/widgets/markdown/MarkDownLayout.kt
+++ b/app/src/main/java/com/fastaccess/ui/widgets/markdown/MarkDownLayout.kt
@@ -33,7 +33,7 @@ class MarkDownLayout : LinearLayout {
}
var markdownListener: MarkdownListener? = null
-
+ var selectionIndex = 0
@BindView(R.id.editorIconsHolder) lateinit var editorIconsHolder: HorizontalScrollView
@BindView(R.id.addEmoji) lateinit var addEmojiView: View
@@ -54,19 +54,20 @@ class MarkDownLayout : LinearLayout {
super.onDetachedFromWindow()
}
- @OnClick(R.id.view) internal fun onViewMarkDown() {
+ @OnClick(R.id.view) fun onViewMarkDown() {
markdownListener?.let {
it.getEditText().let { editText ->
TransitionManager.beginDelayedTransition(this)
if (editText.isEnabled && !InputHelper.isEmpty(editText)) {
editText.isEnabled = false
+ selectionIndex = editText.selectionEnd
MarkDownProvider.setMdText(editText, InputHelper.toString(editText))
editorIconsHolder.visibility = View.INVISIBLE
addEmojiView.visibility = View.INVISIBLE
ViewHelper.hideKeyboard(editText)
} else {
editText.setText(it.getSavedText())
- editText.setSelection(editText.text.length)
+ editText.setSelection(selectionIndex)
editText.isEnabled = true
editorIconsHolder.visibility = View.VISIBLE
addEmojiView.visibility = View.VISIBLE
@@ -87,9 +88,9 @@ class MarkDownLayout : LinearLayout {
Snackbar.make(this, R.string.error_highlighting_editor, Snackbar.LENGTH_SHORT).show()
} else {
when {
- v.id == R.id.link -> EditorLinkImageDialogFragment.newInstance(true)
+ v.id == R.id.link -> EditorLinkImageDialogFragment.newInstance(true, getSelectedText())
.show(it.fragmentManager(), "EditorLinkImageDialogFragment")
- v.id == R.id.image -> EditorLinkImageDialogFragment.newInstance(false)
+ v.id == R.id.image -> EditorLinkImageDialogFragment.newInstance(false, getSelectedText())
.show(it.fragmentManager(), "EditorLinkImageDialogFragment")
v.id == R.id.addEmoji -> {
ViewHelper.hideKeyboard(it.getEditText())
@@ -119,8 +120,6 @@ class MarkDownLayout : LinearLayout {
R.id.header -> MarkDownProvider.addDivider(editText)
R.id.code -> MarkDownProvider.addCode(editText)
R.id.quote -> MarkDownProvider.addQuote(editText)
- R.id.link -> MarkDownProvider.addLink(editText)
- R.id.image -> MarkDownProvider.addPhoto(editText)
R.id.checkbox -> MarkDownProvider.addList(editText, "- [x]")
R.id.unCheckbox -> MarkDownProvider.addList(editText, "- [ ]")
R.id.inlineCode -> MarkDownProvider.addInlinleCode(editText)
@@ -162,4 +161,15 @@ class MarkDownLayout : LinearLayout {
}
}
}
+
+ private fun getSelectedText(): String? {
+ markdownListener?.getEditText()?.let {
+ if (!it.text.toString().isBlank()) {
+ val selectionStart = it.selectionStart
+ val selectionEnd = it.selectionEnd
+ return it.text.toString().substring(selectionStart, selectionEnd)
+ }
+ }
+ return null
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/prettifier/pretty/helper/PrettifyHelper.java b/app/src/main/java/com/prettifier/pretty/helper/PrettifyHelper.java
index 03fd7396..7f3087f3 100644
--- a/app/src/main/java/com/prettifier/pretty/helper/PrettifyHelper.java
+++ b/app/src/main/java/com/prettifier/pretty/helper/PrettifyHelper.java
@@ -8,7 +8,7 @@ import android.support.annotation.NonNull;
public class PrettifyHelper {
- @NonNull private static String getHtmlContent(@NonNull String css, @NonNull String text, @NonNull boolean wrap, boolean isDark) {
+ @NonNull private static String getHtmlContent(@NonNull String css, @NonNull String text, boolean wrap, boolean isDark) {
return "\n" +
"\n" +
"\n" +
diff --git a/app/src/main/res/layouts/main_layouts/layout/create_issue_layout.xml b/app/src/main/res/layouts/main_layouts/layout/create_issue_layout.xml
index 21cb1d0b..fc09b8e3 100644
--- a/app/src/main/res/layouts/main_layouts/layout/create_issue_layout.xml
+++ b/app/src/main/res/layouts/main_layouts/layout/create_issue_layout.xml
@@ -1,7 +1,9 @@
-
+ android:orientation="vertical"
+ android:visibility="gone"
+ tools:visibility="visible">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layouts/main_layouts/layout/emoji_popup_layout.xml b/app/src/main/res/layouts/main_layouts/layout/emoji_popup_layout.xml
index 5a84280b..417978f3 100644
--- a/app/src/main/res/layouts/main_layouts/layout/emoji_popup_layout.xml
+++ b/app/src/main/res/layouts/main_layouts/layout/emoji_popup_layout.xml
@@ -51,7 +51,8 @@
android:background="@color/transparent"
android:hint="@string/search"
android:paddingEnd="@dimen/spacing_xs_large"
- android:paddingStart="@dimen/spacing_xs_large"/>
+ android:paddingStart="@dimen/spacing_xs_large"
+ android:singleLine="true"/>
diff --git a/app/src/main/res/layouts/main_layouts/layout/full_screen_file_changes_layout.xml b/app/src/main/res/layouts/main_layouts/layout/full_screen_file_changes_layout.xml
new file mode 100644
index 00000000..db01da07
--- /dev/null
+++ b/app/src/main/res/layouts/main_layouts/layout/full_screen_file_changes_layout.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layouts/main_layouts/layout/issue_popup_layout.xml b/app/src/main/res/layouts/main_layouts/layout/issue_popup_layout.xml
index 09ebef37..94eba3c5 100644
--- a/app/src/main/res/layouts/main_layouts/layout/issue_popup_layout.xml
+++ b/app/src/main/res/layouts/main_layouts/layout/issue_popup_layout.xml
@@ -193,8 +193,6 @@
style="@style/TextAppearance.AppCompat.Medium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingBottom="@dimen/spacing_normal"
- android:paddingTop="@dimen/spacing_normal"
tools:text="Label 1"/>
diff --git a/app/src/main/res/layouts/main_layouts/layout/playstore_review_layout_warning.xml b/app/src/main/res/layouts/main_layouts/layout/playstore_review_layout_warning.xml
new file mode 100644
index 00000000..9b104901
--- /dev/null
+++ b/app/src/main/res/layouts/main_layouts/layout/playstore_review_layout_warning.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layouts/main_layouts/layout/project_columns_layout.xml b/app/src/main/res/layouts/main_layouts/layout/project_columns_layout.xml
index 22c30db4..24bb3472 100644
--- a/app/src/main/res/layouts/main_layouts/layout/project_columns_layout.xml
+++ b/app/src/main/res/layouts/main_layouts/layout/project_columns_layout.xml
@@ -4,7 +4,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:background="?colorPrimary">
-
-
+ android:paddingTop="@dimen/spacing_normal"
+ tools:text="One must need the visitor in order to study the lord of great awareness."/>
-
+
+
+ android:background="?selectableItemBackgroundBorderless"
+ android:padding="@dimen/spacing_micro"
+ android:src="@drawable/ic_edit"/>
-
+ android:layout_marginEnd="@dimen/spacing_xs_large"
+ android:layout_marginStart="@dimen/spacing_xs_large"
+ android:background="?selectableItemBackgroundBorderless"
+ android:padding="@dimen/spacing_micro"
+ android:src="@drawable/ic_add"/>
-
+
-
-
-
-
-
-
-
+
diff --git a/app/src/main/res/layouts/row_layouts/layout/column_card_row_layout.xml b/app/src/main/res/layouts/row_layouts/layout/column_card_row_layout.xml
index 49d5b3de..f1cb6e86 100644
--- a/app/src/main/res/layouts/row_layouts/layout/column_card_row_layout.xml
+++ b/app/src/main/res/layouts/row_layouts/layout/column_card_row_layout.xml
@@ -5,9 +5,13 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_margin="@dimen/spacing_micro"
+ android:layout_marginBottom="@dimen/spacing_micro"
+ android:layout_marginEnd="@dimen/grid_spacing"
+ android:layout_marginStart="@dimen/grid_spacing"
+ android:layout_marginTop="@dimen/grid_spacing"
android:foreground="?android:selectableItemBackground"
app:cardBackgroundColor="?card_background"
+ app:cardCornerRadius="@dimen/grid_spacing"
app:contentPaddingBottom="@dimen/spacing_normal"
app:contentPaddingLeft="@dimen/spacing_xs_large"
app:contentPaddingTop="@dimen/spacing_normal">
diff --git a/app/src/main/res/layouts/row_layouts/layout/issue_detail_header_row_item.xml b/app/src/main/res/layouts/row_layouts/layout/issue_detail_header_row_item.xml
index 66969400..4509ae6b 100644
--- a/app/src/main/res/layouts/row_layouts/layout/issue_detail_header_row_item.xml
+++ b/app/src/main/res/layouts/row_layouts/layout/issue_detail_header_row_item.xml
@@ -1,134 +1,163 @@
-
+ android:orientation="vertical">
-
+ android:layout_marginBottom="@dimen/spacing_normal"
+ android:layout_marginEnd="@dimen/grid_spacing"
+ android:layout_marginStart="@dimen/grid_spacing"
+ android:layout_marginTop="@dimen/grid_spacing"
+ android:background="?card_background"
+ android:paddingBottom="@dimen/spacing_normal"
+ android:paddingTop="@dimen/spacing_normal"
+ tools:ignore="RtlSymmetry">
-
-
+ android:orientation="vertical">
+ android:layout_marginEnd="@dimen/spacing_micro"
+ android:orientation="horizontal">
+
+
+ android:layout_gravity="center"
+ android:layout_marginEnd="@dimen/spacing_normal"
+ android:layout_weight="1"
+ android:orientation="vertical">
-
+ android:orientation="horizontal">
+
+
+
+
+
+
-
+ android:visibility="gone"
+ tools:text="@string/owner"
+ tools:visibility="visible"/>
-
+ android:background="?selectableItemBackgroundBorderless"
+ android:contentDescription="@string/options"
+ android:padding="@dimen/spacing_micro"
+ android:src="@drawable/ic_add_emoji"/>
+
+
-
+
-
+ android:layout_marginBottom="@dimen/spacing_micro"
+ android:layout_marginEnd="@dimen/spacing_xs_large"
+ android:layout_marginStart="@dimen/spacing_xs_large"
+ android:layout_marginTop="@dimen/spacing_micro"
+ android:textIsSelectable="true"/>
+
+
-
+
+
+
-
-
+ android:gravity="center"/>
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layouts/row_layouts/layout/org_profile_overview_layout.xml b/app/src/main/res/layouts/row_layouts/layout/org_profile_overview_layout.xml
index c7a9bbf7..ffc615d4 100644
--- a/app/src/main/res/layouts/row_layouts/layout/org_profile_overview_layout.xml
+++ b/app/src/main/res/layouts/row_layouts/layout/org_profile_overview_layout.xml
@@ -77,6 +77,7 @@
android:gravity="center|start"
android:paddingBottom="@dimen/spacing_xs_large"
android:paddingTop="@dimen/spacing_xs_large"
+ android:visibility="gone"
tools:text="Cum classis nocere"/>
@@ -92,6 +93,7 @@
android:gravity="center|start"
android:paddingBottom="@dimen/spacing_xs_large"
android:paddingTop="@dimen/spacing_xs_large"
+ android:visibility="gone"
tools:text="Cum classis nocere"/>
@@ -120,8 +123,25 @@
android:gravity="center|start"
android:paddingBottom="@dimen/spacing_xs_large"
android:paddingTop="@dimen/spacing_xs_large"
+ android:visibility="gone"
tools:text="Cum classis nocere"/>
+
+
diff --git a/app/src/main/res/menu/drawer_menu.xml b/app/src/main/res/menu/drawer_menu.xml
index 6aa8c7b4..4608e68a 100644
--- a/app/src/main/res/menu/drawer_menu.xml
+++ b/app/src/main/res/menu/drawer_menu.xml
@@ -55,6 +55,10 @@
android:id="@+id/reportBug"
android:icon="@drawable/ic_bug"
android:title="@string/report_issue"/>
+
-
FastHub changelog
- Version 4.2.0 (Create, Edit & Delete files (make Commits))
+ Version 4.4.1 (Org Project Columns and Cards)
- Reporting Issues or Feature Requests in Google Play review section, will be ignored or might even get your account to be blocked from
- FastHub. You are using an app for GitHub, which provides a proper way to report issues.
-
- Please report the issues in FastHub repo instead, by opening the Drawer Menu, click about and click “Report an Issue”PLEASE
- USE IT.
+
Please report the issues in FastHub repo instead, by opening the Drawer Menu and clicking on “Report an Issue”
+ PLEASE USE IT.
- Bugs , Enhancements & new Features (3.2.0)
+ Bugs , Enhancements & new Features (4.4.1)
- - (New) Make commits on repos from FastHub (this only applies for repo owners so far).
- PRO
-
- - (New) Long pressing files/directories will open up their Commit/Git History.
- - (New) Indicates PR review line has a comment.
- - (New) Showing number of addition, deletion & files in PR files tab.
- - (New) Untoggle MD rendering to see their actual code.
- - (New) Settings option to disable Animations in FastHub.
- - (New) Copy SHA from Commit.
- - (New) Forks, Watchers & Stargazers links handling.
- - (Enhancement) Loading larger number of comments per page.
- - (Enhancement) Hide fab while scrolling in Issues & PR tab.
- - (Fix) PR Status where it got somehow broken in previous release.
- - (Fix) A text under PR status will indicates if the PR is mergable or not.
- - (Fix) Tagging users in full screen editor.
- - (Fix) Comments text selection where sometime they aren’t selectable.
- - (Fix) PR Review footer is invisible in (4.1.0).
- - (Fix) Cursor position after inserting emoji, links & images.
- - (Fix) Teal customized accent was displayed as green.
- - (Fix) Some crashes from the crash report.
+ - (New 4.4.1) Add Labels, Assignees & Milestone when creating/editing Issue
+ - (New) Improvements to handle Students PRO.
+ - (New) Org Project Columns & Cards (Edit, Create & Delete)
+ - (New) Displaying Labels under Issue/Pr description.
+ - (Enhancement) Removal of PR review limit.
+ - (Enhancement) Selected text will be taken in consideration when adding Image/Link.
+ - (Enhancement) Removed Loading background.
+ - (Enhancement) More markdown enhancement.
- (Fix) Lots of bug fixes.
- - There are more stuff are not mentioned, find them out :stuck_out_tongue:
+ - There are more stuff are not mentioned, find them out ;).
What left in FastHub?
- So far, FastHub has implemented almost all the features of GitHub, besides forProject Cards. Hopefully in the
- next release FastHub will include that. The following releases will mainly be bug fixes or if a major feature is implemented.
-
-
-
- How old is FastHub now?
-
-
-
- FastHub is now 5 months & a week old. Since v1.0.0 it has really grow with each and every release. The only way this was
- possible, was due to the community helping out either via reporting bugs, feature requests or even give hand to fix things or
- implement things in the app. I’m really grateful for having such a great community. Thank you guys!
+ So far, FastHub has implemented all the features of GitHub, I'll continue to fix bugs, implement feature requests if any!.
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 6dacd3b7..97e8742c 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -399,6 +399,62 @@
Benachrichtigungs Ton auswählen
Deaktiviere automatische wiedergabe von GIFs
Deaktiviere GIF wiedergabe
+ angeforderte Änderungen
+ Google Play-Dienste sind nicht verfügbar
+ GIST bearbeiten
+ Inhalt
+ erweitern
+ SHA-1 Kopieren
+ Als Code anzeigen
+ In-App-Animationen deaktivieren
+ Deaktiviere in-App-Animationen überall.
+ Dieser PR kann jetzt\jetzt nicht zusammengeführt werden.
+ Projekte
+ Keine Projekte
+ Keine Karten
+ Hinzugefügt von %s %s
+ Zu viele Unterschiede zur Anzeige. Bitte, sehe Sie Sie dir über den Browser an
+ Projekt
+ BITTE lESEN!
+
+ • Warum kann ich meine Organisationen entweder private oder öffentliche sehen\nicht sehen?
+ Öffne https://github.com/Settings/Applications und suche nach FastHub, öffne es dann Blätter zu Organisation Zugang und klicke auf den Grant Knopf,
+ Alternativ melde dich über einen Zugang Token an welches das Setup vereinfacht.
+
+ • Ich habe versuchtt mich mit einem Zugangs Token anzumelden amp; OTP aber es funtioniert nicht?
+ Du kannst dich nicht mit einem zugangs token anmelden & OTP alle Zusammen wegen der Lebenszeit des OTP codes, du musst dich alle paar sekunden anmelden
+
+ • Warum werden mein privates Repo & Enterprise Wiki nicht angezeigt?
+ Es wie FastHub die GitHub Wiki page holt & Private Repos benötigen einen Sitzungs token denn FastHub nicht bekommen kann.
+
+ • Ich habe mich mit meinen Enterprise account angemeldet kann\kann nicht mit anderen sachen interagieren außer meinem Enterprise GitHub?
+ Nun, logisch, kannst du nicht auf etwas anderes als Ihr Unternehmen zugreifen. FastHub versucht, so viel möglich zu erlauben, aber kann
+ in den meisten Fällen nicht viel dafür tun, da Ihre Anmeldedaten auf den GitHub Server nicht vorhanden sind. Aber in einem paar
+ fällen Ihr GitHub Konto OAuth Token wird den Trick tun.
+
+ • Warum habe ich Probleme bei der Bearbeitung von Problemen/PRS?
+ Wenn du ein öffentliches organisations-repo bearbeiten willst, wende dich bitte an deine organisation, um Zugang zu FastHub zu gewähren oder Zugangsdaten zu verwenden!.
+
+ • Ich habe das problem oder Ich möchte dies & das!!
+ Gehe zu https://github.com/k0shk0sh/FastHub/issues/new und erstelle ein neues issue für bugs oder Feature Requests, Ich möchte dich ermutigen
+ das du vor dem erstellen eines Tickets suchst ob es das schon existiert.Jeder doppelter Request wird dazu führen dass du sofort ausgeschlossen wirst.
+
+ • Wie bekomme ich einen PROMO CODE?
+ Wenn du ein Student bist musst du mir eine e-Mail schicken,in der du mir beweist das du ein Student bist, Du brauchst die unten stehen den dokumente:
+
+ - Deine Universitätenausweis & deinen Personalausweis (der deinen Namen zeigt & und dein Gesicht um es zu vergleichen!)
+ - Dein Uni-Start & Enddatum
+ - Bewerte FastHub im Play Store
+
+ Wenn du kein Student bist und du kannst es dir nicht leisten für PRO zu bezahlen, musst du:
+
+ - Einen Arctikel über Fasthub schreiben in Social Media wie (Medium)
+ - Bewerte FastHub im Play Store
+
+ ]]>
+ FAQ
+ Kommentare erfolgreich hinzugefügt
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 0baed60c..570fabc3 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -39,7 +39,7 @@
Colaboradores
Colaboraciones
por
- Clonar Issue
+ Cerrar Issue
Reabrir Issue
Reabrir
Cerrar
@@ -217,10 +217,10 @@
Autor
Fork en GitHub
Enviar un email
- ¿Tenés preguntas sobre FastHub?
+ ¿Tienes preguntas sobre FastHub?
Comentarios
Reportar un error
- ¿Tenés un problema? Reportalo aquí
+ ¿Tienes un problema? Repórtalo aquí
Acerca de
Notificación
Apagar
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index b7a184c7..2af1254d 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -139,7 +139,7 @@
저장소 즐겨찾기/즐겨찾기해제
구독
저장소 구독/구독해제
- 사이드바에서 더 빨리 액세스 할 수 있도록 저장소를 고정하세요
+ 사이드바에서 더 빨리 액세스 할 수 있도록 저장소를 바로가기에 등록하세요
모두 닫기
URL을 찾을 수 없습니다
마지막 업데이트
@@ -153,7 +153,7 @@
모두 읽음으로 표시
모든 알림
읽지 않음
- 모든
+ 전체
저장소 삭제
저장소 삭제는 되돌릴 수 없습니다
30분
@@ -247,13 +247,13 @@
개발 지원
대단히 감사합니다!
테마가 제대로 적용되지 않으면, 앱을 수동으로 재시작해주세요
- 고정
- 고정됨
- 고정해제
- 아직 고정된 저장소가 없으므로 여기에서 볼 수 있도록 고정하세요.\nP.S: 많이 액세스할수록 저장소는 위에 배치될 것입니다.
+ 북마크
+ 바로가기
+ 바로가기 삭제
+ 아직 바로가기 저장소가 없으므로 여기에서 볼 수 있도록 등록하세요.\nP.S: 많이 액세스할수록 저장소는 위에 배치될 것입니다.
네
아니요
- Feeds 없음
+ 소식 없음
Gists 없음
덧글 없음
알림 없음
@@ -279,12 +279,12 @@
변경사항
클릭하여 알림 목록을 열거나 옆으로 밀어 닫으세요
길게 누르면 어디서나 기본 화면으로 이동합니다
- 생성됨
- 담당됨
- 언급됨
+ 생성
+ 담당
+ 언급
이름
색상
- 꼬리표 샹성
+ 꼬리표 생성
조직
조직
구성원
@@ -293,7 +293,7 @@
구성원 없음
팀 없음
조직 없음
- 너의 조직을 찾을 수 없습니까?
+ 조직을 찾을 수 없습니까?
읽음으로 표시
효과
팝업 효과를 활성화합니다
@@ -321,7 +321,7 @@
반응 없음
반응
줄 바꿈
- 기본적으로 코드 뷰어에서 코드 줄 바꿈
+ 기본적으로 코드 뷰어에서 코드 줄 바꿈을 실행합니다
코드 줄 바꿈
오픈소스 라이브러리
알림음을 활성화합니다
@@ -330,15 +330,15 @@
개인 토큰으로 로그인
개인 토큰
기본 인증으로 로그인
- 실제로 조직에 속해 있고 여기에서 볼 수 없다면 다음 링크를 따르십시오.
+ 조직에 속해 있고 여기에서 볼 수 없다면 다음 링크를 따르세요.
\nhttps://help.github.com/articles/about-third-party-application-restrictions\nPS: 조직에 FastHub 엑세스 권한을 부여하고 액세스 토큰을 사용해서 로그인할 수 있습니다.\n또한 https://github.com/settings/applications에서 FastHub를 찾아 조직 액세스로 스크롤 한 다음 승인 버튼을 클릭하세요.
삽입
선택
사진 선택
- $2.00 지원
- $5.00 지원
- $10.00 지원
- $20.00 지원
+ $2.00 기부
+ $5.00 기부
+ $10.00 기부
+ $20.00 기부
언어
언어
언어 선택
@@ -347,7 +347,7 @@
from
in
구독
- 이 저장소에서 이슈가 사용 중지되었습니다
+ 이슈가 비활성화된 저장소입니다
엑세스 토큰
기본 인증
로그인 종류 선택
@@ -381,10 +381,10 @@
배너 선택
이미지 불러오기 오류, 다시 시도하세요
급상승
- GitHub 제한 때문에 emojies로 정렬이 실제로 작동하지 않습니다
+ GitHub 제한 때문에 emojies로 정렬이 작동하지 않습니다
위로 스크롤
아래로 스크롤
- 참여됨
+ 참여
모두 선택됨
모두 선택 취소됨
구분선
@@ -398,9 +398,9 @@
담당자 추가 성공
검토자 추가 성공
이정표 추가 성공
- Feed
+ 소식
프리미엄 테마
- 코드 색상 표
+ 문법 강조기 테마
GitHub API의 모든 항목에 액세스하려면 GitHub 계정에 로그인하십시오.
그렇지 않으면 GitHub API로 전송 된 토큰은 엔터프라이즈 계정에서 전송되기 때문에 Enterprise GitHub가 아닌
다른 항목에 액세스 할 때마다 강제 로그아웃 될 수 있습니다.
@@ -427,4 +427,41 @@
앱 애니메이션
모든 앱 애니메이션을 비활성화합니다
이 풀 리퀘스트는 현재 병합될 수 없습니다
+ 프로젝트
+ 프로젝트 없음
+ 카드 없음
+ %s %s가 추가함
+ 변경사항이 너무 많습니다. 브라우저로 보세요
+ 프로젝트
+ 꼭 읽어주세요
+ 자주하는 질문
+ 댓글을 성공적으로 추가하였습니다.
+
+ • 왜 Private 이나 Public으로 되어진 제 Organizations 을 볼 수 없나요?
+ Open up https://github.com/settings/applications 를 열고 Fasthub라는 것을 찾아보세요. 그것을 선택한 후, Organization Access 부분에서 Grant를 눌러주세요. 또는 초기 로그인시 엑세스 토큰 으로 로그인하면 편리합니다.
+ • Access Token 과 OTP 으로 로그인을 시도했는데 로그인이 되질 않아요...
+ OTP는 일회용 코드입니다. 따리서 유효기간에 인해 엑세스 토큰 + OTP로 로그인이 불가합니다. 이렇게 로그인을 하게 되면 몇 초에 한 번씩 앱이 로그인을 하라고 합니다. 다른 방법을 사용해 주세요.
+ • 제 비공개 리포지토리와 엔터프라이즈 위키가 보이질 않아요!
+ 위 두 정보를 스크랩하려면 특별한 세션 토큰이 필요한데 본 앱을 이 토큰을 받을 수 없습니다. 깃허브 API의 한계이므로 API문의는 깃허브에게 해주세요. 본 앱의 개발자는 API문의를 받지 않습니다.
+ • 제가 엔터프라이즈 계정으로 로그인했는데 Enterprise GitHub 외의 다른 것들은 볼 수 없나요?
+ 결론부터 말하자면 불가능합니다. 본 앱은 여러분이 다른 것들을 볼 수 있게 해 주고 있지만, 기술적 한계로 크게 도움을 드릴 수는 없습니다. 대부분의 경우에는 여러분의 로그인 계정이 깃허브 서버에 상주하고 있기 때문입니다. 그러나 소수/b>의 경우에는 여러분의 OAuth 토큰이 다른 활동을 하게 해 줄 수 있습니다.
+ • Issues/PR 를 수정 하는데 문제가 있습니다.
+ 공개 Organization 리포지토리를 수정하면, 여러분의 Organization에게 문의하여 Fasthub 권환을 받거나 최초 로그인시 엑세스 토큰으로 로그인하세요.
+ • 앱을 사용하는데 문제를 발견했어요! / 혹시 이러한 기능을 추가해주실 수 있나요?
+ 그럼요! https://github.com/k0shk0sh/FastHub/issues/new 로 간 후 새로운 티켓을 만드세요. 티켓을 만들기 전 먼저 전에 있었던 티켓 중 중복되는 것이 없는지 검색해 주세요. 중복시 티켓은 바로 닫힙니다.
+ • PROMO CODE는 어떻게 받나요?
+ 학생인 경우:
+ 자신의 대학(학교) 학생증 & 신분증 복사본 (이름 및 얼굴 대조를 위한 것 입니다. 관련 서류는 인증 후 폐기됩니다.)
+ 자신의 대학(학교)의 시작일 및 졸업일
+ 플레이 스토어에서 FastHub 별점 매기기
+
+
+ 위 조건을 만족시킨 후 본 앱의 개발자인 Kosh Sergani에게 이메일을 보내면 검토후 PROMO CODE를 보내 드립니다.
\n
+ 본 한국어 번역자는 이 앱의 개발에 참여하지 않았습니다. 따라서 기술적 문의는 이 앱의 개발자인 Kosh Sergani에게 해주시기 바랍니다. 본 번역이 앱 사용에 도움이 되셨기를 바랍니다. 감사합니다.
+ ]]>
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 25e1ed31..d21fc1b2 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -1,7 +1,7 @@
-
+
Carregando, por favor aguarde…
Ação
- Configuraçoes
+ Configurações
Descartar
Cancelar
OK
@@ -429,4 +429,14 @@
Ver como código
Animações no App
Desabilitar todas as animações no App.
+ Por favor envie seu pedido em inglês.
+ Abrir em nova janela
+ Projetos
+ Projeto
+ Adicionado por %s %s
+ Sem Projetos
+ Sem Cartões
+ Muitas diferenças para exibir. Por favor, veja-os via navegador
+ Não encontra suas Organizações\?
+ Este PR não pode ser merged agora.
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 6a60ab82..51018d78 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -434,5 +434,7 @@
Проекты
Проекты отсутствуют
Карточки отсутствуют
- Добавлено %s
+ Добавлено %s %s
+ Слишком много изменений для отображения. Посмотрите их, пожалуйста, в браузере
+ Проект
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index c02878d0..fe2491de 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -1,13 +1,33 @@
-
+
+
+
- 加载中, 请稍候…
+ 加载中, 请稍候...
动作
设置
+ 丢弃
取消
确定
无可用数据
搜索
- 请登录以继续使用FastHub
+ 请登录以使用 FastHub
登录你的 GitHub 账户来使用 FastHub
无法登录
登录
@@ -26,30 +46,40 @@
取消关注
用户
详细信息
- 检测到存档文件,请下载文件查看其内容。
- 最少字数(3)
+ 检测到存档文件, 请下载以查看内容.
+ 最少字数 (3)
未找到文件
- 没有简介
- 下载中…
- 下载文件中…
+ 没有 Readme
+ 下载中...
+ 下载文件中...
发布于
+ 草稿
发布
没有内容
贡献者
+ 贡献
由
- 关闭缺陷跟踪
- 重新打开缺陷跟踪
+ 请使用英语描述您的问题.
+ 关闭 Issue
+ 重新打开 Issue
重新打开
关闭
已重新打开
- 锁定意味着:\n·其他用户无法为此缺陷跟踪添加新评论。\n·您和其他有权访问此仓库的协作者仍然可以留下所有人能看到的评论。\n·您可以随时解锁此缺陷跟踪 。
- 解锁意味着:\n·每个人都可以再次对这个缺陷跟踪发表评论。\n·您可以随时再次锁定此缺陷跟踪。\n
+ 锁定意味着:\n
+ ·其他用户无法为此 Issue 添加新评论;\n
+ ·您和其他有写入权限的协作者仍然可以留下所有人都能看到的评论;\n
+ ·您可以随时解锁此 Issue;\n
+
+ 解锁意味着:\n
+ ·每个人都可以对这个 Issue 发表评论;\n
+ ·您可以随时再次锁定此 Issue.\n
+
锁定
解锁
- 关闭缺陷跟踪出错,请稍后再试。
- 重新打开缺陷跟踪出错, 请稍后再试。
- 已关闭缺陷跟踪
- 没有提供描述
+ 关闭 Issue 时发生错误, 请稍后再试.
+ 重新打开 Issue 时发生错误, 请稍后再试.
+ Issue 已关闭
+ 无描述
一级标题
二级标题
三级标题
@@ -66,131 +96,146 @@
添加
变化
状态
- 确定吗?
+ 确定?
成功
至
- 删除评论出错
+ 删除评论时发生错误
删除
评论
评论
已合并
- 文件
文件菜单
文件
下载
返回
上级文件夹
代码查看器
- 用浏览器打开
+ 在浏览器中打开
大文件
- 该文件太大,无法打开。\n触摸“确定”下载。
+ 该文件太大, 无法在线预览.\n点按"确定"下载.
观众
提交
在此输入内容
描述
文件名
带扩展名的文件名
- 私有代码片段
- 公共代码片段
+ 私有 Gist
+ 公开 Gist
提交为
删除
- 删除代码片段出错
+ 删除 Gist 时发生错误
没有文件
- 必填项目
+ 必填
已提交
- 创建代码片段
+ 创建 Gist
清除
用户
标题
+ 文件
里程碑
分配给自己
- 提交缺陷跟踪
- 创建缺陷跟踪出错
- 创建缺陷跟踪
- 请取消选择以继续编辑。
+ 提交 Issue
+ 创建 Issue 时发生错误
+ 创建 Issue
+ 请取消选择以继续编辑.
通知
你有未读的通知
- 打开的
+ 打开
+ 在新窗口中打开
通知类型
标签
没有标签
- 已添加标签
+ 标签已添加
提交反馈
注销
- 感谢您的反馈 !
+ 感谢您的反馈!
当前版本
版本
支持开发, 启用广告
用户名
密码
- 双因素认证码
- 登陆
- 代码片段描述
- 触摸头像打开用户的个人资料
- 长按一个分支打开上游或该分支仓库。
- 下载此版本
+ 两步验证
+ 登录
+ Gist 描述
+ 点按头像打开用户的个人资料
+ 长按 Fork 消息以打开上游仓库或此 Fork 的仓库.
+ 下载
选项
下载或分享
- 收藏/取消收藏 此仓库
- 关注
- 关注/取消关注 此仓库
- 添加书签可以从导航栏快速访问它们。
- 找不到URL
+ 点按评论以标记其作者或编辑你的评论.\n长按你的评论以删除.
+ Star/取消 Star 此仓库
+ Watch
+ Watch/取消 Watch 此仓库
+ 加入书签以从导航栏快速访问.
+ 忽略全部
+ 找不到网址
最后更新
预览
预览
- 预览Markdown文本显示效果\n\n左右滑动Markdown编辑器图标以获取更多选项。
+ 切换 Markdown 语法高亮. \n滑动 Markdown 编辑器图标可查看更多选项.
创建日期
- 文件创建时间
- 最后更新时间
+ 文件的创建时间
+ 文件的最后更新时间
全部标记为已读
所有通知
未读
全部
删除仓库
- 删除此仓库后不能撤销
+ 删除操作无法撤销
30 分钟
20 分钟
10 分钟
5 分钟
1 分钟
+ 1 小时
+ 2 小时
+ 3 小时
已创建
已提交
已下载
已关注
- 缺陷跟踪 的评论
+ 评论 Issue
成员
- 拉取请求 的评论
- 推送给
- 小组
+ 评论 Pull Request
+ Push 到
+ 团队
已删除
未知
- 提交记录 的评论
+ 评论 Commit
切换分支
- 受理人
+ 受理者
修改
- 更新缺陷跟踪
- 更新拉取请求
+ \u2022 已修改
+ 更新 Issue
+ 更新 Pull Request
没有里程碑
添加
完成
主页
- 创建里程碑
- 创建里程碑出错
+ 建立里程碑
+ 建立里程碑时发生错误
截止于
- 没有受理人
+ 没有受理者
此
- 提交记录已切换到所选分支
+ Commit 已被切换到所选分支
一般
- 更改FastHub检查通知的频率
+ 更改 FastHub 检查通知的频率
通知同步间隔
所有
行为
+ 自定义
启用列表动画
列表动画
- 禁用确认退出的Toast
+ 禁用用于防止意外退出的提示框
返回键直接退出
- 任何未保存的更改都将被丢弃
+ 恢复
+ 备份
+ 备份成功!
+ 选择要还原的备份
+ 没有权限.
+ 最后更新: %s
+ 现在
+ 丢弃所有未保存的更改?
私人
使用圆角矩形头像代替圆形头像
矩形头像
@@ -201,63 +246,65 @@
关于 FastHub 的问题
反馈
问题反馈
- 有问题吗? 在这里报告
+ 遇到问题? 在此反馈
关于
通知
- 关掉
+ 关闭
未登录
- 需要两个认证因素
- 没有缺陷跟踪
+ 需要两步验证
+ 没有 Issue
复制
已复制
提交说明
与服务器通信时发生错误
- 请求API时出现意外错误
- 请求服务器发生错误,请稍后再试
+ 调用 API 时出现意外错误
+ 请求服务器发生错误, 请稍后再试
将通知标记为已读
- 派生此代码片段
- 使用默认的浏览器登陆 (OAuth)
+ Fork 此 Gist
+ 使用默认浏览器登录 (OAuth)
或
- 关闭通知读取状态
- 点击通知后,禁止标记为已读。
+ 禁用通知已读状态
+ 点击通知后不再自动标记为已读.
选择主题
- 选择默认使用的主题
+ 选择默认主题
+ 选择主题强调色
+ 主题强调色
网站
支持开发
- 非常感谢!
- 如果主题没有正常显示,请重新打开此应用。
+ 非常感谢!
+ 如果主题没有正常显示, 请手动重启此应用.
书签
书签
删除书签
- 空空如也,给仓库添加书签就可以在这里查看了
- 确定
- 没有
+ 没有书签. \n注: 书签按访问频率降序排列.
+ 是
+ 否
没有动态
- 没有代码片段
+ 没有 Gist
没有评论
没有通知
- 没有被人关注
+ 没有关注者
没有关注的人
没有仓库
- 没有已收藏的仓库
- 没有提交记录
+ 没有已 Star 的仓库
+ 没有 Commit
没有贡献者
- 没有发布版本
- 没有关闭的缺陷跟踪
- 没有打开的缺陷跟踪
+ 没有 Releases
+ 没有关闭的 Issue
+ 没有打开的 Issue
没有事件
- 没有打开的拉取请求
- 没有关闭的拉取请求
+ 没有打开的 Pull Request
+ 没有关闭的 Pull Request
没有搜索结果
- 请接受权限请求,以便 FastHub 将文件存储在设备上
- 公共代码片段
+ 请接受权限请求, 允许 FastHub 在设备上储存文件
+ 公共 Gist
开启广告
- 没有缺陷跟踪
- 没有未读的通知。
- 我的代码片段
+ 没有 Issue
+ 没有未读通知
+ 我的 Gist
更新日志
- 点击打开通知列表或滑动以关闭
- 长按可从任何地方导航到主屏幕
+ 点按通知列表以查看或滑动以忽略
+ 长按可从任何地方跳转到主屏幕
已创建
被分配
提到我
@@ -267,11 +314,12 @@
组织
组织
成员
- 小组
+ 团队
成员
没有成员
- 没有小组
+ 没有团队
没有加入组织
+ 找不到你所属的组织?
标记为已读
动画
启用弹出动画
@@ -283,112 +331,135 @@
所有检查通过
排序
最新的
- 最老的
+ 最旧的
最多评论
最少评论
最近更新
- 最早更新
+ 最久没更新
+ 已是最新版本!
+ 有新版本可用
搜索内容不能为空
- 长按创建缺陷跟踪
- 该拉取请求可以合并
- 审核
+ 长按创建 Issue
+ 该 Pull Request 可以被合并
+ 已审核
驳回
- 批准
- 没有反映
- 反映
+ 认同这些更改
+ 没有反馈
+ 反馈
自动换行
在代码查看器中默认自动换行
自动换行
- 软件许可
- 收到通知后,发出声音。
- 开启通知声音
- 用个人令牌登录
- 个人令牌
- 使用基本认证登录
- 如果你实际是某个组织的成员,但你找不到组织,请访问下面的链接。\nhttps://help.github.com/articles/about-oauth-app-access-restrictions\nPS:您可以使用访问令牌登录,这将授予FastHub权限以查看您的组织列表。
+ 开源许可
+ 收到通知时发出声音.
+ 启用通知声音
+ 启用通知
+ 用 Personal Token 登录
+ Personal Token
+ 使用 Basic Auth 登录
+
+ 如果你实际是某个组织的成员, 但在此没有显示, 请访问下面的链接:\n
+ https://help.github.com/articles/about-oauth-app-access-restrictions\n
+ 注: 您可以使用 Access token 登录, 这将授予 FastHub 查看您的组织列表的权限.
+
插入
选择
选择图片
- 支持 2.00$
- 支持 5.00$
- 支持 10.00$
- 支持 20.00$
+ 捐赠 $2.00
+ 捐赠 $5.00
+ 捐赠 $10.00
+ 捐赠 $20.00$
语言
- 选择语言
- 选择您喜欢的语言
- 取消订阅
- 访问令牌
- 添加评论
- 应用
- 已分配
- 已备份!
- 备份
- 最后更新:%s
- 简介横幅
- 基本认证
- 选择登录方式
- 贡献
- 自定义
- 丢弃
- 跳过全部
- 选择横幅
- • 已修改
- 启用通知
- 始终签名
- 每次选择是否签名
- 在文本编辑器下每次选择是否签名后发送
- 始终签名后发送
- 无法加载图像。
- 动态
- 文件
- 过滤器
- 从
- 加载图片时出错,请重试。
- 于
- 加入 Slack
- 你想加入 FastHub Slack 群组吗?
语言
- 已添加里程碑
- 有新版本可用。
- 没有审核者
- 没有趋势
- 找不到用户
- 由于 GitHub 的限制,按 Emoji 排序功能异常
- 现在
- 1 小时
- 已加入
- 路径
- 未授予许可。
- 付费主题
- 回复
- 这个仓库关闭了缺陷跟踪
- 重置
- 恢复
- 审核请求
- 审核者已添加
- 审核者
- 向下滚动
- 向上滑动
- 选择要还原的备份
- 使用 %2$s%3$s 从我的 %1$s 发送
- 排序方式
+ 选择语言
+ 选择您希望 FastHub 使用的语言
+ 取消订阅
+ 从
+ 于
订阅
+ 这个仓库禁用了 Issue
+ Access Token
+ Basic Authentication
+ 选择登录方式
+ 文件
+ 路径
+ 审核请求
+ 加入 Slack
+ 你想加入 FastHub Slack 群组吗?
已邀请
- 标签
- 选择强调色调
- 主题强调色
- 3 小时
- 已勾选的复选框
- 未勾选的复选框
- 趋势
- 2 小时
- 类型
- 已经是最新版本!
+ 回复
+ 无法加载图像.
想要合并
关注者
- 触摸评论来标记其作者或编辑您的评论。\n\n长按你的评论删除它。
- 分割线
- 使用FastHub 2.5.0,您现在可以更好地为自己的个人资料页面展示横幅。\n\n任何使用FastHub应用的人都会看到您的标题,您也可以看到其他人的标题! 如果你想为自己创建一个横幅,使它成为1280x384或其它等比大小,否则可能会被裁剪。\n\n您可以随时添加或更改您的横幅,通过创建一个描述为“header.fst”的 Gist,其中包含一个有横幅链接的文件。\n\n或者更简单,只需使用内置的图像选择器!
- 草案
- 选择代码主题
+ 审核者
+ 没有审核者
+ 使用 %2$s%3$s 从我的 %1$s 发送
+ 始终签名
+ 始终签名后发送
+ 每次选择是否签名
+ 在编辑器下选择每次是否签名
+ 标签
+ 添加评论
+ 简介横幅
+
+ 从 FastHub 2.5.0 开始, 您可以在个人页面使用横幅来更好的展示自己. \n\n
+ 任何使用 FastHub 应用的人都会看到您的横幅, 您也可以看到其他人的横幅!
+ 如果您想为自己创建一个横幅, 确保其分辨率为 1280x384 或其他等比例大小, 否则显示时可能会被裁剪.\n\n
+ 您可以按照以下步骤随意设置或更改您的横幅: 创建一个描述为 "header.fst", 内容为横幅图片的直链链接的 Gist.\n\n
+ 或还可以更简单, 直接使用内置的图片选择器!
+
+ 选择横幅
+ 加载图片时出错, 请重试.
+ 趋势
+ 由于 GitHub 的限制, 无法正常按 Emoji 排序
+ 向上滑动
+ 向下滑动
+ 已加入
+ 找不到用户
+ 没有趋势
+ 重置
+ 应用
+ 过滤器
+ 类型
+ 排序方式
+ 已分配
+ 审核者已添加
+ 已添加里程碑
+ 动态
+ 付费主题
+ 选择代码高亮主题
+
+ 请登录你的 Github 帐号以使用 Github API 提供的功能,
+ 否则你将只能使用 Enterprise Github 的功能,
+ 因为 Github API 使用的 Token 是来自于你的 Enterprise 帐号的.
+
+ 添加帐号
+ 选择帐号
+ 不匹配
+ 警告
+ 所有者
+ 楼主
+ 取消审核
+ 导航栏不再随主题颜色
+ 导航栏默认样式
+ 选择自定义通知提示音
+ 选择通知提示音
+ 禁止自动播放 GIF
+ 禁止播放 GIF
+ 需要更改
+ Google Play 服务不可用
+ 编辑 Gist
+ 内容
+ 展开
+ 复制 SHA-1
+ 作为代码查看
+ 禁用 APP 内动画
+ 关闭所有 APP 内动画.
+ 此 Pull Request 目前无法合并.
+ 项目
+ 无项目
+ 无卡片
+ %s 于 %s 新增
+ 更改内容过多. 请在浏览器中查看.
+ 项目
+ 常见问题
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index a301817c..9a927f25 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -426,14 +426,14 @@ APP
選擇通知提示聲
停用 GIF 自動播放
停用 GIF 自動播放
- 請用英文來提交你的請求
- 請登入到GitHub帳號以使用所有的api功能。否則透過企業帳號向GitHub api發送token將會被拒絕。
+ 請用英文來提交你的請求
+ 請登入到 GitHub 帳號以使用所有的 api 功能。否則透過企業帳號向 GitHub api 發送 token 將會被拒絕。
不匹配
原發布者
取消 Reviews
請求更改
Google Play 服務不可用
- 編輯Gist
+ 編輯 Gist
內容
展開
複製 SHA-1
@@ -441,9 +441,54 @@ APP
停用 App 內部動畫
在每個地方停用 App 內部動畫
這個 PR 目前沒辦法被 merge
- " 專案"
+ 專案
沒有專案
沒有卡
由 %s 新增
找不到您的組織嗎?
+ 在新視窗中開啟
+ 差異太多無法顯示。請於瀏覽器檢視它們
+ 專案
+ FAQ
+
+ • 為何我無法看到我的 組織 的 私人 / 公開 的東西?
+ 打開 https://github.com/settings/applications 並找到 FastHub, 點擊它並滑到 Organization access 並點擊 Grant 按鈕,
+ 或是透過 Access Token 登入將不需要做這件事
+
+ • 我試著透過 Access Token & OTP 登入,但沒作用?
+ 你不能透過 Access Token & OTP 登入,因為 OTP code 的時限緣故, 你會需要在幾秒鐘內登入。
+
+ • 為何我的私人&企業的 Repo Wiki 無法顯示?
+ 因為 FastHub 在擷取 GitHub Wiki & 私人 Repos 時需要 session token ,但 FastHub 沒有。
+
+ • 使用企業帳號時無法與任何其他非我的企業的東西互動?
\n
+ 邏輯上來說,你無法存取任何東西非你的企業的東西,不過 FastHub 讓它能成功存取,但沒辦法再做到更多了。
+ 在大部分的情況下你的登入認證不存在於 GitHub 的伺服器上。
+ 但在少數情形之下你的 GitHub 帳號的 Oauth token 能達成這個技巧。
+
+ • 我在編輯 Issues/PRs 遇到困難?
+ 如果你正在編輯一個公開組職的 Repo ,請聯絡你的組織以取得權限給 FastHub 或是使用 Access Token 來登入!
+
+ • 我被禁止使用 FastHub 了,為什麼?
+ 很簡單,因為想要某些功能而給了 FastHub 低評價卻從來不更新你的評價。
+ 所以為何你要繼續使用呢?
+
+ • 我有困難/我想要有新功能!!
+ 前往 https://github.com/k0shk0sh/FastHub/issues/new 並創建一個 bug/feature requests 的 issue。
+ 我非常鼓勵你在發布某些東西前先搜尋有沒有重複的,否則會被馬上關閉。
+
+ 給 FastHub 開發者們的訊息\n
+
+ 開發者們你好,
\n
+
+ 你應該知道 FastHub 是一個成長很快開源的 App & 是因為許多來自社群的幫助以及你們。
+ \n因為想要回報 issue 或是 request 而在 Play商店 給 FastHub 低評價將會被忽略。
+ \n作為一個開發者我想我們應該知道開發一個像是 FastHub 要付出多大的努力。
+ \n有些人不知道要在 FastHub 的 Repo 回報而是在 Play商店 給低評價並且認為這會強制開發者(我) 去實作及修復他們想要的東西,而他們會更新評價。
+ \n但是這並沒有發生過,他們不是不修改評價就是要求其他功能(如此典型)。
+ \n請於 FastHub repo 回報 Issue & Features,打開側欄功能表並點擊"回報 Issue",這將會被直接發布在 FastHub repo (我能夠幫助和協助你的地方)。
+
+ ]]>
+ 請閱讀!
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 12529a30..ee862b9c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -92,14 +92,14 @@
• All PRO Themes\n
• PR Reviews & On-line code comments (PRs & Commits)\n
• Commit from FastHub (Edit, Create & Delete files)\n
- • Support to other Merge methods (Rebase & Squash)\n
+ • Supports 2 more Merging methods (Rebase & Squash)\n
• Login to unlimited accounts\n
• Edit & Add unlimited Gist Files\n
• Project Columns & Cards (Edit, Create & Delete)
- • New upcoming PRO features
+ • New upcoming PRO features (Coming soon...)
]]>
Purchase
@@ -124,7 +124,7 @@
Unlock
Unlock Everything
Feeds
-
+
Loading, please wait…
Action
@@ -134,9 +134,9 @@
OK
No data available
Search
- Please sign in to continue using FastHub
+ Please sign in to begin using FastHub
Sign in using your GitHub account to use FastHub
- Failed to sign in
+ Sign in failed
Sign in
Share
Reload
@@ -154,7 +154,7 @@
User
Details
Archive file detected, please download the file to view its content.
- Minimum characters (3)
+ Minimum characters (2)
No file found
No readme found
Downloading…
@@ -230,7 +230,7 @@
No files
Required field
Successfully submitted
- Create Gist
+ Create a Gist
Clear
Users
Title
@@ -553,7 +553,7 @@
Disable auto playing GIFs
Disable Playing GIF
requested changes
- Google Play Service unavailable
+ Google Play Services are unavailable
Edit Gist
Content
expand
@@ -566,6 +566,46 @@
No Projects
No Cards
Added by %s %s
- Changes are large, please open in browser
+ Too many differences to display. Please, view them via browser
Project
+ PLEASE READ!
+
+ • Why can\'t I see my Organizations either Private or Public ones?
+ Open up https://github.com/settings/applications and look for FastHub, open it then scroll to Organization access and click on Grant Button,
+ alternatively login via Access Token which will ease this setup.
+
+ • I tried to login via Access Token & OTP but it does not work?
+ You can\'t login via Access Token & OTP all together due to the lifetime of the OTP code, you\'ll be required to login in every few seconds.
+
+ • Why are my Private Repo & Enterprise Wiki not showing up?
+ It\'s due to FastHub scraping GitHub Wiki page & Private Repos require session token that FastHub is unable to obtain.
+
+ • I login with Enterprise account but can\'t interact with anything other than my Enterprise GitHub?
+ Well, logically, you can\'t access anything else other than your Enterprise. FastHub tries to allow as much possible, but can\'t do much about it
+ in most cases, since your login credential doesn\'t exists in GitHub server. But in a few
+ cases your GitHub account Oauth token will do the trick.
+
+ • Why am I having problems editing Issues/PRs?
+ If you are editing a public Org repo, then please contact your Org to grant access to FastHub or use Access Token to login!.
+
+ • I\'m having this issue or I want this & that!!
+ Head to https://github.com/k0shk0sh/FastHub/issues/new and create new issue for bugs or feature requests, I really do encourage you to
+ search before opening a ticket. Any duplicate request will result in it being closed immediately.
+
+ • How do I get PROMO CODE?
+ If you are a student, you\'ll have to provide me via Email that you are student, you will need below documents:
+
+ - Your university identity card & your identity card (that shows your name & your face to compare it!)
+ - Your university start & end date
+ - Rate FastHub in the Play Store
+
+ If you aren\'t a student and you can\'t afford to pay for PRO, you\'ll need:
+
+ - Write an article about FastHub in social media such as (Medium)
+ - Rate FastHub in the Play Store
+
+ ]]>
+ FAQ
+ Comments added successfully
diff --git a/build.gradle b/build.gradle
index 0b0e929f..0b7bfcd6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
state_version = '1.1.0'
lombokVersion = '1.12.6'
supportVersion = "26.0.1"
- gms = "11.2.0"
+ gms = "11.4.0"
thirtyinchVersion = '0.8.0'
retrofit = '2.3.0'
junitVersion = '4.12'
@@ -13,7 +13,7 @@ buildscript {
assertjVersion = '2.5.0'
espresseVersion = '2.2.2'
requery = '1.3.2'
- kotlin_version = '1.1.4-2'
+ kotlin_version = '1.1.51'
commonmark = '0.9.0'
}
repositories {
@@ -22,7 +22,7 @@ buildscript {
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.0.0-beta5'
+ classpath 'com.android.tools.build:gradle:3.0.0-beta7'
classpath 'com.google.gms:google-services:3.0.0'
classpath 'com.novoda:gradle-build-properties-plugin:0.3'
classpath 'com.dicedmelon.gradle:jacoco-android:0.1.2'
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 15eb24ec..7d171b3b 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,5 +3,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.2-all.zip
android.enableD8=true
\ No newline at end of file
diff --git a/jobdispatcher/build.gradle b/jobdispatcher/build.gradle
deleted file mode 100644
index d326ddf2..00000000
--- a/jobdispatcher/build.gradle
+++ /dev/null
@@ -1,79 +0,0 @@
-apply plugin: "com.android.library"
-
-android {
- compileSdkVersion 26
- buildToolsVersion "26.0.1"
-
- defaultConfig {
- minSdkVersion 16
- targetSdkVersion 26
- versionCode 1
- versionName "0.8.0"
- testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
- }
-
- defaultPublishConfig "release"
- publishNonDefault true
-
- buildTypes {
- debug {
- testCoverageEnabled true
- }
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt')
- }
- }
-
- sourceSets {
- // A set of testing helpers that are shared across test types
- testLib { java.srcDir("src/main") }
- test { java.srcDir("src/testLib") } // Robolectric tests
- androidTest { java.srcDir("src/testLib") } // Android (e2e) tests
- }
-}
-
-dependencies {
- // The main library only depends on the Android support lib
- compile "com.android.support:support-v4:26.0.1"
-
- def junit = 'junit:junit:4.12'
- def robolectric = 'org.robolectric:robolectric:3.3.2'
-
- // The common test library uses JUnit
- testLibCompile junit
-
- // The unit tests are written using JUnit, Robolectric, and Mockito
- testCompile junit
- testCompile robolectric
- testCompile 'org.mockito:mockito-core:2.2.5'
-
- // The Android (e2e) tests are written using JUnit and the test support lib
- androidTestCompile junit
- androidTestCompile 'com.android.support.test:runner:0.5'
-}
-
-task javadocs(type: Javadoc) {
- description "Generate Javadocs"
- source = android.sourceSets.main.java.sourceFiles
- classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
- classpath += configurations.compile
- failOnError false
-}
-
-task javadocsJar(type: Jar, dependsOn: javadocs) {
- description "Package Javadocs into a jar"
- classifier = "javadoc"
- from javadocs.destinationDir
-}
-
-task sourcesJar(type: Jar) {
- description "Package sources into a jar"
- classifier = "sources"
- from android.sourceSets.main.java.sourceFiles
-}
-
-task aar(dependsOn: "assembleRelease") {
- group "artifact"
- description "Builds the library AARs"
-}
diff --git a/jobdispatcher/coverage.gradle b/jobdispatcher/coverage.gradle
deleted file mode 100644
index 204f2dc2..00000000
--- a/jobdispatcher/coverage.gradle
+++ /dev/null
@@ -1,40 +0,0 @@
-apply plugin: "jacoco"
-
-jacoco {
- // see https://github.com/jacoco/jacoco/pull/288 and the top build.gradle
- toolVersion "0.7.6.201602180812"
-}
-
-android {
- testOptions {
- unitTests.all {
- systemProperty "robolectric.logging.enabled", true
- systemProperty "robolectric.logging", "stdout"
-
- jacoco {
- includeNoLocationClasses = true
- }
- }
- }
-}
-
-// ignore these when generating coverage
-def ignoredPrefixes = ['R$', 'R.class', 'BuildConfig.class']
-
-task coverage(type: JacocoReport, dependsOn: ["testDebugUnitTest"]) {
- group = "Reports"
- description = "Generate a coverage report"
-
- classDirectories = fileTree(
- dir: "${project.buildDir}/intermediates/classes/debug/com/firebase/",
- exclude: { d -> ignoredPrefixes.any { p -> d.file.name.startsWith(p) } }
- )
- sourceDirectories = files(["src/main/java/com/firebase/"])
- executionData = files("${project.buildDir}/jacoco/testDebugUnitTest.exec")
-
- reports {
- xml.enabled = true
- html.enabled = true
- html.destination "${buildDir}/coverage_html"
- }
-}
diff --git a/jobdispatcher/src/androidTest/AndroidManifest.xml b/jobdispatcher/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index c87f27fc..00000000
--- a/jobdispatcher/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/jobdispatcher/src/androidTest/java/com/firebase/jobdispatcher/EndToEndTest.java b/jobdispatcher/src/androidTest/java/com/firebase/jobdispatcher/EndToEndTest.java
deleted file mode 100644
index 48b1048b..00000000
--- a/jobdispatcher/src/androidTest/java/com/firebase/jobdispatcher/EndToEndTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2017 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Basic end to end test for the JobDispatcher. Requires Google Play services be installed and
- * available.
- */
-@RunWith(AndroidJUnit4.class)
-public final class EndToEndTest {
- private Context appContext;
- private FirebaseJobDispatcher dispatcher;
-
- @Before public void setUp() {
- appContext = InstrumentationRegistry.getTargetContext();
- dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(appContext));
- TestJobService.reset();
- }
-
- @Test public void basicImmediateJob() throws InterruptedException {
- final CountDownLatch latch = new CountDownLatch(1);
- TestJobService.setProxy(new TestJobService.JobServiceProxy() {
- @Override
- public boolean onStartJob(JobParameters params) {
- latch.countDown();
- return false;
- }
-
- @Override
- public boolean onStopJob(JobParameters params) {
- return false;
- }
- });
-
- dispatcher.mustSchedule(
- dispatcher.newJobBuilder()
- .setService(TestJobService.class)
- .setTrigger(Trigger.NOW)
- .setTag("basic-immediate-job")
- .build());
-
- assertTrue("Latch wasn't counted down as expected", latch.await(120, TimeUnit.SECONDS));
- }
-}
diff --git a/jobdispatcher/src/main/AndroidManifest.xml b/jobdispatcher/src/main/AndroidManifest.xml
deleted file mode 100644
index 71205bd8..00000000
--- a/jobdispatcher/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/BundleProtocol.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/BundleProtocol.java
deleted file mode 100644
index 774a8340..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/BundleProtocol.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-final class BundleProtocol {
- static final String PACKED_PARAM_BUNDLE_PREFIX = "com.firebase.jobdispatcher.";
-
- // PACKED_PARAM values are only read on the client side, so as long as the
- // extraction process gets the same changes then it's fine.
- static final String PACKED_PARAM_CONSTRAINTS = "constraints";
- static final String PACKED_PARAM_LIFETIME = "persistent";
- static final String PACKED_PARAM_RECURRING = "recurring";
- static final String PACKED_PARAM_SERVICE = "service";
- static final String PACKED_PARAM_TAG = "tag";
- static final String PACKED_PARAM_EXTRAS = "extras";
- static final String PACKED_PARAM_TRIGGER_TYPE = "trigger_type";
- static final String PACKED_PARAM_TRIGGER_WINDOW_END = "window_end";
- static final String PACKED_PARAM_TRIGGER_WINDOW_START = "window_start";
- static final int TRIGGER_TYPE_EXECUTION_WINDOW = 1;
- static final int TRIGGER_TYPE_IMMEDIATE = 2;
- static final int TRIGGER_TYPE_CONTENT_URI = 3;
- static final String PACKED_PARAM_RETRY_STRATEGY_INITIAL_BACKOFF_SECONDS =
- "initial_backoff_seconds";
- static final String PACKED_PARAM_RETRY_STRATEGY_MAXIMUM_BACKOFF_SECONDS =
- "maximum_backoff_seconds";
- static final String PACKED_PARAM_RETRY_STRATEGY_POLICY = "retry_policy";
- static final String PACKED_PARAM_REPLACE_CURRENT = "replace_current";
- static final String PACKED_PARAM_CONTENT_URI_FLAGS_ARRAY = "content_uri_flags_array";
- static final String PACKED_PARAM_CONTENT_URI_ARRAY = "content_uri_array";
- static final String PACKED_PARAM_TRIGGERED_URIS = "triggered_uris";
- static final String PACKED_PARAM_OBSERVED_URI = "observed_uris";
-
- BundleProtocol() {
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Constraint.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Constraint.java
deleted file mode 100644
index ff413fb2..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Constraint.java
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.support.annotation.IntDef;
-import android.support.annotation.VisibleForTesting;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A Constraint is a runtime requirement for a job. A job only becomes eligible to run once its
- * trigger has been activated and all constraints are satisfied.
- */
-public final class Constraint {
- /**
- * Only run the job when an unmetered network is available.
- */
- public static final int ON_UNMETERED_NETWORK = 1;
-
- /**
- * Only run the job when a network connection is available. If both this and
- * {@link #ON_UNMETERED_NETWORK} is provided, {@link #ON_UNMETERED_NETWORK} will take
- * precedence.
- */
- public static final int ON_ANY_NETWORK = 1 << 1;
-
- /**
- * Only run the job when the device is currently charging.
- */
- public static final int DEVICE_CHARGING = 1 << 2;
-
- /**
- * Only run the job when the device is idle. This is ignored for devices that don't expose the
- * concept of an idle state.
- */
- public static final int DEVICE_IDLE = 1 << 3;
-
- @VisibleForTesting
- static final int[] ALL_CONSTRAINTS = {
- ON_ANY_NETWORK, ON_UNMETERED_NETWORK, DEVICE_CHARGING, DEVICE_IDLE};
-
- /** Constraint shouldn't ever be instantiated. */
- private Constraint() {}
-
- /**
- * A tooling type-hint for any of the valid constraint values.
- */
- @IntDef(flag = true, value = {
- ON_ANY_NETWORK,
- ON_UNMETERED_NETWORK,
- DEVICE_CHARGING,
- DEVICE_IDLE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface JobConstraint {}
-
- /**
- * Compact a provided array of constraints into a single int.
- *
- * @see #uncompact(int)
- */
- static int compact(@JobConstraint int[] constraints) {
- int result = 0;
- if (constraints == null) {
- return result;
- }
- for (int c : constraints) {
- result |= c;
- }
- return result;
- }
-
- /**
- * Unpack a single int into an array of constraints.
- *
- * @see #compact(int[])
- */
- static int[] uncompact(int compactConstraints) {
- int length = 0;
- for (int c : ALL_CONSTRAINTS) {
- length += (compactConstraints & c) == c ? 1 : 0;
- }
- int[] list = new int[length];
-
- int i = 0;
- for (int c : ALL_CONSTRAINTS) {
- if ((compactConstraints & c) == c) {
- list[i++] = c;
- }
- }
-
- return list;
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/DefaultJobValidator.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/DefaultJobValidator.java
deleted file mode 100644
index 4804804e..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/DefaultJobValidator.java
+++ /dev/null
@@ -1,288 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static com.firebase.jobdispatcher.RetryStrategy.RETRY_POLICY_EXPONENTIAL;
-import static com.firebase.jobdispatcher.RetryStrategy.RETRY_POLICY_LINEAR;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.support.annotation.CallSuper;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Validates Jobs according to some safe standards.
- *
- * Custom JobValidators should typically extend from this.
- */
-public class DefaultJobValidator implements JobValidator {
-
- /**
- * The maximum length of a tag, in characters (i.e. String.length()). Strings longer than this
- * will cause validation to fail.
- */
- public static final int MAX_TAG_LENGTH = 100;
-
- /**
- * The maximum size, in bytes, that the provided extras bundle can be. Corresponds to
- * {@link Parcel#dataSize()}.
- */
- public final static int MAX_EXTRAS_SIZE_BYTES = 10 * 1024;
-
- /** Private ref to the Context. Necessary to check that the manifest is configured correctly. */
- private final Context context;
-
- public DefaultJobValidator(Context context) {
- this.context = context;
- }
-
- /** @see {@link #MAX_EXTRAS_SIZE_BYTES}. */
- private static int measureBundleSize(Bundle extras) {
- Parcel p = Parcel.obtain();
- extras.writeToParcel(p, 0);
- int sizeInBytes = p.dataSize();
- p.recycle();
-
- return sizeInBytes;
- }
-
- /** Combines two {@literal Lists} together. */
- @Nullable
- private static List mergeErrorLists(@Nullable List errors,
- @Nullable List newErrors) {
- if (errors == null) {
- return newErrors;
- }
- if (newErrors == null) {
- return errors;
- }
-
- errors.addAll(newErrors);
- return errors;
- }
-
- @Nullable
- private static List addError(@Nullable List errors, String newError) {
- if (newError == null) {
- return errors;
- }
- if (errors == null) {
- return getMutableSingletonList(newError);
- }
-
- Collections.addAll(errors, newError);
-
- return errors;
- }
-
- @Nullable
- private static List addErrorsIf(boolean condition, List errors, String newErr) {
- if (condition) {
- return addError(errors, newErr);
- }
-
- return errors;
- }
-
- /**
- * Attempts to validate the provided {@code JobParameters}. If the JobParameters is valid, null will be
- * returned. If the JobParameters has errors, a list of those errors will be returned.
- */
- @Nullable
- @Override
- @CallSuper
- public List validate(JobParameters job) {
- List errors = null;
-
- errors = mergeErrorLists(errors, validate(job.getTrigger()));
- errors = mergeErrorLists(errors, validate(job.getRetryStrategy()));
-
- if (job.isRecurring() && job.getTrigger() == Trigger.NOW) {
- errors = addError(errors, "ImmediateTriggers can't be used with recurring jobs");
- }
-
- errors = mergeErrorLists(errors, validateForTransport(job.getExtras()));
- if (job.getLifetime() > Lifetime.UNTIL_NEXT_BOOT) {
- //noinspection ConstantConditions
- errors = mergeErrorLists(errors, validateForPersistence(job.getExtras()));
- }
-
- errors = mergeErrorLists(errors, validateTag(job.getTag()));
- errors = mergeErrorLists(errors, validateService(job.getService()));
-
- return errors;
- }
-
- /**
- * Attempts to validate the provided Trigger. If valid, null is returned. Otherwise a list of
- * errors will be returned.
- *
- * Note that a Trigger that passes validation here is not necessarily valid in all permutations
- * of a JobParameters. For example, an Immediate is never valid for a recurring job.
- * @param trigger
- */
- @Nullable
- @Override
- @CallSuper
- public List validate(JobTrigger trigger) {
- if (trigger != Trigger.NOW
- && !(trigger instanceof JobTrigger.ExecutionWindowTrigger)
- && !(trigger instanceof JobTrigger.ContentUriTrigger)) {
- return getMutableSingletonList("Unknown trigger provided");
- }
-
- return null;
- }
-
- /**
- * Attempts to validate the provided RetryStrategy. If valid, null is returned. Otherwise a list
- * of errors will be returned.
- */
- @Nullable
- @Override
- @CallSuper
- public List validate(RetryStrategy retryStrategy) {
- List errors = null;
-
- int policy = retryStrategy.getPolicy();
- int initial = retryStrategy.getInitialBackoff();
- int maximum = retryStrategy.getMaximumBackoff();
-
- errors = addErrorsIf(policy != RETRY_POLICY_EXPONENTIAL && policy != RETRY_POLICY_LINEAR,
- errors, "Unknown retry policy provided");
- errors = addErrorsIf(maximum < initial,
- errors, "Maximum backoff must be greater than or equal to initial backoff");
- errors = addErrorsIf(300 > maximum,
- errors, "Maximum backoff must be greater than 300s (5 minutes)");
- errors = addErrorsIf(initial < 30,
- errors, "Initial backoff must be at least 30s");
-
- return errors;
- }
-
- @Nullable
- private List validateForPersistence(Bundle extras) {
- List errors = null;
-
- if (extras != null) {
- // check the types to make sure they're persistable
- for (String k : extras.keySet()) {
- errors = addError(errors, validateExtrasType(extras, k));
- }
- }
-
- return errors;
- }
-
- @Nullable
- private List validateForTransport(Bundle extras) {
- if (extras == null) {
- return null;
- }
-
- int bundleSizeInBytes = measureBundleSize(extras);
- if (bundleSizeInBytes > MAX_EXTRAS_SIZE_BYTES) {
- return getMutableSingletonList(String.format(Locale.US,
- "Extras too large: %d bytes is > the max (%d bytes)",
- bundleSizeInBytes, MAX_EXTRAS_SIZE_BYTES));
- }
-
- return null;
- }
-
- @Nullable
- private String validateExtrasType(Bundle extras, String key) {
- Object o = extras.get(key);
-
- if (o == null
- || o instanceof Integer
- || o instanceof Long
- || o instanceof Double
- || o instanceof String
- || o instanceof Boolean) {
- return null;
- }
-
- return String.format(Locale.US,
- "Received value of type '%s' for key '%s', but only the"
- + " following extra parameter types are supported:"
- + " Integer, Long, Double, String, and Boolean",
- o == null ? null : o.getClass(), key);
- }
-
- private List validateService(String service) {
- if (service == null || service.isEmpty()) {
- return getMutableSingletonList("Service can't be empty");
- }
-
- if (context == null) {
- return getMutableSingletonList("Context is null, can't query PackageManager");
- }
-
- PackageManager pm = context.getPackageManager();
- if (pm == null) {
- return getMutableSingletonList("PackageManager is null, can't validate service");
- }
-
- final String msg = "Couldn't find a registered service with the name " + service
- + ". Is it declared in the manifest with the right intent-filter?";
-
- Intent executeIntent = new Intent(JobService.ACTION_EXECUTE);
- executeIntent.setClassName(context, service);
- List intentServices = pm.queryIntentServices(executeIntent, 0);
- if (intentServices == null || intentServices.isEmpty()) {
- return getMutableSingletonList(msg);
- }
-
- for (ResolveInfo info : intentServices) {
- if (info.serviceInfo != null && info.serviceInfo.enabled) {
- // found a match!
- return null;
- }
- }
-
- return getMutableSingletonList(msg);
- }
-
- private List validateTag(String tag) {
- if (tag == null) {
- return getMutableSingletonList("Tag can't be null");
- }
-
- if (tag.length() > MAX_TAG_LENGTH) {
- return getMutableSingletonList("Tag must be shorter than " + MAX_TAG_LENGTH);
- }
-
- return null;
- }
-
- @NonNull
- private static List getMutableSingletonList(String msg) {
- ArrayList strings = new ArrayList<>();
- strings.add(msg);
- return strings;
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Driver.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Driver.java
deleted file mode 100644
index fe832721..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Driver.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.support.annotation.NonNull;
-import com.firebase.jobdispatcher.FirebaseJobDispatcher.CancelResult;
-import com.firebase.jobdispatcher.FirebaseJobDispatcher.ScheduleResult;
-
-/**
- * Driver represents a component that understands how to schedule, validate, and execute jobs.
- */
-public interface Driver {
-
- /**
- * Schedules the provided Job.
- *
- * @return one of the SCHEDULE_RESULT_ constants
- */
- @ScheduleResult
- int schedule(@NonNull Job job);
-
- /**
- * Cancels the job with the provided tag and class.
- *
- * @return one of the CANCEL_RESULT_ constants.
- */
- @CancelResult
- int cancel(@NonNull String tag);
-
- /**
- * Cancels all jobs registered with this Driver.
- *
- * @return one of the CANCEL_RESULT_ constants.
- */
- @CancelResult
- int cancelAll();
-
- /**
- * Returns a JobValidator configured for this backend.
- */
- @NonNull
- JobValidator getValidator();
-
- /**
- * Indicates whether the backend is available.
- */
- boolean isAvailable();
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ExecutionDelegator.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ExecutionDelegator.java
deleted file mode 100644
index 1dbacb3c..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ExecutionDelegator.java
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static android.content.Context.BIND_AUTO_CREATE;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.support.annotation.NonNull;
-import android.support.annotation.VisibleForTesting;
-import android.support.v4.util.SimpleArrayMap;
-import android.util.Log;
-import com.firebase.jobdispatcher.JobService.JobResult;
-import java.lang.ref.WeakReference;
-
-/**
- * ExecutionDelegator tracks local Binder connections to client JobServices and handles
- * communication with those services.
- */
-/* package */ class ExecutionDelegator {
- @VisibleForTesting
- static final int JOB_FINISHED = 1;
-
- static final String TAG = "FJD.ExternalReceiver";
-
- interface JobFinishedCallback {
- void onJobFinished(@NonNull JobInvocation jobInvocation, @JobResult int result);
- }
-
- /**
- * A mapping of {@link JobInvocation} to (local) binder connections.
- * Synchronized by itself.
- */
- private final SimpleArrayMap serviceConnections =
- new SimpleArrayMap<>();
- private final ResponseHandler responseHandler =
- new ResponseHandler(Looper.getMainLooper(), new WeakReference<>(this));
- private final Context context;
- private final JobFinishedCallback jobFinishedCallback;
-
- ExecutionDelegator(Context context, JobFinishedCallback jobFinishedCallback) {
- this.context = context;
- this.jobFinishedCallback = jobFinishedCallback;
- }
-
- /**
- * Executes the provided {@code jobInvocation} by kicking off the creation of a new Binder
- * connection to the Service.
- *
- * @return true if the service was bound successfully.
- */
- boolean executeJob(JobInvocation jobInvocation) {
- if (jobInvocation == null) {
- return false;
- }
-
- JobServiceConnection conn = new JobServiceConnection(jobInvocation,
- responseHandler.obtainMessage(JOB_FINISHED));
-
- synchronized (serviceConnections) {
- JobServiceConnection oldConnection = serviceConnections.put(jobInvocation, conn);
- if (oldConnection != null) {
- Log.e(TAG, "Received execution request for already running job");
- }
- return context.bindService(createBindIntent(jobInvocation), conn, BIND_AUTO_CREATE);
- }
- }
-
- @NonNull
- private Intent createBindIntent(JobParameters jobParameters) {
- Intent execReq = new Intent(JobService.ACTION_EXECUTE);
- execReq.setClassName(context, jobParameters.getService());
- return execReq;
- }
-
- void stopJob(JobInvocation job) {
- synchronized (serviceConnections) {
- JobServiceConnection jobServiceConnection = serviceConnections.remove(job);
- if (jobServiceConnection != null) {
- jobServiceConnection.onStop();
- safeUnbindService(jobServiceConnection);
- }
- }
- }
-
- private void safeUnbindService(JobServiceConnection connection) {
- if (connection != null && connection.isBound()) {
- try {
- context.unbindService(connection);
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "Error unbinding service: " + e.getMessage());
- }
- }
- }
-
- private void onJobFinishedMessage(JobInvocation jobInvocation, int result) {
- synchronized (serviceConnections) {
- JobServiceConnection connection = serviceConnections.remove(jobInvocation);
- safeUnbindService(connection);
- }
-
- jobFinishedCallback.onJobFinished(jobInvocation, result);
- }
-
- private static class ResponseHandler extends Handler {
-
- /**
- * We hold a WeakReference to the ExecutionDelegator because it holds a reference to a
- * Service Context and Handlers are often kept in memory longer than you'd expect because
- * any pending Messages can maintain references to them.
- */
- private final WeakReference executionDelegatorReference;
-
- ResponseHandler(Looper looper, WeakReference executionDelegator) {
- super(looper);
- this.executionDelegatorReference = executionDelegator;
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case JOB_FINISHED:
- if (msg.obj instanceof JobInvocation) {
- ExecutionDelegator delegator = this.executionDelegatorReference.get();
- if (delegator == null) {
- Log.wtf(TAG, "handleMessage: service was unexpectedly GC'd"
- + ", can't send job result");
- return;
- }
-
- delegator.onJobFinishedMessage((JobInvocation) msg.obj, msg.arg1);
- return;
- }
-
- Log.wtf(TAG, "handleMessage: unknown obj returned");
- return;
-
- default:
- Log.wtf(TAG, "handleMessage: unknown message type received: " + msg.what);
- }
- }
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/FirebaseJobDispatcher.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/FirebaseJobDispatcher.java
deleted file mode 100644
index 1a76093f..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/FirebaseJobDispatcher.java
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import com.firebase.jobdispatcher.RetryStrategy.RetryPolicy;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * The FirebaseJobDispatcher provides a driver-agnostic API for scheduling and cancelling Jobs.
- *
- * @see #FirebaseJobDispatcher(Driver)
- * @see Driver
- * @see JobParameters
- */
-public final class FirebaseJobDispatcher {
- /**
- * Indicates the schedule request seems to have been successful.
- */
- public final static int SCHEDULE_RESULT_SUCCESS = 0;
-
- /**
- * Indicates the schedule request encountered an unknown error.
- */
- public final static int SCHEDULE_RESULT_UNKNOWN_ERROR = 1;
-
- /**
- * Indicates the schedule request failed because the driver was unavailable.
- */
- public final static int SCHEDULE_RESULT_NO_DRIVER_AVAILABLE = 2;
-
- /**
- * Indicates the schedule request failed because the Trigger was unsupported.
- */
- public final static int SCHEDULE_RESULT_UNSUPPORTED_TRIGGER = 3;
-
- /**
- * Indicates the schedule request failed because the service is not exposed or configured
- * correctly.
- */
- public final static int SCHEDULE_RESULT_BAD_SERVICE = 4;
-
- /**
- * Indicates the cancel request seems to have been successful.
- */
- public final static int CANCEL_RESULT_SUCCESS = 0;
- /**
- * Indicates the cancel request encountered an unknown error.
- */
- public final static int CANCEL_RESULT_UNKNOWN_ERROR = 1;
- /**
- * Indicates the cancel request failed because the driver was unavailable.
- */
- public final static int CANCEL_RESULT_NO_DRIVER_AVAILABLE = 2;
- /**
- * The backing Driver for this instance.
- */
- private final Driver mDriver;
- /**
- * The ValidationEnforcer configured for the current Driver.
- */
- private final ValidationEnforcer mValidator;
- /**
- * Single instance of a RetryStrategy.Builder, configured with the current driver's validation
- * settings. We can do this because the RetryStrategy.Builder is stateless.
- */
- private RetryStrategy.Builder mRetryStrategyBuilder;
-
- /**
- * Instantiates a new FirebaseJobDispatcher using the provided Driver.
- */
- public FirebaseJobDispatcher(Driver driver) {
- mDriver = driver;
- mValidator = new ValidationEnforcer(mDriver.getValidator());
- mRetryStrategyBuilder = new RetryStrategy.Builder(mValidator);
- }
-
- /**
- * Attempts to schedule the provided Job.
- *
- * Returns one of the SCHEDULE_RESULT_ constants.
- */
- @ScheduleResult
- public int schedule(@NonNull Job job) {
- if (!mDriver.isAvailable()) {
- return SCHEDULE_RESULT_NO_DRIVER_AVAILABLE;
- }
-
- return mDriver.schedule(job);
- }
-
- /**
- * Attempts to cancel the Job that matches the provided tag and endpoint.
- *
- * Returns one of the CANCEL_RESULT_ constants.
- */
- @CancelResult
- public int cancel(@NonNull String tag) {
- if (!mDriver.isAvailable()) {
- return CANCEL_RESULT_NO_DRIVER_AVAILABLE;
- }
-
- return mDriver.cancel(tag);
- }
-
- /**
- * Attempts to cancel all Jobs registered for this package.
- *
- * Returns one of the CANCEL_RESULT_ constants.
- */
- @CancelResult
- public int cancelAll() {
- if (!mDriver.isAvailable()) {
- return CANCEL_RESULT_NO_DRIVER_AVAILABLE;
- }
-
- return mDriver.cancelAll();
- }
-
- /**
- * Attempts to schedule the provided Job, throwing an exception if it fails.
- *
- * @throws ScheduleFailedException
- */
- public void mustSchedule(Job job) {
- if (schedule(job) != SCHEDULE_RESULT_SUCCESS) {
- throw new ScheduleFailedException();
- }
- }
-
- /**
- * Returns a ValidationEnforcer configured for the current Driver.
- */
- public ValidationEnforcer getValidator() {
- return mValidator;
- }
-
- /**
- * Creates a new Job.Builder, configured with the current driver's validation settings.
- */
- @NonNull
- public Job.Builder newJobBuilder() {
- return new Job.Builder(mValidator);
- }
-
- /**
- * Creates a new RetryStrategy from the provided parameters, validated with the current driver's
- * {@link JobValidator}.
- *
- * @param policy the backoff policy to use. One of the {@link RetryPolicy} constants.
- * @param initialBackoff the initial backoff, in seconds.
- * @param maximumBackoff the maximum backoff, in seconds.
- * @throws ValidationEnforcer.ValidationException
- * @see RetryStrategy
- */
- public RetryStrategy newRetryStrategy(@RetryPolicy int policy, int initialBackoff,
- int maximumBackoff) {
-
- return mRetryStrategyBuilder.build(policy, initialBackoff, maximumBackoff);
- }
-
- /**
- * Results that can legally be returned from {@link #schedule(Job)} calls.
- */
- @IntDef({
- SCHEDULE_RESULT_SUCCESS,
- SCHEDULE_RESULT_UNKNOWN_ERROR,
- SCHEDULE_RESULT_NO_DRIVER_AVAILABLE,
- SCHEDULE_RESULT_UNSUPPORTED_TRIGGER,
- SCHEDULE_RESULT_BAD_SERVICE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ScheduleResult {
- }
-
- /**
- * Results that can legally be returned from {@link #cancel(String)} or {@link #cancelAll()}
- * calls.
- */
- @IntDef({
- CANCEL_RESULT_SUCCESS,
- CANCEL_RESULT_UNKNOWN_ERROR,
- CANCEL_RESULT_NO_DRIVER_AVAILABLE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface CancelResult {
- }
-
- /**
- * Thrown when a {@link FirebaseJobDispatcher#schedule(com.firebase.jobdispatcher.Job)} call
- * fails.
- */
- public final static class ScheduleFailedException extends RuntimeException {
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayCallbackExtractor.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayCallbackExtractor.java
deleted file mode 100644
index 03fca073..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayCallbackExtractor.java
+++ /dev/null
@@ -1,247 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.annotation.SuppressLint;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.Nullable;
-import android.util.Log;
-import android.util.Pair;
-import java.util.ArrayList;
-
-/**
- * Responsible for extracting a JobCallback from a given Bundle.
- *
- *
Google Play services will send the Binder packed inside a simple strong Binder wrapper ({@link
- * #PENDING_CALLBACK_CLASS}) under the key {@link #BUNDLE_KEY_CALLBACK "callback"}.
- */
-/* package */ final class GooglePlayCallbackExtractor {
-
- private static final String TAG = GooglePlayReceiver.TAG;
- private static final String ERROR_NULL_CALLBACK = "No callback received, terminating";
- private static final String ERROR_INVALID_CALLBACK = "Bad callback received, terminating";
-
- /** The Parcelable class that wraps the Binder we need to access. */
- private static final String PENDING_CALLBACK_CLASS =
- "com.google.android.gms.gcm.PendingCallback";
- /** The key for the wrapped Binder. */
- private static final String BUNDLE_KEY_CALLBACK = "callback";
- /** A magic number that indicates the following bytes belong to a Bundle. */
- private static final int BUNDLE_MAGIC = 0x4C444E42;
- /** A magic number that indicates the following value is a Parcelable. */
- private static final int VAL_PARCELABLE = 4;
-
- // GuardedBy("GooglePlayCallbackExtractor.class")
- private static Boolean shouldReadKeysAsStringsCached = null;
-
- public Pair extractCallback(@Nullable Bundle data) {
- if (data == null) {
- Log.e(TAG, ERROR_NULL_CALLBACK);
- return null;
- }
-
- return extractWrappedBinderFromParcel(data);
- }
-
- /**
- * Bundles are written out in the following format:
- * A header, which consists of:
- *
- * - length (int)
- * - magic number ({@link #BUNDLE_MAGIC}) (int)
- * - number of entries (int)
- *
- *
- * Then the map values, each of which looks like this:
- *
- * - string key
- * - int type marker
- * - (any) parceled value
- *
- *
- * We're just going to iterate over the map looking for the right key (BUNDLE_KEY_CALLBACK)
- * and try and read the IBinder straight from the parcelled data. This is entirely dependent
- * on the implementation of Parcel, but these specific parts of Parcel / Bundle haven't
- * changed since 2008 and newer versions of Android will ship with newer versions of Google
- * Play services which embed the IBinder directly into the Bundle (no need to deal with the
- * Parcelable issues).
- */
- @Nullable
- @SuppressLint("ParcelClassLoader")
- private Pair extractWrappedBinderFromParcel(Bundle data) {
- Bundle cleanBundle = new Bundle();
- Parcel serialized = toParcel(data);
- JobCallback callback = null;
-
- try {
- int length = serialized.readInt();
- if (length <= 0) {
- // Empty Bundle
- Log.w(TAG, ERROR_NULL_CALLBACK);
- return null;
- }
-
- int magic = serialized.readInt();
- if (magic != BUNDLE_MAGIC) {
- // Not a Bundle
- Log.w(TAG, ERROR_NULL_CALLBACK);
- return null;
- }
-
- int numEntries = serialized.readInt();
- for (int i = 0; i < numEntries; i++) {
- String entryKey = readKey(serialized);
- if (entryKey == null) {
- continue;
- }
-
- if (!(callback == null && BUNDLE_KEY_CALLBACK.equals(entryKey))) {
- // If it's not the 'callback' key, we can just read it using the standard
- // mechanisms because we're not afraid of rogue BadParcelableExceptions.
- Object value = serialized.readValue(null /* class loader */);
- if (value instanceof String) {
- cleanBundle.putString(entryKey, (String) value);
- } else if (value instanceof Boolean) {
- cleanBundle.putBoolean(entryKey, (boolean) value);
- } else if (value instanceof Integer) {
- cleanBundle.putInt(entryKey, (int) value);
- } else if (value instanceof ArrayList) {
- // The only acceptable ArrayList in a Bundle is one that consists entirely
- // of Parcelables, so this cast is safe.
- @SuppressWarnings("unchecked") // safe by specification
- ArrayList arrayList = (ArrayList) value;
- cleanBundle.putParcelableArrayList(entryKey, arrayList);
- } else if (value instanceof Bundle) {
- cleanBundle.putBundle(entryKey, (Bundle) value);
- } else if (value instanceof Parcelable) {
- cleanBundle.putParcelable(entryKey, (Parcelable) value);
- }
-
- // Move to the next key
- continue;
- }
-
- int typeTag = serialized.readInt();
- if (typeTag != VAL_PARCELABLE) {
- // If the key is correct ("callback"), but it's not a Parcelable then something
- // went wrong and we should bail.
- Log.w(TAG, ERROR_INVALID_CALLBACK);
- return null;
- }
-
- String clsname = serialized.readString();
- if (!PENDING_CALLBACK_CLASS.equals(clsname)) {
- // If it's a Parcelable, but not one we recognize then we should not try and
- // unpack it.
- Log.w(TAG, ERROR_INVALID_CALLBACK);
- return null;
- }
-
- // Instead of trying to instantiate clsname, we'll just read its single member.
- IBinder remote = serialized.readStrongBinder();
- callback = new GooglePlayJobCallback(remote);
- }
-
- if (callback == null) {
- Log.w(TAG, ERROR_NULL_CALLBACK);
- return null;
- }
- return Pair.create(callback, cleanBundle);
- } finally {
- serialized.recycle();
- }
- }
-
- private static Parcel toParcel(Bundle data) {
- Parcel serialized = Parcel.obtain();
- data.writeToParcel(serialized, 0);
- serialized.setDataPosition(0);
- return serialized;
- }
-
- /**
- * Reads the next key (String) from the provided {@code serialized} Parcel.
- *
- * Naively using {@link Parcel#readString()} fails on versions of Android older than L,
- * whereas {@link Parcel#readValue(ClassLoader)} works on older versions but fails on anything L
- * or newer.
- */
- private String readKey(Parcel serialized) {
- if (shouldReadKeysAsStrings()) {
- return serialized.readString();
- }
-
- // Older platforms require readValue
- Object entryKeyObj = serialized.readValue(null /* Use the system ClassLoader */);
- if (!(entryKeyObj instanceof String)) {
- // Should never happen (Bundle keys are always Strings)
- Log.w(TAG, ERROR_INVALID_CALLBACK);
- return null;
- }
-
- return (String) entryKeyObj;
- }
-
- /**
- * Checks whether {@link Parcel#readString()} or {@link Parcel#readValue()} should be used to
- * access Bundle keys from a serialized Parcel. Commit {@link
- * https://android.googlesource.com/platform/frameworks/base/+/9c3e74f
- * I57bda9eb79ceaaa9c1b94ad49d9e462b52102149} (which only officially landed in Lollipop) changed
- * from using writeValue to writeString for Bundle keys. Some OEMs have pulled this change into
- * their KitKat fork, so we can't trust the SDK version check. Instead, we'll write a dummy
- * Bundle to a Parcel and figure it out using that.
- *
- * The check is cached because the result can't change during runtime.
- */
- private static synchronized boolean shouldReadKeysAsStrings() {
- // We're pretty sure that readString() should always be used on L+, but if we shortcircuit
- // this check then we have no evidence that this code is functioning correctly on KitKat
- // devices that have the corresponding writeString() change.
- if (shouldReadKeysAsStringsCached == null) {
- final String expectedKey = "key";
- Bundle testBundle = new Bundle();
- testBundle.putString(expectedKey, "value");
- Parcel testParcel = toParcel(testBundle);
- try {
- // length
- checkCondition(testParcel.readInt() > 0);
- // magic
- checkCondition(testParcel.readInt() == BUNDLE_MAGIC);
- // num entries
- checkCondition(testParcel.readInt() == 1);
-
- shouldReadKeysAsStringsCached = expectedKey.equals(testParcel.readString());
- } catch (RuntimeException e) {
- shouldReadKeysAsStringsCached = Boolean.FALSE;
- } finally {
- testParcel.recycle();
- }
- }
-
- return shouldReadKeysAsStringsCached;
- }
-
- /** Throws an {@code IllegalStateException} if {@code condition} is false. */
- private static void checkCondition(boolean condition) {
- if (!condition) {
- throw new IllegalStateException();
- }
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayDriver.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayDriver.java
deleted file mode 100644
index 58314821..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayDriver.java
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.support.annotation.NonNull;
-
-import com.firebase.jobdispatcher.FirebaseJobDispatcher.ScheduleResult;
-
-/**
- * GooglePlayDriver provides an implementation of Driver for devices with Google Play
- * services installed. This backend does not do any availability checks and any uses should be
- * guarded with a call to {@code GoogleApiAvailability#isGooglePlayServicesAvailable(android.content.Context)}
- *
- * @see
- * GoogleApiAvailability
- */
-public final class GooglePlayDriver implements Driver {
- static final String BACKEND_PACKAGE = "com.google.android.gms";
- private final static String ACTION_SCHEDULE = "com.google.android.gms.gcm.ACTION_SCHEDULE";
-
- private final static String BUNDLE_PARAM_SCHEDULER_ACTION = "scheduler_action";
- private final static String BUNDLE_PARAM_TAG = "tag";
- private final static String BUNDLE_PARAM_TOKEN = "app";
- private final static String BUNDLE_PARAM_COMPONENT = "component";
-
- private final static String SCHEDULER_ACTION_SCHEDULE_TASK = "SCHEDULE_TASK";
- private final static String SCHEDULER_ACTION_CANCEL_TASK = "CANCEL_TASK";
- private final static String SCHEDULER_ACTION_CANCEL_ALL = "CANCEL_ALL";
- private static final String INTENT_PARAM_SOURCE = "source";
- private static final String INTENT_PARAM_SOURCE_VERSION = "source_version";
-
- private static final int JOB_DISPATCHER_SOURCE_CODE = 1 << 3;
- private static final int JOB_DISPATCHER_SOURCE_VERSION_CODE = 1;
-
- private final JobValidator mValidator;
- /**
- * The application Context. Used to send broadcasts.
- */
- private final Context mContext;
- /**
- * A PendingIntent from this package. Passed inside the broadcast so the receiver can verify the
- * sender's package.
- */
- private final PendingIntent mToken;
- /**
- * Turns Jobs into Bundles.
- */
- private final GooglePlayJobWriter mWriter;
-
- /**
- * Instantiates a new GooglePlayDriver.
- */
- public GooglePlayDriver(Context context) {
- mContext = context;
- mToken = PendingIntent.getBroadcast(context, 0, new Intent(), 0);
- mWriter = new GooglePlayJobWriter();
- mValidator = new DefaultJobValidator(context);
- }
-
- @Override
- public boolean isAvailable() {
- ApplicationInfo applicationInfo = null;
- try {
- applicationInfo = mContext.getPackageManager().getApplicationInfo(BACKEND_PACKAGE, 0);
- } catch (PackageManager.NameNotFoundException e) {
- e.printStackTrace();
- }
- return applicationInfo != null && applicationInfo.enabled;
- }
-
-
- /**
- * Schedules the provided Job.
- */
- @Override
- @ScheduleResult
- public int schedule(@NonNull Job job) {
- mContext.sendBroadcast(createScheduleRequest(job));
-
- return FirebaseJobDispatcher.SCHEDULE_RESULT_SUCCESS;
- }
-
- @Override
- public int cancel(@NonNull String tag) {
- mContext.sendBroadcast(createCancelRequest(tag));
-
- return FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS;
- }
-
- @Override
- public int cancelAll() {
- mContext.sendBroadcast(createBatchCancelRequest());
-
- return FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS;
- }
-
- @NonNull
- protected Intent createCancelRequest(@NonNull String tag) {
- Intent cancelReq = createSchedulerIntent(SCHEDULER_ACTION_CANCEL_TASK);
- cancelReq.putExtra(BUNDLE_PARAM_TAG, tag);
- cancelReq.putExtra(BUNDLE_PARAM_COMPONENT, new ComponentName(mContext, getReceiverClass()));
- return cancelReq;
- }
-
- @NonNull
- protected Intent createBatchCancelRequest() {
- Intent cancelReq = createSchedulerIntent(SCHEDULER_ACTION_CANCEL_ALL);
- cancelReq.putExtra(BUNDLE_PARAM_COMPONENT, new ComponentName(mContext, getReceiverClass()));
- return cancelReq;
- }
-
- @NonNull
- protected Class getReceiverClass() {
- return GooglePlayReceiver.class;
- }
-
- @NonNull
- @Override
- public JobValidator getValidator() {
- return mValidator;
- }
-
- @NonNull
- private Intent createScheduleRequest(JobParameters job) {
- Intent scheduleReq = createSchedulerIntent(SCHEDULER_ACTION_SCHEDULE_TASK);
- scheduleReq.putExtras(mWriter.writeToBundle(job, scheduleReq.getExtras()));
- return scheduleReq;
- }
-
- @NonNull
- private Intent createSchedulerIntent(String schedulerAction) {
- Intent scheduleReq = new Intent(ACTION_SCHEDULE);
-
- scheduleReq.setPackage(BACKEND_PACKAGE);
- scheduleReq.putExtra(BUNDLE_PARAM_SCHEDULER_ACTION, schedulerAction);
- scheduleReq.putExtra(BUNDLE_PARAM_TOKEN, mToken);
- scheduleReq.putExtra(INTENT_PARAM_SOURCE, JOB_DISPATCHER_SOURCE_CODE);
- scheduleReq.putExtra(INTENT_PARAM_SOURCE_VERSION, JOB_DISPATCHER_SOURCE_VERSION_CODE);
-
- return scheduleReq;
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayJobCallback.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayJobCallback.java
deleted file mode 100644
index 7f47fe59..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayJobCallback.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.RemoteException;
-
-/**
- * Wraps the GooglePlay-specific callback class in a JobCallback-compatible interface.
- */
-/* package */ final class GooglePlayJobCallback implements JobCallback {
-
- private static final String DESCRIPTOR = "com.google.android.gms.gcm.INetworkTaskCallback";
- /** The only supported transaction ID. */
- private static final int TRANSACTION_TASK_FINISHED = IBinder.FIRST_CALL_TRANSACTION + 1;
-
- private final IBinder mRemote;
-
- public GooglePlayJobCallback(IBinder binder) {
- mRemote = binder;
- }
-
- @Override
- public void jobFinished(@JobService.JobResult int status) {
- Parcel request = Parcel.obtain();
- Parcel response = Parcel.obtain();
- try {
- request.writeInterfaceToken(DESCRIPTOR);
- request.writeInt(status);
-
- mRemote.transact(TRANSACTION_TASK_FINISHED, request, response, 0);
-
- response.readException();
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- } finally {
- request.recycle();
- response.recycle();
- }
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayJobWriter.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayJobWriter.java
deleted file mode 100644
index 0389f90f..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayJobWriter.java
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.annotation.IntDef;
-import android.support.annotation.VisibleForTesting;
-import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger;
-import com.firebase.jobdispatcher.RetryStrategy.RetryPolicy;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/* package */ final class GooglePlayJobWriter {
-
- static final String REQUEST_PARAM_UPDATE_CURRENT = "update_current";
- static final String REQUEST_PARAM_EXTRAS = "extras";
- static final String REQUEST_PARAM_PERSISTED = "persisted";
- static final String REQUEST_PARAM_REQUIRED_NETWORK = "requiredNetwork";
- static final String REQUEST_PARAM_REQUIRES_CHARGING = "requiresCharging";
- static final String REQUEST_PARAM_REQUIRES_IDLE = "requiresIdle";
- static final String REQUEST_PARAM_RETRY_STRATEGY = "retryStrategy";
- static final String REQUEST_PARAM_SERVICE = "service";
- static final String REQUEST_PARAM_TAG = "tag";
-
- static final String REQUEST_PARAM_RETRY_STRATEGY_INITIAL_BACKOFF_SECONDS =
- "initial_backoff_seconds";
- static final String REQUEST_PARAM_RETRY_STRATEGY_MAXIMUM_BACKOFF_SECONDS =
- "maximum_backoff_seconds";
- static final String REQUEST_PARAM_RETRY_STRATEGY_POLICY = "retry_policy";
-
- static final String REQUEST_PARAM_TRIGGER_TYPE = "trigger_type";
- static final String REQUEST_PARAM_TRIGGER_WINDOW_END = "window_end";
- static final String REQUEST_PARAM_TRIGGER_WINDOW_FLEX = "period_flex";
- static final String REQUEST_PARAM_TRIGGER_WINDOW_PERIOD = "period";
- static final String REQUEST_PARAM_TRIGGER_WINDOW_START = "window_start";
-
- @VisibleForTesting
- /* package */ static final int LEGACY_RETRY_POLICY_EXPONENTIAL = 0;
- @VisibleForTesting
- /* package */ static final int LEGACY_RETRY_POLICY_LINEAR = 1;
- @VisibleForTesting
- /* package */ final static int LEGACY_NETWORK_UNMETERED = 1;
- @VisibleForTesting
- /* package */ final static int LEGACY_NETWORK_CONNECTED = 0;
- @VisibleForTesting
- /* package */ final static int LEGACY_NETWORK_ANY = 2;
-
- private JobCoder jobCoder = new JobCoder(BundleProtocol.PACKED_PARAM_BUNDLE_PREFIX, false);
-
- private static void writeExecutionWindowTriggerToBundle(JobParameters job, Bundle b,
- JobTrigger.ExecutionWindowTrigger trigger) {
-
- b.putInt(REQUEST_PARAM_TRIGGER_TYPE, BundleProtocol.TRIGGER_TYPE_EXECUTION_WINDOW);
-
- if (job.isRecurring()) {
- b.putLong(REQUEST_PARAM_TRIGGER_WINDOW_PERIOD,
- trigger.getWindowEnd());
- b.putLong(REQUEST_PARAM_TRIGGER_WINDOW_FLEX,
- trigger.getWindowEnd() - trigger.getWindowStart());
- } else {
- b.putLong(REQUEST_PARAM_TRIGGER_WINDOW_START,
- trigger.getWindowStart());
- b.putLong(REQUEST_PARAM_TRIGGER_WINDOW_END,
- trigger.getWindowEnd());
- }
- }
-
- private static void writeImmediateTriggerToBundle(Bundle b) {
- b.putInt(REQUEST_PARAM_TRIGGER_TYPE, BundleProtocol.TRIGGER_TYPE_IMMEDIATE);
- b.putLong(REQUEST_PARAM_TRIGGER_WINDOW_START, 0);
- b.putLong(REQUEST_PARAM_TRIGGER_WINDOW_END, 30);
- }
-
- private void writeContentUriTriggerToBundle(Bundle data, ContentUriTrigger uriTrigger) {
- data.putInt(BundleProtocol.PACKED_PARAM_TRIGGER_TYPE,
- BundleProtocol.TRIGGER_TYPE_CONTENT_URI);
-
- int size = uriTrigger.getUris().size();
- int[] flagsArray = new int[size];
- Uri[] uriArray = new Uri[size];
- for (int i = 0; i < size; i++) {
- ObservedUri uri = uriTrigger.getUris().get(i);
- flagsArray[i] = uri.getFlags();
- uriArray[i] = uri.getUri();
- }
- data.putIntArray(BundleProtocol.PACKED_PARAM_CONTENT_URI_FLAGS_ARRAY, flagsArray);
- data.putParcelableArray(BundleProtocol.PACKED_PARAM_CONTENT_URI_ARRAY, uriArray);
- }
-
- public Bundle writeToBundle(JobParameters job, Bundle b) {
- b.putString(REQUEST_PARAM_TAG, job.getTag());
- b.putBoolean(REQUEST_PARAM_UPDATE_CURRENT, job.shouldReplaceCurrent());
-
- boolean persisted = job.getLifetime() == Lifetime.FOREVER;
- b.putBoolean(REQUEST_PARAM_PERSISTED, persisted);
- b.putString(REQUEST_PARAM_SERVICE, GooglePlayReceiver.class.getName());
-
- writeTriggerToBundle(job, b);
- writeConstraintsToBundle(job, b);
- writeRetryStrategyToBundle(job, b);
-
- // Embed the job spec (minus extras) into the extras (under a prefix)
- Bundle extras = job.getExtras();
- if (extras == null) {
- extras = new Bundle();
- }
- b.putBundle(REQUEST_PARAM_EXTRAS, jobCoder.encode(job, extras));
-
- return b;
- }
-
- private void writeRetryStrategyToBundle(JobParameters job, Bundle b) {
- RetryStrategy strategy = job.getRetryStrategy();
-
- Bundle rb = new Bundle();
- rb.putInt(REQUEST_PARAM_RETRY_STRATEGY_POLICY,
- convertRetryPolicyToLegacyVersion(strategy.getPolicy()));
- rb.putInt(REQUEST_PARAM_RETRY_STRATEGY_INITIAL_BACKOFF_SECONDS,
- strategy.getInitialBackoff());
- rb.putInt(REQUEST_PARAM_RETRY_STRATEGY_MAXIMUM_BACKOFF_SECONDS,
- strategy.getMaximumBackoff());
-
- b.putBundle(REQUEST_PARAM_RETRY_STRATEGY, rb);
- }
-
- private int convertRetryPolicyToLegacyVersion(@RetryPolicy int policy) {
- switch (policy) {
- case RetryStrategy.RETRY_POLICY_LINEAR:
- return LEGACY_RETRY_POLICY_LINEAR;
-
- case RetryStrategy.RETRY_POLICY_EXPONENTIAL:
- // fallthrough
- default:
- return LEGACY_RETRY_POLICY_EXPONENTIAL;
- }
- }
-
- private void writeTriggerToBundle(JobParameters job, Bundle b) {
- final JobTrigger trigger = job.getTrigger();
-
- if (trigger == Trigger.NOW) {
- writeImmediateTriggerToBundle(b);
- } else if (trigger instanceof JobTrigger.ExecutionWindowTrigger) {
- writeExecutionWindowTriggerToBundle(job, b, (JobTrigger.ExecutionWindowTrigger) trigger);
- } else if (trigger instanceof JobTrigger.ContentUriTrigger) {
- writeContentUriTriggerToBundle(b, (JobTrigger.ContentUriTrigger) trigger);
- } else {
- throw new IllegalArgumentException("Unknown trigger: " + trigger.getClass());
- }
- }
-
- private void writeConstraintsToBundle(JobParameters job, Bundle b) {
- int c = Constraint.compact(job.getConstraints());
-
- b.putBoolean(REQUEST_PARAM_REQUIRES_CHARGING,
- (c & Constraint.DEVICE_CHARGING) == Constraint.DEVICE_CHARGING);
- b.putBoolean(REQUEST_PARAM_REQUIRES_IDLE,
- (c & Constraint.DEVICE_IDLE) == Constraint.DEVICE_IDLE);
- b.putInt(REQUEST_PARAM_REQUIRED_NETWORK, convertConstraintsToLegacyNetConstant(c));
- }
-
- /**
- * Converts a bitmap of Constraint values into a LegacyNetworkConstraint constant (int).
- */
- @LegacyNetworkConstant
- private int convertConstraintsToLegacyNetConstant(int constraintMap) {
- int reqNet = LEGACY_NETWORK_ANY;
-
- reqNet = (constraintMap & Constraint.ON_ANY_NETWORK) == Constraint.ON_ANY_NETWORK
- ? LEGACY_NETWORK_CONNECTED
- : reqNet;
-
- reqNet = (constraintMap & Constraint.ON_UNMETERED_NETWORK) == Constraint.ON_UNMETERED_NETWORK
- ? LEGACY_NETWORK_UNMETERED
- : reqNet;
-
- return reqNet;
- }
-
- @IntDef({LEGACY_NETWORK_ANY, LEGACY_NETWORK_CONNECTED, LEGACY_NETWORK_UNMETERED})
- @Retention(RetentionPolicy.SOURCE)
- private @interface LegacyNetworkConstant {}
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayMessageHandler.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayMessageHandler.java
deleted file mode 100644
index 664805f6..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayMessageHandler.java
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static com.firebase.jobdispatcher.GooglePlayJobWriter.REQUEST_PARAM_TAG;
-import static com.firebase.jobdispatcher.GooglePlayReceiver.TAG;
-
-import android.annotation.TargetApi;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.util.Log;
-import com.firebase.jobdispatcher.JobInvocation.Builder;
-
-/**
- * A messenger for communication with GCM Network Scheduler.
- */
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
-class GooglePlayMessageHandler extends Handler {
-
- static final int MSG_START_EXEC = 1;
- static final int MSG_STOP_EXEC = 2;
- static final int MSG_RESULT = 3;
- private static final int MSG_INIT = 4;
- private final GooglePlayReceiver googlePlayReceiver;
-
- public GooglePlayMessageHandler(Looper mainLooper, GooglePlayReceiver googlePlayReceiver) {
- super(mainLooper);
- this.googlePlayReceiver = googlePlayReceiver;
- }
-
- @Override
- public void handleMessage(Message message) {
- if (message == null) {
- return;
- }
-
- AppOpsManager appOpsManager = (AppOpsManager) googlePlayReceiver.getApplicationContext()
- .getSystemService(Context.APP_OPS_SERVICE);
- try {
- appOpsManager.checkPackage(message.sendingUid, GooglePlayDriver.BACKEND_PACKAGE);
- } catch (SecurityException e) {
- Log.e(TAG, "Message was not sent from GCM.");
- return;
- }
-
- switch (message.what) {
- case MSG_START_EXEC:
- handleStartMessage(message);
- break;
-
- case MSG_STOP_EXEC:
- handleStopMessage(message);
- break;
-
- case MSG_INIT:
- // Not implemented.
- break;
-
- default:
- Log.e(TAG, "Unrecognized message received: " + message);
- break;
- }
- }
-
- private void handleStartMessage(Message message) {
- final Bundle data = message.getData();
-
- final Messenger replyTo = message.replyTo;
- String tag = data.getString(REQUEST_PARAM_TAG);
- if (replyTo == null || tag == null) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Invalid start execution message.");
- }
- return;
- }
-
- GooglePlayMessengerCallback messengerCallback =
- new GooglePlayMessengerCallback(replyTo, tag);
- JobInvocation jobInvocation = googlePlayReceiver.prepareJob(messengerCallback, data);
- googlePlayReceiver.getExecutionDelegator().executeJob(jobInvocation);
- }
-
- private void handleStopMessage(Message message) {
- Builder builder = GooglePlayReceiver.getJobCoder().decode(message.getData());
- if (builder == null) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Invalid stop execution message.");
- }
- return;
- }
- JobInvocation job = builder.build();
- googlePlayReceiver.getExecutionDelegator().stopJob(job);
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayMessengerCallback.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayMessengerCallback.java
deleted file mode 100644
index 06fb153f..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayMessengerCallback.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static com.firebase.jobdispatcher.GooglePlayJobWriter.REQUEST_PARAM_TAG;
-
-import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.support.annotation.NonNull;
-import com.firebase.jobdispatcher.JobService.JobResult;
-
-/**
- * Wraps the GooglePlay messenger in a JobCallback-compatible interface.
- */
-class GooglePlayMessengerCallback implements JobCallback {
-
- private final Messenger messenger;
- private final String tag;
-
- GooglePlayMessengerCallback(Messenger messenger, String tag) {
- this.messenger = messenger;
- this.tag = tag;
- }
-
- @Override
- public void jobFinished(@JobResult int status) {
- try {
- messenger.send(createResultMessage(status));
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- @NonNull
- private Message createResultMessage(int result) {
- final Message msg = Message.obtain();
- msg.what = GooglePlayMessageHandler.MSG_RESULT;
- msg.arg1 = result;
-
- Bundle b = new Bundle();
- b.putString(REQUEST_PARAM_TAG, tag);
- msg.setData(b);
- return msg;
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayReceiver.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayReceiver.java
deleted file mode 100644
index e8dc6060..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/GooglePlayReceiver.java
+++ /dev/null
@@ -1,274 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Messenger;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import android.support.v4.util.SimpleArrayMap;
-import android.util.Log;
-import android.util.Pair;
-import com.firebase.jobdispatcher.Job.Builder;
-import com.firebase.jobdispatcher.JobService.JobResult;
-import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger;
-
-/**
- * Handles incoming execute requests from the GooglePlay driver and forwards them to your Service.
- */
-public class GooglePlayReceiver extends Service implements ExecutionDelegator.JobFinishedCallback {
- /**
- * Logging tag.
- */
- /* package */ static final String TAG = "FJD.GooglePlayReceiver";
- /**
- * The action sent by Google Play services that triggers job execution.
- */
- @VisibleForTesting
- static final String ACTION_EXECUTE = "com.google.android.gms.gcm.ACTION_TASK_READY";
-
- /** Action sent by Google Play services when your app has been updated. */
- @VisibleForTesting
- static final String ACTION_INITIALIZE = "com.google.android.gms.gcm.SERVICE_ACTION_INITIALIZE";
-
- private static final String ERROR_NULL_INTENT = "Null Intent passed, terminating";
- private static final String ERROR_UNKNOWN_ACTION = "Unknown action received, terminating";
- private static final String ERROR_NO_DATA = "No data provided, terminating";
-
- private static final JobCoder prefixedCoder =
- new JobCoder(BundleProtocol.PACKED_PARAM_BUNDLE_PREFIX, true);
-
- private final GooglePlayCallbackExtractor callbackExtractor = new GooglePlayCallbackExtractor();
-
- /**
- * The single Messenger that's returned from valid onBind requests. Guarded by intrinsic lock.
- */
- @VisibleForTesting
- Messenger serviceMessenger;
-
- /**
- * Driver for rescheduling jobs. Guarded by intrinsic lock.
- */
- @VisibleForTesting
- Driver driver;
-
- /**
- * Guarded by intrinsic lock.
- */
- @VisibleForTesting
- ValidationEnforcer validationEnforcer;
-
- /**
- * The ExecutionDelegator used to communicate with client JobServices.
- * Guarded by intrinsic lock.
- */
- private ExecutionDelegator executionDelegator;
-
- /**
- * The most recent startId passed to onStartCommand.
- * Guarded by intrinsic lock.
- */
- private int latestStartId;
-
- /**
- * Endpoint (String) -> Tag (String) -> JobCallback
- */
- private SimpleArrayMap> callbacks =
- new SimpleArrayMap<>(1);
-
- private static void sendResultSafely(JobCallback callback, int result) {
- try {
- callback.jobFinished(result);
- } catch (Throwable e) {
- Log.e(TAG, "Encountered error running callback", e.getCause());
- }
- }
-
- @Override
- public final int onStartCommand(Intent intent, int flags, int startId) {
- try {
- super.onStartCommand(intent, flags, startId);
-
- if (intent == null) {
- Log.w(TAG, ERROR_NULL_INTENT);
- return START_NOT_STICKY;
- }
-
- String action = intent.getAction();
- if (ACTION_EXECUTE.equals(action)) {
- getExecutionDelegator().executeJob(prepareJob(intent));
- return START_NOT_STICKY;
- } else if (ACTION_INITIALIZE.equals(action)) {
- return START_NOT_STICKY;
- }
-
- Log.e(TAG, ERROR_UNKNOWN_ACTION);
- return START_NOT_STICKY;
- } finally {
- synchronized (this) {
- latestStartId = startId;
- if (callbacks.isEmpty()) {
- stopSelf(latestStartId);
- }
- }
- }
- }
-
- @Nullable
- @Override
- public IBinder onBind(Intent intent) {
- // Only Lollipop+ supports UID checking messages, so we can't trust this system on older
- // platforms.
- if (intent == null
- || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
- || !ACTION_EXECUTE.equals(intent.getAction())) {
- return null;
- }
- return getServiceMessenger().getBinder();
- }
-
- private synchronized Messenger getServiceMessenger() {
- if (serviceMessenger == null) {
- serviceMessenger =
- new Messenger(new GooglePlayMessageHandler(Looper.getMainLooper(), this));
- }
- return serviceMessenger;
- }
-
- /* package */ synchronized ExecutionDelegator getExecutionDelegator() {
- if (executionDelegator == null) {
- executionDelegator = new ExecutionDelegator(this, this);
- }
- return executionDelegator;
- }
-
- @NonNull
- private synchronized Driver getGooglePlayDriver() {
- if (driver == null) {
- driver = new GooglePlayDriver(getApplicationContext());
- }
- return driver;
- }
-
- @NonNull
- private synchronized ValidationEnforcer getValidationEnforcer() {
- if (validationEnforcer == null) {
- validationEnforcer = new ValidationEnforcer(getGooglePlayDriver().getValidator());
- }
- return validationEnforcer;
- }
-
- @Nullable
- @VisibleForTesting
- JobInvocation prepareJob(Intent intent) {
- Bundle intentExtras = intent.getExtras();
- if (intentExtras == null) {
- Log.e(TAG, ERROR_NO_DATA);
- return null;
- }
-
- // get the callback first. If we don't have this we can't talk back to the backend.
- Pair extraction = callbackExtractor.extractCallback(intentExtras);
- if (extraction == null) {
- Log.i(TAG, "no callback found");
- return null;
- }
- return prepareJob(extraction.first, extraction.second);
- }
-
- @Nullable
- synchronized JobInvocation prepareJob(JobCallback callback, Bundle bundle) {
- JobInvocation job = prefixedCoder.decodeIntentBundle(bundle);
- if (job == null) {
- Log.e(TAG, "unable to decode job");
- sendResultSafely(callback, JobService.RESULT_FAIL_NORETRY);
- return null;
- }
- SimpleArrayMap map = callbacks.get(job.getService());
- if (map == null) {
- map = new SimpleArrayMap<>(1);
- callbacks.put(job.getService(), map);
- }
-
- map.put(job.getTag(), callback);
-
- return job;
- }
-
- @Override
- public synchronized void onJobFinished(@NonNull JobInvocation js, @JobResult int result) {
- try {
- SimpleArrayMap map = callbacks.get(js.getService());
- if (map == null) {
- return;
- }
- JobCallback callback = map.remove(js.getTag());
- if (callback == null) {
- return;
- }
- if (map.isEmpty()) {
- callbacks.remove(js.getService());
- }
-
- if (needsToBeRescheduled(js, result)) {
- reschedule(js);
- } else {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "sending jobFinished for " + js.getTag() + " = " + result);
- }
- sendResultSafely(callback, result);
- }
- } finally {
- if (callbacks.isEmpty()) {
- // Safe to call stopSelf, even if we're being bound to
- stopSelf(latestStartId);
- }
- }
- }
-
- private void reschedule(JobInvocation jobInvocation) {
- Job job = new Builder(getValidationEnforcer(), jobInvocation)
- .setReplaceCurrent(true)
- .build();
-
- getGooglePlayDriver().schedule(job);
- }
-
- /**
- * Recurring content URI triggered jobs need to be rescheduled when execution is finished.
- *
- * GooglePlay does not support recurring content URI triggered jobs.
- *
- *
{@link JobService#RESULT_FAIL_RETRY} needs to be sent or current triggered URIs will be
- * lost.
- */
- private static boolean needsToBeRescheduled(JobParameters job, int result) {
- return job.isRecurring()
- && job.getTrigger() instanceof ContentUriTrigger
- && result != JobService.RESULT_FAIL_RETRY;
- }
-
- static JobCoder getJobCoder() {
- return prefixedCoder;
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Job.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Job.java
deleted file mode 100644
index 692404fe..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Job.java
+++ /dev/null
@@ -1,378 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import com.firebase.jobdispatcher.Constraint.JobConstraint;
-
-/**
- * Job is the embodiment of a unit of work and an associated set of triggers, settings, and runtime
- * constraints.
- */
-public final class Job implements JobParameters {
- private final String mService;
- private final String mTag;
- private final JobTrigger mTrigger;
- private final RetryStrategy mRetryStrategy;
- private final int mLifetime;
- private final boolean mRecurring;
- private final int[] mConstraints;
- private final boolean mReplaceCurrent;
- private Bundle mExtras;
-
- private Job(Builder builder) {
- mService = builder.mServiceClassName;
- mExtras = builder.mExtras;
- mTag = builder.mTag;
- mTrigger = builder.mTrigger;
- mRetryStrategy = builder.mRetryStrategy;
- mLifetime = builder.mLifetime;
- mRecurring = builder.mRecurring;
- mConstraints = builder.mConstraints != null ? builder.mConstraints : new int[0];
- mReplaceCurrent = builder.mReplaceCurrent;
- }
-
- /**
- * {@inheritDoc}
- */
- @NonNull
- @Override
- public int[] getConstraints() {
- return mConstraints;
- }
-
- /**
- * {@inheritDoc}
- */
- @Nullable
- @Override
- public Bundle getExtras() {
- return mExtras;
- }
-
- /**
- * {@inheritDoc}
- */
- @NonNull
- @Override
- public RetryStrategy getRetryStrategy() {
- return mRetryStrategy;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean shouldReplaceCurrent() {
- return mReplaceCurrent;
- }
-
- @Nullable
- @Override
- public TriggerReason getTriggerReason() {
- return null;
- }
-
- /**
- * {@inheritDoc}
- */
- @NonNull
- @Override
- public String getTag() {
- return mTag;
- }
-
- /**
- * {@inheritDoc}
- */
- @NonNull
- @Override
- public JobTrigger getTrigger() {
- return mTrigger;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int getLifetime() {
- return mLifetime;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isRecurring() {
- return mRecurring;
- }
-
- /**
- * {@inheritDoc}
- */
- @NonNull
- @Override
- public String getService() {
- return mService;
- }
-
- /**
- * A class that understands how to build a {@link Job}. Retrieved by calling
- * {@link FirebaseJobDispatcher#newJobBuilder()}.
- */
- public final static class Builder implements JobParameters {
- private final ValidationEnforcer mValidator;
-
- private String mServiceClassName;
- private Bundle mExtras;
- private String mTag;
- private JobTrigger mTrigger = Trigger.NOW;
- private int mLifetime = Lifetime.UNTIL_NEXT_BOOT;
- private int[] mConstraints;
-
- private RetryStrategy mRetryStrategy = RetryStrategy.DEFAULT_EXPONENTIAL;
- private boolean mReplaceCurrent = false;
- private boolean mRecurring = false;
-
- Builder(ValidationEnforcer validator) {
- mValidator = validator;
- }
-
- Builder(ValidationEnforcer validator, JobParameters job) {
- mValidator = validator;
-
- mTag = job.getTag();
- mServiceClassName = job.getService();
- mTrigger = job.getTrigger();
- mRecurring = job.isRecurring();
- mLifetime = job.getLifetime();
- mConstraints = job.getConstraints();
- mExtras = job.getExtras();
- mRetryStrategy = job.getRetryStrategy();
- }
-
- /**
- * Adds the provided constraint to the current list of runtime constraints.
- */
- public Builder addConstraint(@JobConstraint int constraint) {
- // Create a new, longer constraints array
- int[] newConstraints = new int[mConstraints == null ? 1 : mConstraints.length + 1];
-
- if (mConstraints != null && mConstraints.length != 0) {
- // Copy all the old values over
- System.arraycopy(mConstraints, 0, newConstraints, 0, mConstraints.length);
- }
-
- // add the new value
- newConstraints[newConstraints.length - 1] = constraint;
- // update the pointer
- mConstraints = newConstraints;
-
- return this;
- }
-
- /**
- * Sets whether this Job should replace pre-existing Jobs with the same tag.
- */
- public Builder setReplaceCurrent(boolean replaceCurrent) {
- mReplaceCurrent = replaceCurrent;
-
- return this;
- }
-
- /**
- * Builds the Job, using the settings provided so far.
- *
- * @throws ValidationEnforcer.ValidationException
- */
- public Job build() {
- mValidator.ensureValid(this);
-
- return new Job(this);
- }
-
- /**
- * {@inheritDoc}
- */
- @NonNull
- @Override
- public String getService() {
- return mServiceClassName;
- }
-
- /**
- * Sets the backing JobService class for the Job. See {@link #getService()}.
- */
- public Builder setService(Class extends JobService> serviceClass) {
- mServiceClassName = serviceClass == null ? null : serviceClass.getName();
-
- return this;
- }
-
- /**
- * Sets the backing JobService class name for the Job. See {@link #getService()}.
- *
- *
Should not be exposed, for internal use only.
- */
- Builder setServiceName(String serviceClassName) {
- mServiceClassName = serviceClassName;
-
- return this;
- }
-
- /**
- * {@inheritDoc}
- */
- @NonNull
- @Override
- public String getTag() {
- return mTag;
- }
-
- /**
- * Sets the unique String tag used to identify the Job. See {@link #getTag()}.
- */
- public Builder setTag(String tag) {
- mTag = tag;
-
- return this;
- }
-
- /**
- * {@inheritDoc}
- */
- @NonNull
- @Override
- public JobTrigger getTrigger() {
- return mTrigger;
- }
-
- /**
- * Sets the Trigger used for the Job. See {@link #getTrigger()}.
- */
- public Builder setTrigger(JobTrigger trigger) {
- mTrigger = trigger;
-
- return this;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- @Lifetime.LifetimeConstant
- public int getLifetime() {
- return mLifetime;
- }
-
- /**
- * Sets the Job's lifetime, or how long it should persist. See {@link #getLifetime()}.
- */
- public Builder setLifetime(@Lifetime.LifetimeConstant int lifetime) {
- mLifetime = lifetime;
-
- return this;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isRecurring() {
- return mRecurring;
- }
-
- /**
- * Sets whether the job should recur. The default is false.
- */
- public Builder setRecurring(boolean recurring) {
- mRecurring = recurring;
-
- return this;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- @JobConstraint
- public int[] getConstraints() {
- return mConstraints == null ? new int[]{} : mConstraints;
- }
-
- /**
- * Sets the Job's runtime constraints. See {@link #getConstraints()}.
- */
- public Builder setConstraints(@JobConstraint int... constraints) {
- mConstraints = constraints;
-
- return this;
- }
-
- /**
- * {@inheritDoc}
- */
- @Nullable
- @Override
- public Bundle getExtras() {
- return mExtras;
- }
-
- /**
- * Sets the user-defined extras associated with the Job. See {@link #getExtras()}.
- */
- public Builder setExtras(Bundle extras) {
- mExtras = extras;
-
- return this;
- }
-
- /**
- * {@inheritDoc}
- */
- @NonNull
- @Override
- public RetryStrategy getRetryStrategy() {
- return mRetryStrategy;
- }
-
- /**
- * Set the RetryStrategy used for the Job. See {@link #getRetryStrategy()}.
- */
- public Builder setRetryStrategy(RetryStrategy retryStrategy) {
- mRetryStrategy = retryStrategy;
-
- return this;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean shouldReplaceCurrent() {
- return mReplaceCurrent;
- }
-
- @Nullable
- @Override
- public TriggerReason getTriggerReason() {
- return null;
- }
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobCallback.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobCallback.java
deleted file mode 100644
index adeb82ea..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobCallback.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-/**
- * JobCallback describes an object that knows how to send a JobResult back to the underlying
- * execution driver.
- */
-public interface JobCallback {
- /**
- * @throws RuntimeException
- */
- void jobFinished(@JobService.JobResult int status);
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobCoder.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobCoder.java
deleted file mode 100644
index e5523587..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobCoder.java
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static com.firebase.jobdispatcher.Constraint.compact;
-import static com.firebase.jobdispatcher.Constraint.uncompact;
-import static com.firebase.jobdispatcher.ExecutionDelegator.TAG;
-
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.Log;
-import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-/**
- * JobCoder is a tool to encode and decode JobSpecs from Bundles.
- */
-/* package */ final class JobCoder {
- private final boolean includeExtras;
- private final String prefix;
-
- private static final String JSON_URI_FLAGS = "uri_flags";
- private static final String JSON_URIS = "uris";
-
- JobCoder(String prefix, boolean includeExtras) {
- this.includeExtras = includeExtras;
- this.prefix = prefix;
- }
-
- @NonNull
- Bundle encode(@NonNull JobParameters jobParameters, @NonNull Bundle data) {
- if (data == null) {
- throw new IllegalArgumentException("Unexpected null Bundle provided");
- }
-
- data.putInt(prefix + BundleProtocol.PACKED_PARAM_LIFETIME,
- jobParameters.getLifetime());
- data.putBoolean(prefix + BundleProtocol.PACKED_PARAM_RECURRING,
- jobParameters.isRecurring());
- data.putBoolean(prefix + BundleProtocol.PACKED_PARAM_REPLACE_CURRENT,
- jobParameters.shouldReplaceCurrent());
- data.putString(prefix + BundleProtocol.PACKED_PARAM_TAG,
- jobParameters.getTag());
- data.putString(prefix + BundleProtocol.PACKED_PARAM_SERVICE,
- jobParameters.getService());
- data.putInt(prefix + BundleProtocol.PACKED_PARAM_CONSTRAINTS,
- compact(jobParameters.getConstraints()));
-
- if (includeExtras) {
- data.putBundle(prefix + BundleProtocol.PACKED_PARAM_EXTRAS,
- jobParameters.getExtras());
- }
-
- encodeTrigger(jobParameters.getTrigger(), data);
- encodeRetryStrategy(jobParameters.getRetryStrategy(), data);
-
- return data;
- }
-
- JobInvocation decodeIntentBundle(@NonNull Bundle bundle) {
- if (bundle == null) {
- Log.e(TAG, "Unexpected null Bundle provided");
- return null;
- }
-
- Bundle taskExtras = bundle.getBundle(GooglePlayJobWriter.REQUEST_PARAM_EXTRAS);
- if (taskExtras == null) {
- return null;
- }
-
- JobInvocation.Builder builder = decode(taskExtras);
-
- List triggeredContentUris =
- bundle.getParcelableArrayList(BundleProtocol.PACKED_PARAM_TRIGGERED_URIS);
- if (triggeredContentUris != null) {
- builder.setTriggerReason(new TriggerReason(triggeredContentUris));
- }
- return builder.build();
- }
-
- @Nullable
- public JobInvocation.Builder decode(@NonNull Bundle data) {
- if (data == null) {
- throw new IllegalArgumentException("Unexpected null Bundle provided");
- }
-
- boolean recur = data.getBoolean(prefix + BundleProtocol.PACKED_PARAM_RECURRING);
- boolean replaceCur = data.getBoolean(prefix + BundleProtocol.PACKED_PARAM_REPLACE_CURRENT);
- int lifetime = data.getInt(prefix + BundleProtocol.PACKED_PARAM_LIFETIME);
- int[] constraints = uncompact(data.getInt(prefix + BundleProtocol.PACKED_PARAM_CONSTRAINTS));
-
- JobTrigger trigger = decodeTrigger(data);
- RetryStrategy retryStrategy = decodeRetryStrategy(data);
-
- String tag = data.getString(prefix + BundleProtocol.PACKED_PARAM_TAG);
- String service = data.getString(prefix + BundleProtocol.PACKED_PARAM_SERVICE);
-
- if (tag == null || service == null || trigger == null || retryStrategy == null) {
- return null;
- }
-
- JobInvocation.Builder builder = new JobInvocation.Builder();
- builder.setTag(tag);
- builder.setService(service);
- builder.setTrigger(trigger);
- builder.setRetryStrategy(retryStrategy);
- builder.setRecurring(recur);
- //noinspection WrongConstant
- builder.setLifetime(lifetime);
- //noinspection WrongConstant
- builder.setConstraints(constraints);
- builder.setReplaceCurrent(replaceCur);
-
- // repack the taskExtras
- builder.addExtras(data);
- return builder;
- }
-
- @NonNull
- private JobTrigger decodeTrigger(Bundle data) {
- switch (data.getInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_TYPE)) {
- case BundleProtocol.TRIGGER_TYPE_IMMEDIATE:
- return Trigger.NOW;
-
- case BundleProtocol.TRIGGER_TYPE_EXECUTION_WINDOW:
- return Trigger.executionWindow(
- data.getInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_WINDOW_START),
- data.getInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_WINDOW_END));
-
- case BundleProtocol.TRIGGER_TYPE_CONTENT_URI:
- String uris = data.getString(prefix + BundleProtocol.PACKED_PARAM_OBSERVED_URI);
- List observedUris = convertJsonToObservedUris(uris);
- return Trigger.contentUriTrigger(Collections.unmodifiableList(observedUris));
-
- default:
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Unsupported trigger.");
- }
- return null;
- }
- }
-
- private void encodeTrigger(JobTrigger trigger, Bundle data) {
- if (trigger == Trigger.NOW) {
- data.putInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_TYPE,
- BundleProtocol.TRIGGER_TYPE_IMMEDIATE);
- } else if (trigger instanceof JobTrigger.ExecutionWindowTrigger) {
- JobTrigger.ExecutionWindowTrigger t = (JobTrigger.ExecutionWindowTrigger) trigger;
-
- data.putInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_TYPE,
- BundleProtocol.TRIGGER_TYPE_EXECUTION_WINDOW);
- data.putInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_WINDOW_START,
- t.getWindowStart());
- data.putInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_WINDOW_END,
- t.getWindowEnd());
- } else if (trigger instanceof JobTrigger.ContentUriTrigger) {
- data.putInt(prefix + BundleProtocol.PACKED_PARAM_TRIGGER_TYPE,
- BundleProtocol.TRIGGER_TYPE_CONTENT_URI);
- ContentUriTrigger uriTrigger = (ContentUriTrigger) trigger;
- String jsonTrigger = convertObservedUrisToJsonString(uriTrigger.getUris());
- data.putString(prefix + BundleProtocol.PACKED_PARAM_OBSERVED_URI, jsonTrigger);
- } else {
- throw new IllegalArgumentException("Unsupported trigger.");
- }
- }
-
- private RetryStrategy decodeRetryStrategy(Bundle data) {
- int policy = data.getInt(prefix + BundleProtocol.PACKED_PARAM_RETRY_STRATEGY_POLICY);
- if (policy != RetryStrategy.RETRY_POLICY_EXPONENTIAL
- && policy != RetryStrategy.RETRY_POLICY_LINEAR) {
-
- return RetryStrategy.DEFAULT_EXPONENTIAL;
- }
-
- //noinspection WrongConstant
- return new RetryStrategy(
- policy,
- data.getInt(prefix + BundleProtocol.PACKED_PARAM_RETRY_STRATEGY_INITIAL_BACKOFF_SECONDS),
- data.getInt(prefix + BundleProtocol.PACKED_PARAM_RETRY_STRATEGY_MAXIMUM_BACKOFF_SECONDS));
- }
-
- private void encodeRetryStrategy(RetryStrategy retryStrategy, Bundle data) {
- if (retryStrategy == null) {
- retryStrategy = RetryStrategy.DEFAULT_EXPONENTIAL;
- }
-
- data.putInt(prefix + BundleProtocol.PACKED_PARAM_RETRY_STRATEGY_POLICY,
- retryStrategy.getPolicy());
- data.putInt(prefix + BundleProtocol.PACKED_PARAM_RETRY_STRATEGY_INITIAL_BACKOFF_SECONDS,
- retryStrategy.getInitialBackoff());
- data.putInt(prefix + BundleProtocol.PACKED_PARAM_RETRY_STRATEGY_MAXIMUM_BACKOFF_SECONDS,
- retryStrategy.getMaximumBackoff());
- }
-
- @NonNull
- private String convertObservedUrisToJsonString(@NonNull List uris) {
- JSONObject contentUris = new JSONObject();
- JSONArray jsonFlags = new JSONArray();
- JSONArray jsonUris = new JSONArray();
- for (ObservedUri uri : uris) {
- jsonFlags.put(uri.getFlags());
- jsonUris.put(uri.getUri());
- }
- try {
- contentUris.put(JSON_URI_FLAGS, jsonFlags);
- contentUris.put(JSON_URIS, jsonUris);
- } catch (JSONException e) {
- throw new RuntimeException(e);
- }
- return contentUris.toString();
- }
-
- @NonNull
- private List convertJsonToObservedUris(@NonNull String contentUrisJson) {
- List uris = new ArrayList<>();
- try {
- JSONObject json = new JSONObject(contentUrisJson);
- JSONArray jsonFlags = json.getJSONArray(JSON_URI_FLAGS);
- JSONArray jsonUris = json.getJSONArray(JSON_URIS);
- int length = jsonFlags.length();
-
- for (int i = 0; i < length; i++) {
- int flags = jsonFlags.getInt(i);
- String uri = jsonUris.getString(i);
- uris.add(new ObservedUri(Uri.parse(uri), flags));
- }
- } catch (JSONException e) {
- throw new RuntimeException(e);
- }
- return uris;
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobInvocation.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobInvocation.java
deleted file mode 100644
index 08263e84..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobInvocation.java
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import com.firebase.jobdispatcher.Constraint.JobConstraint;
-
-/**
- * An internal non-Job implementation of JobParameters. Passed to JobService invocations.
- */
-/* package */ final class JobInvocation implements JobParameters {
-
- @NonNull
- private final String mTag;
-
- @NonNull
- private final String mService;
-
- @NonNull
- private final JobTrigger mTrigger;
-
- private final boolean mRecurring;
-
- private final int mLifetime;
-
- @NonNull
- @JobConstraint
- private final int[] mConstraints;
-
- @NonNull
- private final Bundle mExtras;
-
- private final RetryStrategy mRetryStrategy;
-
- private final boolean mReplaceCurrent;
-
- private final TriggerReason mTriggerReason;
-
- private JobInvocation(Builder builder) {
- mTag = builder.mTag;
- mService = builder.mService;
- mTrigger = builder.mTrigger;
- mRetryStrategy = builder.mRetryStrategy;
- mRecurring = builder.mRecurring;
- mLifetime = builder.mLifetime;
- mConstraints = builder.mConstraints;
- mExtras = builder.mExtras;
- mReplaceCurrent = builder.mReplaceCurrent;
- mTriggerReason = builder.mTriggerReason;
- }
-
- @NonNull
- @Override
- public String getService() {
- return mService;
- }
-
- @NonNull
- @Override
- public String getTag() {
- return mTag;
- }
-
- @NonNull
- @Override
- public JobTrigger getTrigger() {
- return mTrigger;
- }
-
- @Override
- public int getLifetime() {
- return mLifetime;
- }
-
- @Override
- public boolean isRecurring() {
- return mRecurring;
- }
-
- @NonNull
- @Override
- public int[] getConstraints() {
- return mConstraints;
- }
-
- @NonNull
- @Override
- public Bundle getExtras() {
- return mExtras;
- }
-
- @NonNull
- @Override
- public RetryStrategy getRetryStrategy() {
- return mRetryStrategy;
- }
-
- @Override
- public boolean shouldReplaceCurrent() {
- return mReplaceCurrent;
- }
-
- @Override
- public TriggerReason getTriggerReason() {
- return mTriggerReason;
- }
-
- static final class Builder {
-
- @NonNull
- private String mTag;
-
- @NonNull
- private String mService;
-
- @NonNull
- private JobTrigger mTrigger;
-
- private boolean mRecurring;
-
- private int mLifetime;
-
- @NonNull
- @JobConstraint
- private int[] mConstraints;
-
- @NonNull
- private final Bundle mExtras = new Bundle();
-
- private RetryStrategy mRetryStrategy;
-
- private boolean mReplaceCurrent;
-
- private TriggerReason mTriggerReason;
-
- JobInvocation build() {
- if (mTag == null || mService == null || mTrigger == null) {
- throw new IllegalArgumentException("Required fields were not populated.");
- }
- return new JobInvocation(this);
- }
-
- public Builder setTag(@NonNull String mTag) {
- this.mTag = mTag;
- return this;
- }
-
- public Builder setService(@NonNull String mService) {
- this.mService = mService;
- return this;
- }
-
- public Builder setTrigger(@NonNull JobTrigger mTrigger) {
- this.mTrigger = mTrigger;
- return this;
- }
-
- public Builder setRecurring(boolean mRecurring) {
- this.mRecurring = mRecurring;
- return this;
- }
-
- public Builder setLifetime(@Lifetime.LifetimeConstant int mLifetime) {
- this.mLifetime = mLifetime;
- return this;
- }
-
- public Builder setConstraints(@JobConstraint @NonNull int[] mConstraints) {
- this.mConstraints = mConstraints;
- return this;
- }
-
- public Builder addExtras(@NonNull Bundle bundle) {
- if (bundle != null) {
- mExtras.putAll(bundle);
- }
- return this;
- }
-
- public Builder setRetryStrategy(RetryStrategy mRetryStrategy) {
- this.mRetryStrategy = mRetryStrategy;
- return this;
- }
-
- public Builder setReplaceCurrent(boolean mReplaceCurrent) {
- this.mReplaceCurrent = mReplaceCurrent;
- return this;
- }
-
- public Builder setTriggerReason(TriggerReason triggerReason) {
- this.mTriggerReason = triggerReason;
- return this;
- }
- }
-
- /**
- * @return true if the tag and the service of provided {@link JobInvocation} have the same
- * values.
- */
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || !getClass().equals(o.getClass())) {
- return false;
- }
-
- JobInvocation jobInvocation = (JobInvocation) o;
-
- return mTag.equals(jobInvocation.mTag)
- && mService.equals(jobInvocation.mService);
- }
-
- @Override
- public int hashCode() {
- int result = mTag.hashCode();
- result = 31 * result + mService.hashCode();
- return result;
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobParameters.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobParameters.java
deleted file mode 100644
index 24f0844e..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobParameters.java
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import com.firebase.jobdispatcher.Constraint.JobConstraint;
-
-/**
- * JobParameters represents anything that can describe itself in terms of Job components.
- */
-public interface JobParameters {
-
- /**
- * Returns the name of the backing JobService class.
- */
- @NonNull
- String getService();
-
- /**
- * Returns a string identifier for the Job. Used when cancelling Jobs and displaying debug
- * messages.
- */
- @NonNull
- String getTag();
-
- /**
- * The Job's Trigger, which decides when the Job is ready to run.
- */
- @NonNull
- JobTrigger getTrigger();
-
- /**
- * The Job's lifetime; how long it should persist for.
- */
- @Lifetime.LifetimeConstant
- int getLifetime();
-
- /**
- * Whether the Job should repeat.
- */
- boolean isRecurring();
-
- /**
- * The runtime constraints applied to this Job. A Job is not run until the trigger is activated
- * and all the runtime constraints are satisfied.
- */
- @JobConstraint
- int[] getConstraints();
-
- /**
- * The optional set of user-supplied extras associated with this Job.
- */
- @Nullable
- Bundle getExtras();
-
- /**
- * The RetryStrategy for the Job. Used to determine how to handle failures.
- */
- @NonNull
- RetryStrategy getRetryStrategy();
-
- /**
- * Whether the Job should replace a pre-existing Job with the same tag.
- */
- boolean shouldReplaceCurrent();
-
- /** @return A {@link TriggerReason} that - if non null - describes why the job was triggered. */
- @Nullable
- TriggerReason getTriggerReason();
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobService.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobService.java
deleted file mode 100644
index 41076e76..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobService.java
+++ /dev/null
@@ -1,259 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.app.Service;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Message;
-import android.support.annotation.IntDef;
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import android.support.v4.util.SimpleArrayMap;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Locale;
-
-/**
- * JobService is the fundamental unit of work used in the JobDispatcher.
- *
- * Users will need to override {@link #onStartJob(JobParameters)}, which is where any asynchronous
- * execution should start. This method, like most lifecycle methods, runs on the main thread; you
- * must offload execution to another thread (or {@link android.os.AsyncTask}, or
- * {@link android.os.Handler}, or your favorite flavor of concurrency).
- *
- * Once any asynchronous work is complete {@link #jobFinished(JobParameters, boolean)} should be
- * called to inform the backing driver of the result.
- *
- * Implementations should also override {@link #onStopJob(JobParameters)}, which will be called if
- * the scheduling engine wishes to interrupt your work (most likely because the runtime constraints
- * that are associated with the job in question are no longer met).
- */
-public abstract class JobService extends Service {
- /**
- * Returned to indicate the job was executed successfully. If the job is not recurring (i.e. a
- * one-off) it will be dequeued and forgotten. If it is recurring the trigger will be reset and
- * the job will be requeued.
- */
- public static final int RESULT_SUCCESS = 0;
-
- /**
- * Returned to indicate the job encountered an error during execution and should be retried after
- * a backoff period.
- */
- public static final int RESULT_FAIL_RETRY = 1;
-
- /**
- * Returned to indicate the job encountered an error during execution but should not be retried.
- * If the job is not recurring (i.e. a one-off) it will be dequeued and forgotten. If it is
- * recurring the trigger will be reset and the job will be requeued.
- */
- public static final int RESULT_FAIL_NORETRY = 2;
-
- static final String TAG = "FJD.JobService";
-
- @VisibleForTesting
- static final String ACTION_EXECUTE = "com.firebase.jobdispatcher.ACTION_EXECUTE";
-
- /**
- * Correlates job tags (unique strings) with Messages, which are used to signal the completion
- * of a job.
- */
- private final SimpleArrayMap runningJobs = new SimpleArrayMap<>(1);
- private LocalBinder binder = new LocalBinder();
-
- /**
- * The entry point to your Job. Implementations should offload work to another thread of
- * execution as soon as possible because this runs on the main thread. If work was offloaded,
- * call {@link JobService#jobFinished(JobParameters, boolean)} to notify the scheduling service
- * that the work is completed.
- *
- * In order to reschedule use {@link JobService#jobFinished(JobParameters, boolean)}.
- *
- * @return {@code true} if there is more work remaining in the worker thread, {@code false} if the job was completed.
- */
- @MainThread
- public abstract boolean onStartJob(JobParameters job);
-
- /**
- * Called when the scheduling engine has decided to interrupt the execution of a running job,
- * most likely because the runtime constraints associated with the job are no longer satisfied.
- * The job must stop execution.
- *
- * @return true if the job should be retried
- * @see com.firebase.jobdispatcher.JobInvocation.Builder#setRetryStrategy(RetryStrategy)
- * @see RetryStrategy
- */
- @MainThread
- public abstract boolean onStopJob(JobParameters job);
-
- @MainThread
- void start(JobParameters job, Message msg) {
- synchronized (runningJobs) {
- if (runningJobs.containsKey(job.getTag())) {
- Log.w(TAG, String
- .format(Locale.US, "Job with tag = %s was already running.", job.getTag()));
- return;
- }
- runningJobs.put(job.getTag(), new JobCallback(msg));
-
- boolean moreWork = onStartJob(job);
- if (!moreWork) {
- JobCallback callback = runningJobs.remove(job.getTag());
- if (callback != null) {
- callback.sendResult(RESULT_SUCCESS);
- }
- }
- }
- }
-
- @MainThread
- void stop(JobInvocation job) {
- synchronized (runningJobs) {
- JobCallback jobCallback = runningJobs.remove(job.getTag());
-
- if (jobCallback == null) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Provided job has already been executed.");
- }
- return;
- }
- boolean shouldRetry = onStopJob(job);
- jobCallback.sendResult(shouldRetry ? RESULT_FAIL_RETRY : RESULT_SUCCESS);
- }
- }
-
- /**
- * Callback to inform the scheduling driver that you've finished executing. Can be called from
- * any thread. When the system receives this message, it will release the wakelock being held.
- *
- * @param job
- * @param needsReschedule
- * whether the job should be rescheduled
- * @see com.firebase.jobdispatcher.JobInvocation.Builder#setRetryStrategy(RetryStrategy)
- */
- public final void jobFinished(@NonNull JobParameters job, boolean needsReschedule) {
- if (job == null) {
- Log.e(TAG, "jobFinished called with a null JobParameters");
- return;
- }
-
- synchronized (runningJobs) {
- JobCallback jobCallback = runningJobs.remove(job.getTag());
-
- if (jobCallback != null) {
- jobCallback.sendResult(needsReschedule ? RESULT_FAIL_RETRY : RESULT_SUCCESS);
- }
- }
- }
-
- @Override
- public final int onStartCommand(Intent intent, int flags, int startId) {
- stopSelf(startId);
-
- return START_NOT_STICKY;
- }
-
- @Nullable
- @Override
- public final IBinder onBind(Intent intent) {
- return binder;
- }
-
- @Override
- public final boolean onUnbind(Intent intent) {
- synchronized (runningJobs) {
- for (int i = runningJobs.size() - 1; i >= 0; i--) {
- JobCallback callback = runningJobs.get(runningJobs.keyAt(i));
- if (callback != null && callback.message != null) {
- if (callback.message.obj instanceof JobParameters) {
- callback.sendResult(onStopJob((JobParameters) callback.message.obj)
- // returned true, would like to be rescheduled
- ? RESULT_FAIL_RETRY
- // returned false, but was interrupted so consider it a fail
- : RESULT_FAIL_NORETRY);
- }
- }
- }
- }
-
- return super.onUnbind(intent);
- }
-
- @Override
- public final void onRebind(Intent intent) {
- super.onRebind(intent);
- }
-
- @Override
- public final void onStart(Intent intent, int startId) {
- }
-
- @Override
- protected final void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- super.dump(fd, writer, args);
- }
-
- @Override
- public final void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- }
-
- @Override
- public final void onTaskRemoved(Intent rootIntent) {
- super.onTaskRemoved(rootIntent);
- }
-
- /**
- * The result returned from a job execution.
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({RESULT_SUCCESS, RESULT_FAIL_RETRY, RESULT_FAIL_NORETRY})
- public @interface JobResult {
- }
-
- private final static class JobCallback {
- public final Message message;
-
- private JobCallback(Message message) {
- this.message = message;
- }
-
- void sendResult(@JobResult int result) {
- try {
- if (message != null) {
- message.arg1 = result;
- message.sendToTarget();
- }
- } catch (Exception ignored) {}//catch this freaking crash!!!!
- }
- }
-
- class LocalBinder extends Binder {
- JobService getService() {
- return JobService.this;
- }
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobServiceConnection.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobServiceConnection.java
deleted file mode 100644
index c96e9f68..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobServiceConnection.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static com.firebase.jobdispatcher.ExecutionDelegator.TAG;
-
-import android.content.ComponentName;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-import android.os.Message;
-import android.support.annotation.VisibleForTesting;
-import android.util.Log;
-
-/**
- * ServiceConnection for job execution.
- */
-@VisibleForTesting
-class JobServiceConnection implements ServiceConnection {
-
- private final JobInvocation jobInvocation;
- // Should be sent only once. Can't be reused.
- private final Message jobFinishedMessage;
- private boolean wasMessageUsed = false;
-
- //Guarded by "this". Can be updated from main and binder threads.
- private JobService.LocalBinder binder;
-
- JobServiceConnection(JobInvocation jobInvocation, Message jobFinishedMessage) {
- this.jobFinishedMessage = jobFinishedMessage;
- this.jobInvocation = jobInvocation;
- this.jobFinishedMessage.obj = this.jobInvocation;
- }
-
- @Override
- public synchronized void onServiceConnected(ComponentName name, IBinder service) {
- if (!(service instanceof JobService.LocalBinder)) {
- Log.w(TAG, "Unknown service connected");
- return;
- }
- if (wasMessageUsed) {
- Log.w(TAG, "onServiceConnected Duplicate calls. Ignored.");
- return;
- } else {
- wasMessageUsed = true;
- }
-
- binder = (JobService.LocalBinder) service;
-
- JobService jobService = binder.getService();
-
- jobService.start(jobInvocation, jobFinishedMessage);
- }
-
- @Override
- public synchronized void onServiceDisconnected(ComponentName name) {
- binder = null;
- }
-
- synchronized boolean isBound() {
- return binder != null;
- }
-
- synchronized void onStop() {
- if (isBound()) {
- binder.getService().stop(jobInvocation);
- }
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobTrigger.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobTrigger.java
deleted file mode 100644
index b1c510c7..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobTrigger.java
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import java.util.List;
-
-/**
- * Contains all supported triggers.
- */
-public class JobTrigger {
-
- /**
- * ImmediateTrigger is a Trigger that's immediately available. The Job will be run as soon as
- * the runtime constraints are satisfied.
- */
- public static final class ImmediateTrigger extends JobTrigger {
- /* package */ ImmediateTrigger() {}
- }
-
- /**
- * ExecutionWindow represents a Job trigger that becomes eligible once
- * the current elapsed time exceeds the scheduled time + the {@code windowStart}
- * value. The scheduler backend is encouraged to use the windowEnd value as a
- * signal that the job should be run, but this is not an enforced behavior.
- */
- public static final class ExecutionWindowTrigger extends JobTrigger {
- private final int mWindowStart;
- private final int mWindowEnd;
-
- /* package */ ExecutionWindowTrigger(int windowStart, int windowEnd) {
- this.mWindowStart = windowStart;
- this.mWindowEnd = windowEnd;
- }
-
- public int getWindowStart() {
- return mWindowStart;
- }
-
- public int getWindowEnd() {
- return mWindowEnd;
- }
- }
-
- /** A trigger that will be triggered on content update for any of provided uris. */
- public static final class ContentUriTrigger extends JobTrigger {
- private final List uris;
-
- /* package */ ContentUriTrigger(List uris) {
- this.uris = uris;
- }
-
- public List getUris() {
- return uris;
- }
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobValidator.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobValidator.java
deleted file mode 100644
index c97198f4..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/JobValidator.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.support.annotation.Nullable;
-import java.util.List;
-
-/**
- * A JobValidator is an object that knows how to validate Jobs and some of their composite
- * components.
- */
-public interface JobValidator {
- /**
- * Returns a List of error messages, or null if the JobParameters is
- * valid.
- */
- @Nullable
- List validate(JobParameters job);
-
- /**
- * Returns a List of error messages, or null if the Trigger is
- * valid.
- * @param trigger
- */
- @Nullable
- List validate(JobTrigger trigger);
-
- /**
- * Returns a List of error messages, or null if the RetryStrategy
- * is valid.
- */
- @Nullable
- List validate(RetryStrategy retryStrategy);
-
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Lifetime.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Lifetime.java
deleted file mode 100644
index 456bc221..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Lifetime.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.support.annotation.IntDef;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Lifetime represents how long a Job should last.
- */
-public final class Lifetime {
- /**
- * The Job should be preserved until the next boot. This is the default.
- */
- public final static int UNTIL_NEXT_BOOT = 1;
-
- /**
- * The Job should be preserved "forever."
- */
- public final static int FOREVER = 2;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({FOREVER, UNTIL_NEXT_BOOT})
- @interface LifetimeConstant {}
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ObservedUri.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ObservedUri.java
deleted file mode 100644
index b22c15e0..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ObservedUri.java
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.net.Uri;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/** Represents a single observed URI and any associated flags. */
-public final class ObservedUri {
-
- private final Uri uri;
-
- private final int flags;
-
- /** Flag enforcement. */
- @IntDef(flag = true, value = Flags.FLAG_NOTIFY_FOR_DESCENDANTS)
- @Retention(RetentionPolicy.SOURCE)
- public @interface Flags {
-
- /**
- * Triggers if any descendants of the given URI change. Corresponds to the {@code
- * notifyForDescendants} of {@link android.content.ContentResolver#registerContentObserver}.
- */
- int FLAG_NOTIFY_FOR_DESCENDANTS = 1 << 0;
- }
-
- /**
- * Create a new ObservedUri.
- *
- * @param uri The URI to observe.
- * @param flags Any {@link Flags} associated with the URI.
- */
- public ObservedUri(@NonNull Uri uri, @Flags int flags) {
- if (uri == null) {
- throw new IllegalArgumentException("URI must not be null.");
- }
- this.uri = uri;
- this.flags = flags;
- }
-
- public Uri getUri() {
- return uri;
- }
-
- public int getFlags() {
- return flags;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (!(o instanceof ObservedUri)) {
- return false;
- }
-
- ObservedUri otherUri = (ObservedUri) o;
- return flags == otherUri.flags && uri.equals(otherUri.uri);
- }
-
- @Override
- public int hashCode() {
- return uri.hashCode() ^ flags;
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/RetryStrategy.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/RetryStrategy.java
deleted file mode 100644
index 7d9b3190..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/RetryStrategy.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.support.annotation.IntDef;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * RetryStrategy represents an approach to handling job execution failures. Jobs will have a
- * time-based backoff enforced, based on the chosen policy (one of {@code RETRY_POLICY_EXPONENTIAL}
- * or {@code RETRY_POLICY_LINEAR}.
- */
-public final class RetryStrategy {
- /**
- * Increase the backoff time exponentially.
- *
- * Calculated using {@code initial_backoff * 2 ^ (num_failures - 1)}.
- */
- public final static int RETRY_POLICY_EXPONENTIAL = 1;
-
- /**
- * Increase the backoff time linearly.
- *
- * Calculated using {@code initial_backoff * num_failures}.
- */
- public final static int RETRY_POLICY_LINEAR = 2;
-
- /**
- * Expected schedule is: [30s, 60s, 120s, 240s, ..., 3600s]
- */
- public final static RetryStrategy DEFAULT_EXPONENTIAL =
- new RetryStrategy(RETRY_POLICY_EXPONENTIAL, 30, 3600);
-
- /**
- * Expected schedule is: [30s, 60s, 90s, 120s, ..., 3600s]
- */
- public final static RetryStrategy DEFAULT_LINEAR =
- new RetryStrategy(RETRY_POLICY_LINEAR, 30, 3600);
-
- @RetryPolicy
- private final int mPolicy;
- private final int mInitialBackoff;
- private final int mMaximumBackoff;
-
- /* package */ RetryStrategy(@RetryPolicy int policy, int initialBackoff, int maximumBackoff) {
- mPolicy = policy;
- mInitialBackoff = initialBackoff;
- mMaximumBackoff = maximumBackoff;
- }
-
- /**
- * Returns the backoff policy in place.
- */
- @RetryPolicy
- public int getPolicy() {
- return mPolicy;
- }
-
- /**
- * Returns the initial backoff (i.e. when # of failures == 1), in seconds.
- */
- public int getInitialBackoff() {
- return mInitialBackoff;
- }
-
- /**
- * Returns the maximum backoff duration in seconds.
- */
- public int getMaximumBackoff() {
- return mMaximumBackoff;
- }
-
- @IntDef({RETRY_POLICY_LINEAR, RETRY_POLICY_EXPONENTIAL})
- @Retention(RetentionPolicy.SOURCE)
- public @interface RetryPolicy {
- }
-
- /* package */ final static class Builder {
- private final ValidationEnforcer mValidator;
-
- Builder(ValidationEnforcer validator) {
- mValidator = validator;
- }
-
- public RetryStrategy build(@RetryPolicy int policy, int initialBackoff, int maxBackoff) {
- RetryStrategy rs = new RetryStrategy(policy, initialBackoff, maxBackoff);
- mValidator.ensureValid(rs);
- return rs;
- }
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/SimpleJobService.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/SimpleJobService.java
deleted file mode 100644
index 8cfbe5f4..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/SimpleJobService.java
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.os.AsyncTask;
-import android.support.annotation.CallSuper;
-import android.support.v4.util.SimpleArrayMap;
-
-/**
- * SimpleJobService provides a simple way of doing background work in a JobService.
- *
- * Users should override onRunJob and return one of the {@link JobResult} ints.
- */
-public abstract class SimpleJobService extends JobService {
- private final SimpleArrayMap runningJobs =
- new SimpleArrayMap<>();
-
- @CallSuper
- @Override
- public boolean onStartJob(JobParameters job) {
- AsyncJobTask async = new AsyncJobTask(this, job);
-
- synchronized (runningJobs) {
- runningJobs.put(job, async);
- }
-
- async.execute();
-
- return true; // more work to do
- }
-
- @CallSuper
- @Override
- public boolean onStopJob(JobParameters job) {
- synchronized (runningJobs) {
- AsyncJobTask async = runningJobs.remove(job);
- if (async != null) {
- async.cancel(true);
- return true;
- }
- }
-
- return false;
- }
-
- private void onJobFinished(JobParameters jobParameters, boolean b) {
- synchronized (runningJobs) {
- runningJobs.remove(jobParameters);
- }
-
- jobFinished(jobParameters, b);
- }
-
- @JobResult
- public abstract int onRunJob(JobParameters job);
-
- private static class AsyncJobTask extends AsyncTask {
- private final SimpleJobService jobService;
- private final JobParameters jobParameters;
-
- private AsyncJobTask(SimpleJobService jobService, JobParameters jobParameters) {
- this.jobService = jobService;
- this.jobParameters = jobParameters;
- }
-
- @Override
- protected Integer doInBackground(Void... params) {
- return jobService.onRunJob(jobParameters);
- }
-
- @Override
- protected void onPostExecute(Integer integer) {
- jobService.onJobFinished(jobParameters, integer == JobService.RESULT_FAIL_RETRY);
- }
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Trigger.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Trigger.java
deleted file mode 100644
index 3e01ae9c..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/Trigger.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.support.annotation.NonNull;
-import java.util.List;
-
-/**
- * Generally, a Trigger is an object that can answer the question, "is this job ready to run?"
- *
- * More specifically, a Trigger is an opaque, abstract class used to root the type hierarchy.
- */
-public final class Trigger {
-
- /**
- * Immediate is a Trigger that's immediately available. The Job will be run as soon as the
- * runtime constraints are satisfied.
- *
- * It is invalid to schedule an Immediate with a recurring Job.
- */
- public final static JobTrigger.ImmediateTrigger NOW = new JobTrigger.ImmediateTrigger();
-
- /**
- * Creates a new ExecutionWindow based on the provided time interval.
- *
- * @param windowStart The earliest time (in seconds) the job should be
- * considered eligible to run. Calculated from when the
- * job was scheduled (for new jobs) or last run (for
- * recurring jobs).
- * @param windowEnd The latest time (in seconds) the job should be run in
- * an ideal world. Calculated in the same way as
- * {@code windowStart}.
- * @throws IllegalArgumentException if the provided parameters are too
- * restrictive.
- */
- public static JobTrigger.ExecutionWindowTrigger executionWindow(int windowStart, int windowEnd) {
- if (windowStart < 0) {
- throw new IllegalArgumentException("Window start can't be less than 0");
- } else if (windowEnd < windowStart) {
- throw new IllegalArgumentException("Window end can't be less than window start");
- }
-
- return new JobTrigger.ExecutionWindowTrigger(windowStart, windowEnd);
- }
-
- /**
- * Creates a new ContentUriTrigger based on the provided list of {@link ObservedUri}.
- *
- * @param uris The list of URIs to observe. The trigger will be available if a piece of content,
- * corresponding to any of provided URIs, is updated.
- * @throws IllegalArgumentException if provided list of URIs is null or empty.
- */
- public static JobTrigger.ContentUriTrigger contentUriTrigger(@NonNull List uris) {
- if (uris == null || uris.isEmpty()) {
- throw new IllegalArgumentException("Uris must not be null or empty.");
- }
- return new JobTrigger.ContentUriTrigger(uris);
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/TriggerReason.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/TriggerReason.java
deleted file mode 100644
index 00fc053b..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/TriggerReason.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.net.Uri;
-import java.util.List;
-
-/** The class contains a summary of the events which caused the job to be executed. */
-public class TriggerReason {
- private final List mTriggeredContentUris;
-
- TriggerReason(List mTriggeredContentUris) {
- this.mTriggeredContentUris = mTriggeredContentUris;
- }
-
- public List getTriggeredContentUris() {
- return mTriggeredContentUris;
- }
-}
diff --git a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ValidationEnforcer.java b/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ValidationEnforcer.java
deleted file mode 100644
index d9cfbe02..00000000
--- a/jobdispatcher/src/main/java/com/firebase/jobdispatcher/ValidationEnforcer.java
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.text.TextUtils;
-import java.util.List;
-
-/**
- * Wraps a JobValidator and provides helpful validation utilities.
- */
-public class ValidationEnforcer implements JobValidator {
- private final JobValidator mValidator;
-
- public ValidationEnforcer(JobValidator validator) {
- mValidator = validator;
- }
-
- /**
- * {@inheritDoc}
- */
- @Nullable
- @Override
- public List validate(JobParameters job) {
- return mValidator.validate(job);
- }
-
- /**
- * {@inheritDoc}
- * @param trigger
- */
- @Nullable
- @Override
- public List validate(JobTrigger trigger) {
- return mValidator.validate(trigger);
- }
-
- /**
- * {@inheritDoc}
- */
- @Nullable
- @Override
- public List validate(RetryStrategy retryStrategy) {
- return mValidator.validate(retryStrategy);
- }
-
- /**
- * Indicates whether the provided JobParameters is valid.
- */
- public final boolean isValid(JobParameters job) {
- return validate(job) == null;
- }
-
- /**
- * Indicates whether the provided JobTrigger is valid.
- */
- public final boolean isValid(JobTrigger trigger) {
- return validate(trigger) == null;
- }
-
- /**
- * Indicates whether the provided RetryStrategy is valid.
- */
- public final boolean isValid(RetryStrategy retryStrategy) {
- return validate(retryStrategy) == null;
- }
-
- /**
- * Throws a RuntimeException if the provided JobParameters is invalid.
- *
- * @throws ValidationException
- */
- public final void ensureValid(JobParameters job) {
- ensureNoErrors(validate(job));
- }
-
- /**
- * Throws a RuntimeException if the provided JobTrigger is invalid.
- *
- * @throws ValidationException
- */
- public final void ensureValid(JobTrigger trigger) {
- ensureNoErrors(validate(trigger));
- }
-
- /**
- * Throws a RuntimeException if the provided RetryStrategy is
- * invalid.
- *
- * @throws ValidationException
- */
- public final void ensureValid(RetryStrategy retryStrategy) {
- ensureNoErrors(validate(retryStrategy));
- }
-
- private void ensureNoErrors(List errors) {
- if (errors != null) {
- throw new ValidationException("JobParameters is invalid", errors);
- }
- }
-
- /**
- * An Exception thrown when a validation error is encountered.
- */
- public final static class ValidationException extends RuntimeException {
- private final List mErrors;
-
- public ValidationException(String msg, @NonNull List errors) {
- super(msg + ": " + TextUtils.join("\n - ", errors));
- mErrors = errors;
- }
-
- public List getErrors() {
- return mErrors;
- }
- }
-}
diff --git a/jobdispatcher/src/test/java/android/net/http/AndroidHttpClient.java b/jobdispatcher/src/test/java/android/net/http/AndroidHttpClient.java
deleted file mode 100644
index 0720a56f..00000000
--- a/jobdispatcher/src/test/java/android/net/http/AndroidHttpClient.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package android.net.http;
-
-/**
- * Robolectric requires this class be available in the classpath, otherwise {@link
- * org.robolectric.Shadows#shadowOf(android.os.Looper)} fails. We don't use AndroidHttpClient, so
- * include a stub to make Robolectric happy.
- *
- * @see https://github.com/robolectric/robolectric/issues/1862
- */
-public class AndroidHttpClient {}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ConstraintTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ConstraintTest.java
deleted file mode 100644
index 808d7a3c..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ConstraintTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static org.junit.Assert.assertEquals;
-
-import android.text.TextUtils;
-import java.util.Arrays;
-import java.util.List;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23)
-public class ConstraintTest {
-
- /**
- * Just to get 100% coverage.
- */
- @Test
- public void testPrivateConstructor() throws Exception {
- TestUtil.assertHasSinglePrivateConstructor(Constraint.class);
- }
-
- @Test
- public void testCompactAndUnCompact() {
- for (List combo : TestUtil.getAllConstraintCombinations()) {
- int[] input = TestUtil.toIntArray(combo);
- Arrays.sort(input);
-
- int[] output = Constraint.uncompact(Constraint.compact(input));
- Arrays.sort(output);
-
- for (int i = 0; i < combo.size(); i++) {
- assertEquals("Combination = " + TextUtils.join(", ", combo),
- input[i],
- output[i]);
- }
-
- assertEquals("Expected length of two arrays to be the same",
- input.length,
- output.length);
- }
- }
-
- @Test
- public void compactNull() {
- assertEquals(0, Constraint.compact(null));
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ContentUriTriggerTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ContentUriTriggerTest.java
deleted file mode 100644
index 5ea200af..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ContentUriTriggerTest.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static junit.framework.Assert.assertEquals;
-
-import android.provider.ContactsContract;
-import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-/** Test for {@link ContentUriTrigger}. */
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 21)
-public class ContentUriTriggerTest {
-
- @Test(expected = IllegalArgumentException.class)
- public void constrains_null() throws Exception {
- Trigger.contentUriTrigger(null);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void constrains_emptyList() throws Exception {
- Trigger.contentUriTrigger(Collections.emptyList());
- }
-
- @Test
- public void constrains_valid() throws Exception {
- List uris = Arrays.asList(new ObservedUri(ContactsContract.AUTHORITY_URI, 0));
- ContentUriTrigger uriTrigger = Trigger.contentUriTrigger(uris);
- assertEquals(uris, uriTrigger.getUris());
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/DefaultJobValidatorTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/DefaultJobValidatorTest.java
deleted file mode 100644
index 2d526417..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/DefaultJobValidatorTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static java.util.Collections.singletonList;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.provider.ContactsContract;
-import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger;
-import com.firebase.jobdispatcher.ObservedUri.Flags;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23)
-public class DefaultJobValidatorTest {
-
- @Mock
- private Context mMockContext;
-
- private DefaultJobValidator mValidator;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- mValidator = new DefaultJobValidator(mMockContext);
- }
-
- @SuppressWarnings("WrongConstant")
- @Test
- public void testValidate_retryStrategy() throws Exception {
- Map> testCases = new HashMap<>();
- testCases.put(
- new RetryStrategy(0 /* bad policy */, 30, 3600),
- singletonList("Unknown retry policy provided"));
- testCases.put(
- new RetryStrategy(RetryStrategy.RETRY_POLICY_LINEAR, 15, 3600),
- singletonList("Initial backoff must be at least 30s"));
- testCases.put(
- new RetryStrategy(RetryStrategy.RETRY_POLICY_EXPONENTIAL, 15, 3600),
- singletonList("Initial backoff must be at least 30s"));
- testCases.put(
- new RetryStrategy(RetryStrategy.RETRY_POLICY_LINEAR, 30, 60),
- singletonList("Maximum backoff must be greater than 300s (5 minutes)"));
- testCases.put(
- new RetryStrategy(RetryStrategy.RETRY_POLICY_EXPONENTIAL, 30, 60),
- singletonList("Maximum backoff must be greater than 300s (5 minutes)"));
- testCases.put(
- new RetryStrategy(RetryStrategy.RETRY_POLICY_LINEAR, 301, 300),
- singletonList("Maximum backoff must be greater than or equal to initial backoff"));
- testCases.put(
- new RetryStrategy(RetryStrategy.RETRY_POLICY_EXPONENTIAL, 301, 300),
- singletonList("Maximum backoff must be greater than or equal to initial backoff"));
-
- for (Entry> testCase : testCases.entrySet()) {
- List validationErrors = mValidator.validate(testCase.getKey());
- assertNotNull("Expected validation errors, but got null", validationErrors);
-
- for (String expected : testCase.getValue()) {
- assertTrue(
- "Expected validation errors to contain \"" + expected + "\"",
- validationErrors.contains(expected));
- }
- }
- }
-
- @Test
- public void testValidate_trigger() throws Exception {
- Map testCases = new HashMap<>();
-
- testCases.put(Trigger.NOW, null);
- testCases.put(Trigger.executionWindow(0, 100), null);
- ContentUriTrigger contentUriTrigger =
- Trigger.contentUriTrigger(
- Arrays.asList(
- new ObservedUri(
- ContactsContract.AUTHORITY_URI, Flags.FLAG_NOTIFY_FOR_DESCENDANTS)));
- testCases.put(contentUriTrigger, null);
-
- for (Entry testCase : testCases.entrySet()) {
- List validationErrors = mValidator.validate(testCase.getKey());
- if (testCase.getValue() == null) {
- assertNull("Expected no validation errors for trigger", validationErrors);
- } else {
- assertTrue(
- "Expected validation errors to contain \"" + testCase.getValue() + "\"",
- validationErrors.contains(testCase.getValue()));
- }
- }
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExecutionDelegatorTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExecutionDelegatorTest.java
deleted file mode 100644
index a523c465..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExecutionDelegatorTest.java
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static com.firebase.jobdispatcher.TestUtil.getContentUriTrigger;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.support.annotation.NonNull;
-import com.firebase.jobdispatcher.JobService.JobResult;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
-@SuppressWarnings("WrongConstant")
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 21)
-public class ExecutionDelegatorTest {
-
- private Context mMockContext;
- private TestJobReceiver mReceiver;
- private ExecutionDelegator mExecutionDelegator;
-
- @Before
- public void setUp() {
- mMockContext = spy(RuntimeEnvironment.application);
- doReturn("com.example.foo").when(mMockContext).getPackageName();
-
- mReceiver = new TestJobReceiver();
- mExecutionDelegator = new ExecutionDelegator(mMockContext, mReceiver);
- }
-
- @Test
- public void testExecuteJob_sendsBroadcastWithJobAndMessage() throws Exception {
- for (JobInvocation input : TestUtil.getJobInvocationCombinations()) {
- verifyExecuteJob(input);
- }
- }
-
- private void verifyExecuteJob(JobInvocation input) throws Exception {
- reset(mMockContext);
- mReceiver.lastResult = -1;
-
- mReceiver.setLatch(new CountDownLatch(1));
-
- mExecutionDelegator.executeJob(input);
-
- final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class);
- final ArgumentCaptor connCaptor =
- ArgumentCaptor.forClass(ServiceConnection.class);
- verify(mMockContext).bindService(intentCaptor.capture(), connCaptor.capture(), anyInt());
-
- final Intent result = intentCaptor.getValue();
- // verify the intent was sent to the right place
- assertEquals(input.getService(), result.getComponent().getClassName());
- assertEquals(JobService.ACTION_EXECUTE, result.getAction());
-
- final ServiceConnection connection = connCaptor.getValue();
-
- ComponentName cname = mock(ComponentName.class);
- JobService.LocalBinder mockLocalBinder = mock(JobService.LocalBinder.class);
- final JobParameters[] out = new JobParameters[1];
-
- JobService mockJobService = new JobService() {
- @Override
- public boolean onStartJob(JobParameters job) {
- out[0] = job;
- return false;
- }
-
- @Override
- public boolean onStopJob(JobParameters job) {
- return false;
- }
- };
-
- when(mockLocalBinder.getService()).thenReturn(mockJobService);
-
- connection.onServiceConnected(cname, mockLocalBinder);
-
- TestUtil.assertJobsEqual(input, out[0]);
-
- // make sure the countdownlatch was decremented
- assertTrue(mReceiver.mLatch.await(1, TimeUnit.SECONDS));
-
- // verify the lastResult was set correctly
- assertEquals(JobService.RESULT_SUCCESS, mReceiver.lastResult);
- }
-
- @Test
- public void testExecuteJob_handlesNull() {
- assertFalse("Expected calling triggerExecution on null to fail and return false",
- mExecutionDelegator.executeJob(null));
- }
-
- @Test
- public void testHandleMessage_doesntCrashOnBadJobData() {
- JobInvocation j = new JobInvocation.Builder()
- .setService(TestJobService.class.getName())
- .setTag("tag")
- .setTrigger(Trigger.NOW)
- .build();
-
- mExecutionDelegator.executeJob(j);
-
- ArgumentCaptor intentCaptor =
- ArgumentCaptor.forClass(Intent.class);
- ArgumentCaptor connCaptor =
- ArgumentCaptor.forClass(ServiceConnection.class);
-
- //noinspection WrongConstant
- verify(mMockContext).bindService(intentCaptor.capture(), connCaptor.capture(), anyInt());
-
- Intent executeReq = intentCaptor.getValue();
- assertEquals(JobService.ACTION_EXECUTE, executeReq.getAction());
- }
-
- @Test
- public void onStop_mock() throws InterruptedException {
- JobInvocation job = new JobInvocation.Builder()
- .setTag("TAG")
- .setTrigger(getContentUriTrigger())
- .setService(TestJobService.class.getName())
- .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
- .build();
-
- reset(mMockContext);
- mReceiver.lastResult = -1;
-
- mExecutionDelegator.executeJob(job);
-
- final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class);
- final ArgumentCaptor connCaptor =
- ArgumentCaptor.forClass(ServiceConnection.class);
- verify(mMockContext).bindService(intentCaptor.capture(), connCaptor.capture(), anyInt());
-
- final Intent result = intentCaptor.getValue();
- // verify the intent was sent to the right place
- assertEquals(job.getService(), result.getComponent().getClassName());
- assertEquals(JobService.ACTION_EXECUTE, result.getAction());
-
- final JobParameters[] out = new JobParameters[2];
-
- JobService mockJobService = new JobService() {
- @Override
- public boolean onStartJob(JobParameters job) {
- out[0] = job;
- return true;
- }
-
- @Override
- public boolean onStopJob(JobParameters job) {
- out[1] = job;
- return false;
- }
- };
-
- JobService.LocalBinder mockLocalBinder = mock(JobService.LocalBinder.class);
- when(mockLocalBinder.getService()).thenReturn(mockJobService);
-
- ComponentName componentName = mock(ComponentName.class);
- final ServiceConnection connection = connCaptor.getValue();
- connection.onServiceConnected(componentName, mockLocalBinder);
-
- mExecutionDelegator.stopJob(job);
-
- TestUtil.assertJobsEqual(job, out[0]);
- TestUtil.assertJobsEqual(job, out[1]);
- }
-
- private final static class TestJobReceiver implements ExecutionDelegator.JobFinishedCallback {
- int lastResult;
-
- private CountDownLatch mLatch;
-
- @Override
- public void onJobFinished(@NonNull JobInvocation js, @JobResult int result) {
- lastResult = result;
-
- if (mLatch != null) {
- mLatch.countDown();
- }
- }
-
- /**
- * Convenience method for tests.
- */
- public void setLatch(CountDownLatch latch) {
- mLatch = latch;
- }
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExecutionWindowTriggerTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExecutionWindowTriggerTest.java
deleted file mode 100644
index 6dfe8577..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExecutionWindowTriggerTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23)
-public class ExecutionWindowTriggerTest {
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- @Test
- public void testNewInstance_withValidWindow() throws Exception {
- JobTrigger.ExecutionWindowTrigger trigger = Trigger.executionWindow(0, 60);
-
- assertEquals(0, trigger.getWindowStart());
- assertEquals(60, trigger.getWindowEnd());
- }
-
- @Test
- public void testNewInstance_withNegativeStart() throws Exception {
- expectedException.expect(IllegalArgumentException.class);
-
- Trigger.executionWindow(-10, 60);
- }
-
- @Test
- public void testNewInstance_withNegativeEnd() throws Exception {
- expectedException.expect(IllegalArgumentException.class);
-
- Trigger.executionWindow(0, -1);
- }
-
- @Test
- public void testNewInstance_withReversedValues() throws Exception {
- expectedException.expect(IllegalArgumentException.class);
-
- Trigger.executionWindow(60, 0);
- }
-
- @Test
- public void testNewInstance_withTooSmallWindow_now() throws Exception {
- expectedException.expect(IllegalArgumentException.class);
-
- Trigger.executionWindow(60, 59);
- }
-
- @Test
- public void testNewInstance_withTooSmallWindow_inFuture() throws Exception {
- expectedException.expect(IllegalArgumentException.class);
-
- Trigger.executionWindow(200, 100);
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExtendedShadowParcel.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExtendedShadowParcel.java
deleted file mode 100644
index e5ecf104..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ExtendedShadowParcel.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.firebase.jobdispatcher;
-
-import android.os.IBinder;
-import android.os.Parcel;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.RealObject;
-import org.robolectric.shadows.ShadowParcel;
-
-/**
- * ShadowParcel doesn't correctly handle {@link Parcel#writeStrongBinder(IBinder)} or {@link
- * Parcel#readStrongBinder()}, so we shim a simple implementation that uses an in-memory map to read
- * and write Binder objects.
- */
-@Implements(Parcel.class)
-public class ExtendedShadowParcel extends ShadowParcel {
- @RealObject private Parcel realObject;
-
- // Map each IBinder to an integer, and use the super's int-writing capability to fake Binder
- // read/writes.
- private final AtomicInteger nextBinderId = new AtomicInteger(1);
- private final Map binderMap =
- Collections.synchronizedMap(new HashMap());
-
- @Implementation
- public void writeStrongBinder(IBinder binder) {
- int id = nextBinderId.getAndIncrement();
- binderMap.put(id, binder);
- realObject.writeInt(id);
- }
-
- @Implementation
- public IBinder readStrongBinder() {
- return binderMap.get(realObject.readInt());
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/FirebaseJobDispatcherTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/FirebaseJobDispatcherTest.java
deleted file mode 100644
index dd0b22ee..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/FirebaseJobDispatcherTest.java
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import com.firebase.jobdispatcher.FirebaseJobDispatcher.ScheduleFailedException;
-import java.util.Arrays;
-import java.util.List;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23)
-public class FirebaseJobDispatcherTest {
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- @Mock
- private Driver mDriver;
-
- @Mock
- private JobValidator mValidator;
-
- private FirebaseJobDispatcher mDispatcher;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- when(mDriver.getValidator()).thenReturn(mValidator);
-
- mDispatcher = new FirebaseJobDispatcher(mDriver);
- setDriverAvailability(true);
- }
-
- @Test
- public void testSchedule_passThrough() throws Exception {
- final int[] possibleResults = {
- FirebaseJobDispatcher.SCHEDULE_RESULT_SUCCESS,
- FirebaseJobDispatcher.SCHEDULE_RESULT_NO_DRIVER_AVAILABLE,
- FirebaseJobDispatcher.SCHEDULE_RESULT_BAD_SERVICE,
- FirebaseJobDispatcher.SCHEDULE_RESULT_UNKNOWN_ERROR,
- FirebaseJobDispatcher.SCHEDULE_RESULT_UNSUPPORTED_TRIGGER};
-
- for (int result : possibleResults) {
- when(mDriver.schedule(null)).thenReturn(result);
- assertEquals(result, mDispatcher.schedule(null));
- }
-
- verify(mDriver, times(possibleResults.length)).schedule(null);
- }
-
- @Test
- public void testSchedule_unavailable() throws Exception {
- setDriverAvailability(false);
- assertEquals(
- FirebaseJobDispatcher.SCHEDULE_RESULT_NO_DRIVER_AVAILABLE,
- mDispatcher.schedule(null));
- verify(mDriver, never()).schedule(null);
- }
-
- @Test
- public void testCancelJob() throws Exception {
- final String tag = "foo";
-
- // simulate success
- when(mDriver.cancel(tag))
- .thenReturn(FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS);
-
- assertEquals(
- "Expected dispatcher to pass the result of Driver#cancel(String, Class) through",
- FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS,
- mDispatcher.cancel(tag));
-
- // verify the driver was indeed called
- verify(mDriver).cancel(tag);
- }
-
- @Test
- public void testCancelJob_unavailable() throws Exception {
- setDriverAvailability(false); // driver is unavailable
-
- assertEquals(
- FirebaseJobDispatcher.CANCEL_RESULT_NO_DRIVER_AVAILABLE,
- mDispatcher.cancel("foo"));
-
- // verify the driver was never even consulted
- verify(mDriver, never()).cancel("foo");
- }
-
- @Test
- public void testCancelAllJobs() throws Exception {
- when(mDriver.cancelAll()).thenReturn(FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS);
-
- assertEquals(FirebaseJobDispatcher.CANCEL_RESULT_SUCCESS, mDispatcher.cancelAll());
- verify(mDriver).cancelAll();
- }
-
- @Test
- public void testCancelAllJobs_unavailable() throws Exception {
- setDriverAvailability(false); // driver is unavailable
-
- assertEquals(
- FirebaseJobDispatcher.CANCEL_RESULT_NO_DRIVER_AVAILABLE,
- mDispatcher.cancelAll());
-
- verify(mDriver, never()).cancelAll();
- }
-
- @Test
- public void testMustSchedule_success() throws Exception {
- when(mDriver.schedule(null)).thenReturn(FirebaseJobDispatcher.SCHEDULE_RESULT_SUCCESS);
-
- /* assert no exception is thrown */
- mDispatcher.mustSchedule(null);
- }
-
- @Test
- public void testMustSchedule_unavailable() throws Exception {
- setDriverAvailability(false); // driver is unavailable
- expectedException.expect(FirebaseJobDispatcher.ScheduleFailedException.class);
-
- mDispatcher.mustSchedule(null);
- }
-
- @Test
- public void testMustSchedule_failure() throws Exception {
- final int[] possibleErrors = {
- FirebaseJobDispatcher.SCHEDULE_RESULT_NO_DRIVER_AVAILABLE,
- FirebaseJobDispatcher.SCHEDULE_RESULT_BAD_SERVICE,
- FirebaseJobDispatcher.SCHEDULE_RESULT_UNKNOWN_ERROR,
- FirebaseJobDispatcher.SCHEDULE_RESULT_UNSUPPORTED_TRIGGER};
-
- for (int scheduleError : possibleErrors) {
- when(mDriver.schedule(null)).thenReturn(scheduleError);
-
- try {
- mDispatcher.mustSchedule(null);
-
- fail("Expected mustSchedule() with error code " + scheduleError + " to fail");
- } catch (ScheduleFailedException expected) { /* expected */ }
- }
-
- verify(mDriver, times(possibleErrors.length)).schedule(null);
- }
-
- @Test
- public void testNewRetryStrategyBuilder() {
- // custom validator that only approves strategies where initialbackoff == 30s
- when(mValidator.validate(any(RetryStrategy.class))).thenAnswer(new Answer>() {
- @Override
- public List answer(InvocationOnMock invocation) throws Throwable {
- RetryStrategy rs = (RetryStrategy) invocation.getArguments()[0];
- // only succeed if initialBackoff == 30s
- return rs.getInitialBackoff() == 30 ? null : Arrays.asList("foo", "bar");
- }
- });
-
- try {
- mDispatcher.newRetryStrategy(RetryStrategy.RETRY_POLICY_EXPONENTIAL, 0, 30);
- fail("Expected initial backoff != 30s to fail using custom validator");
- } catch (Exception unused) { /* unused */ }
-
- try {
- mDispatcher.newRetryStrategy(RetryStrategy.RETRY_POLICY_EXPONENTIAL, 30, 30);
- } catch (Exception unused) {
- fail("Expected initial backoff == 30s not to fail using custom validator");
- }
- }
-
- public void setDriverAvailability(boolean driverAvailability) {
- when(mDriver.isAvailable()).thenReturn(driverAvailability);
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayCallbackExtractorTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayCallbackExtractorTest.java
deleted file mode 100644
index f6591850..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayCallbackExtractorTest.java
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Pair;
-import com.firebase.jobdispatcher.TestUtil.InspectableBinder;
-import com.firebase.jobdispatcher.TestUtil.TransactionArguments;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(
- constants = BuildConfig.class,
- manifest = Config.NONE,
- sdk = 23,
- shadows = {ExtendedShadowParcel.class}
-)
-public final class GooglePlayCallbackExtractorTest {
- @Mock
- private IBinder mBinder;
-
- private GooglePlayCallbackExtractor mExtractor;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mExtractor = new GooglePlayCallbackExtractor();
- }
-
- @Test
- public void testExtractCallback_nullBundle() {
- assertNull(mExtractor.extractCallback(null));
- }
-
- @Test
- public void testExtractCallback_nullParcelable() {
- Bundle emptyBundle = new Bundle();
- assertNull(extractCallback(emptyBundle));
- }
-
- @Test
- public void testExtractCallback_badParcelable() {
- Bundle misconfiguredBundle = new Bundle();
- misconfiguredBundle.putParcelable("callback", new BadParcelable(1));
-
- assertNull(extractCallback(misconfiguredBundle));
- }
-
- @Test
- public void testExtractCallback_goodParcelable() {
- InspectableBinder binder = new InspectableBinder();
- Bundle validBundle = new Bundle();
- validBundle.putParcelable("callback", binder.toPendingCallback());
-
- Pair extraction = extractCallback(validBundle);
- assertNotNull(extraction);
- assertEquals("should have stripped the 'callback' entry from the extracted bundle",
- 0, extraction.second.keySet().size());
- extraction.first.jobFinished(JobService.RESULT_SUCCESS);
-
- // Check our homemade Binder is doing the right things:
- TransactionArguments args = binder.getArguments().get(0);
- // Should have set the transaction code:
- assertEquals("transaction code", IBinder.FIRST_CALL_TRANSACTION + 1, args.code);
-
- // strong mode bit
- args.data.readInt();
- // interface token
- assertEquals("com.google.android.gms.gcm.INetworkTaskCallback", args.data.readString());
- // result
- assertEquals("result", JobService.RESULT_SUCCESS, args.data.readInt());
- }
-
- @Test
- public void testExtractCallback_extraMapValues() {
- Bundle validBundle = new Bundle();
- validBundle.putString("foo", "bar");
- validBundle.putInt("bar", 3);
- validBundle.putParcelable("parcelable", new Bundle());
- validBundle.putParcelable("callback", new InspectableBinder().toPendingCallback());
-
- Pair extraction = extractCallback(validBundle);
- assertNotNull(extraction);
- assertEquals("should have stripped the 'callback' entry from the extracted bundle",
- 3, extraction.second.keySet().size());
- }
-
- private Pair extractCallback(Bundle bundle) {
- return mExtractor.extractCallback(bundle);
- }
-
- private static final class BadParcelable implements Parcelable {
- public static final Parcelable.Creator CREATOR
- = new Parcelable.Creator() {
- @Override
- public BadParcelable createFromParcel(Parcel in) {
- return new BadParcelable(in);
- }
-
- @Override
- public BadParcelable[] newArray(int size) {
- return new BadParcelable[size];
- }
- };
- private final int mNum;
-
- public BadParcelable(int i) {
- mNum = i;
- }
-
- private BadParcelable(Parcel in) {
- mNum = in.readInt();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dst, int flags) {
- dst.writeInt(mNum);
- }
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayDriverTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayDriverTest.java
deleted file mode 100644
index 822a8f1d..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayDriverTest.java
+++ /dev/null
@@ -1,203 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.Parcelable;
-import android.support.annotation.NonNull;
-import android.text.TextUtils;
-import java.util.Arrays;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23)
-public class GooglePlayDriverTest {
- @Mock
- public Context mMockContext;
-
- private TestJobDriver mDriver;
- private FirebaseJobDispatcher mDispatcher;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mDriver = new TestJobDriver(new GooglePlayDriver(mMockContext));
- mDispatcher = new FirebaseJobDispatcher(mDriver);
-
- when(mMockContext.getPackageName()).thenReturn("foo.bar.whatever");
- }
-
- @Test
- public void testSchedule_failsWhenPlayServicesIsUnavailable() throws Exception {
- markBackendUnavailable();
- mockPackageManagerInfo();
-
- Job job = null;
- try {
- job = mDispatcher.newJobBuilder()
- .setService(TestJobService.class)
- .setTag("foobar")
- .setConstraints(Constraint.DEVICE_CHARGING)
- .setTrigger(Trigger.executionWindow(0, 60))
- .build();
- } catch (ValidationEnforcer.ValidationException ve) {
- fail(TextUtils.join("\n", ve.getErrors()));
- }
-
- assertEquals("Expected schedule() request to fail when backend is unavailable",
- FirebaseJobDispatcher.SCHEDULE_RESULT_NO_DRIVER_AVAILABLE,
- mDispatcher.schedule(job));
- }
-
- @Test
- public void testCancelJobs_backendUnavailable() throws Exception {
- markBackendUnavailable();
-
- assertEquals("Expected cancelAll() request to fail when backend is unavailable",
- FirebaseJobDispatcher.CANCEL_RESULT_NO_DRIVER_AVAILABLE,
- mDispatcher.cancelAll());
- }
-
- @Test
- public void testSchedule_sendsAppropriateBroadcast() {
- ArgumentCaptor pmQueryIntentCaptor = mockPackageManagerInfo();
-
- Job job = mDispatcher.newJobBuilder()
- .setConstraints(Constraint.DEVICE_CHARGING)
- .setService(TestJobService.class)
- .setTrigger(Trigger.executionWindow(0, 60))
- .setRecurring(false)
- .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
- .setTag("foobar")
- .build();
-
- Intent pmQueryIntent = pmQueryIntentCaptor.getValue();
- assertEquals(JobService.ACTION_EXECUTE, pmQueryIntent.getAction());
- assertEquals(TestJobService.class.getName(), pmQueryIntent.getComponent().getClassName());
-
- assertEquals("Expected schedule() request to succeed",
- FirebaseJobDispatcher.SCHEDULE_RESULT_SUCCESS,
- mDispatcher.schedule(job));
-
- final ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class);
- verify(mMockContext).sendBroadcast(captor.capture());
-
- Intent broadcast = captor.getValue();
-
- assertNotNull(broadcast);
- assertEquals("com.google.android.gms.gcm.ACTION_SCHEDULE", broadcast.getAction());
- assertEquals("SCHEDULE_TASK", broadcast.getStringExtra("scheduler_action"));
- assertEquals("com.google.android.gms", broadcast.getPackage());
- assertEquals(8, broadcast.getIntExtra("source", -1));
- assertEquals(1, broadcast.getIntExtra("source_version", -1));
-
- final Parcelable parcelablePendingIntent = broadcast.getParcelableExtra("app");
- assertTrue("Expected 'app' value to be a PendingIntent",
- parcelablePendingIntent instanceof PendingIntent);
- }
-
- private ArgumentCaptor mockPackageManagerInfo() {
- PackageManager packageManager = mock(PackageManager.class);
- when(mMockContext.getPackageManager()).thenReturn(packageManager);
- ArgumentCaptor intentArgCaptor = ArgumentCaptor.forClass(Intent.class);
-
- ResolveInfo info = new ResolveInfo();
- info.serviceInfo = new ServiceInfo();
- info.serviceInfo.enabled = true;
-
- //noinspection WrongConstant
- when(packageManager.queryIntentServices(intentArgCaptor.capture(), eq(0)))
- .thenReturn(Arrays.asList(info));
-
- return intentArgCaptor;
- }
-
- @Test
- public void testCancel_sendsAppropriateBroadcast() {
- mDispatcher.cancel("foobar");
-
- ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class);
- verify(mMockContext).sendBroadcast(captor.capture());
-
- Intent broadcast = captor.getValue();
-
- assertNotNull(broadcast);
- assertEquals("foobar", broadcast.getStringExtra("tag"));
- }
-
- private void markBackendUnavailable() {
- mDriver.available = false;
- }
-
- public final static class TestJobDriver implements Driver {
- public boolean available = true;
-
- private final Driver wrappedDriver;
-
- public TestJobDriver(Driver wrappedDriver) {
- this.wrappedDriver = wrappedDriver;
- }
-
- @Override
- public int schedule(@NonNull Job job) {
- return this.wrappedDriver.schedule(job);
- }
-
- @Override
- public int cancel(@NonNull String tag) {
- return this.wrappedDriver.cancel(tag);
- }
-
- @Override
- public int cancelAll() {
- return this.wrappedDriver.cancelAll();
- }
-
- @NonNull
- @Override
- public JobValidator getValidator() {
- return this.wrappedDriver.getValidator();
- }
-
- @Override
- public boolean isAvailable() {
- return available;
- }
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayJobWriterTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayJobWriterTest.java
deleted file mode 100644
index c56ab0d0..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayJobWriterTest.java
+++ /dev/null
@@ -1,268 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract;
-import com.firebase.jobdispatcher.Job.Builder;
-import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger;
-import com.firebase.jobdispatcher.ObservedUri.Flags;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23)
-public class GooglePlayJobWriterTest {
-
- private static final boolean[] ALL_BOOLEANS = {true, false};
-
- private GooglePlayJobWriter mWriter;
-
- private static Builder initializeDefaultBuilder() {
- return TestUtil.getBuilderWithNoopValidator()
- .setConstraints(Constraint.DEVICE_CHARGING)
- .setExtras(null)
- .setLifetime(Lifetime.FOREVER)
- .setRecurring(false)
- .setReplaceCurrent(false)
- .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
- .setService(TestJobService.class)
- .setTag("tag")
- .setTrigger(Trigger.NOW);
- }
-
- @Before
- public void setUp() throws Exception {
- mWriter = new GooglePlayJobWriter();
- }
-
- @Test
- public void testWriteToBundle_tags() {
- for (String tag : Arrays.asList("foo", "bar", "foobar", "this is a tag")) {
- Bundle b = mWriter.writeToBundle(
- initializeDefaultBuilder().setTag(tag).build(),
- new Bundle());
-
- assertEquals("tag", tag, b.getString("tag"));
- }
- }
-
- @Test
- public void testWriteToBundle_updateCurrent() {
- for (boolean replaceCurrent : ALL_BOOLEANS) {
- Bundle b = mWriter.writeToBundle(
- initializeDefaultBuilder().setReplaceCurrent(replaceCurrent).build(),
- new Bundle());
-
- assertEquals("update_current", replaceCurrent, b.getBoolean("update_current"));
- }
- }
-
- @Test
- public void testWriteToBundle_persisted() {
- Bundle b = mWriter.writeToBundle(
- initializeDefaultBuilder().setLifetime(Lifetime.FOREVER).build(),
- new Bundle());
-
- assertTrue("persisted", b.getBoolean("persisted"));
-
- for (int lifetime : new int[]{Lifetime.UNTIL_NEXT_BOOT}) {
- b = mWriter.writeToBundle(
- initializeDefaultBuilder().setLifetime(lifetime).build(),
- new Bundle());
-
- assertFalse("persisted", b.getBoolean("persisted"));
- }
- }
-
- @Test
- public void testWriteToBundle_service() {
- Bundle b = mWriter.writeToBundle(
- initializeDefaultBuilder().setService(TestJobService.class).build(),
- new Bundle());
-
- assertEquals("service", GooglePlayReceiver.class.getName(), b.getString("service"));
- }
-
- @Test
- public void testWriteToBundle_requiredNetwork() {
- Map mapping = new HashMap<>();
- mapping.put(Constraint.ON_ANY_NETWORK, GooglePlayJobWriter.LEGACY_NETWORK_CONNECTED);
- mapping.put(Constraint.ON_UNMETERED_NETWORK, GooglePlayJobWriter.LEGACY_NETWORK_UNMETERED);
- mapping.put(0, GooglePlayJobWriter.LEGACY_NETWORK_ANY);
-
- for (Entry testCase : mapping.entrySet()) {
- @SuppressWarnings("WrongConstant")
- Bundle b = mWriter.writeToBundle(
- initializeDefaultBuilder().setConstraints(testCase.getKey()).build(),
- new Bundle());
-
- assertEquals("requiredNetwork", (int) testCase.getValue(), b.getInt("requiredNetwork"));
- }
- }
-
- @Test
- public void testWriteToBundle_unmeteredConstraintShouldTakePrecendence() {
- Bundle b = mWriter.writeToBundle(
- initializeDefaultBuilder()
- .setConstraints(Constraint.ON_ANY_NETWORK, Constraint.ON_UNMETERED_NETWORK)
- .build(),
- new Bundle());
-
- assertEquals("expected ON_UNMETERED_NETWORK to take precendence over ON_ANY_NETWORK",
- GooglePlayJobWriter.LEGACY_NETWORK_UNMETERED, b.getInt("requiredNetwork"));
- }
-
- @Test
- public void testWriteToBundle_requiresCharging() {
- assertTrue("requiresCharging", mWriter.writeToBundle(
- initializeDefaultBuilder().setConstraints(Constraint.DEVICE_CHARGING).build(),
- new Bundle()).getBoolean("requiresCharging"));
-
- for (Integer constraint : Arrays.asList(
- Constraint.ON_ANY_NETWORK,
- Constraint.ON_UNMETERED_NETWORK)) {
-
- assertFalse("requiresCharging", mWriter.writeToBundle(
- initializeDefaultBuilder().setConstraints(constraint).build(),
- new Bundle()).getBoolean("requiresCharging"));
- }
- }
-
- @Test
- public void testWriteToBundle_requiresIdle() {
- assertTrue("requiresIdle", mWriter.writeToBundle(
- initializeDefaultBuilder().setConstraints(Constraint.DEVICE_IDLE).build(),
- new Bundle()).getBoolean("requiresIdle"));
-
- for (Integer constraint : Arrays.asList(
- Constraint.ON_ANY_NETWORK,
- Constraint.ON_UNMETERED_NETWORK)) {
-
- assertFalse("requiresIdle", mWriter.writeToBundle(
- initializeDefaultBuilder().setConstraints(constraint).build(),
- new Bundle()).getBoolean("requiresIdle"));
- }
- }
-
- @Test
- public void testWriteToBundle_retryPolicy() {
- assertEquals("retry_policy",
- GooglePlayJobWriter.LEGACY_RETRY_POLICY_EXPONENTIAL,
- mWriter.writeToBundle(
- initializeDefaultBuilder()
- .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
- .build(),
- new Bundle()).getBundle("retryStrategy").getInt("retry_policy"));
-
- assertEquals("retry_policy",
- GooglePlayJobWriter.LEGACY_RETRY_POLICY_LINEAR,
- mWriter.writeToBundle(
- initializeDefaultBuilder()
- .setRetryStrategy(RetryStrategy.DEFAULT_LINEAR)
- .build(),
- new Bundle()).getBundle("retryStrategy").getInt("retry_policy"));
- }
-
- @Test
- public void testWriteToBundle_backoffSeconds() {
- for (RetryStrategy retryStrategy : Arrays
- .asList(RetryStrategy.DEFAULT_EXPONENTIAL, RetryStrategy.DEFAULT_LINEAR)) {
-
- Bundle b = mWriter.writeToBundle(
- initializeDefaultBuilder().setRetryStrategy(retryStrategy).build(),
- new Bundle()).getBundle("retryStrategy");
-
- assertEquals("initial_backoff_seconds",
- retryStrategy.getInitialBackoff(),
- b.getInt("initial_backoff_seconds"));
-
- assertEquals("maximum_backoff_seconds",
- retryStrategy.getMaximumBackoff(),
- b.getInt("maximum_backoff_seconds"));
-
- }
- }
-
- @Test
- public void testWriteToBundle_triggers() {
- // immediate
- Bundle b = mWriter.writeToBundle(
- initializeDefaultBuilder().setTrigger(Trigger.NOW).build(),
- new Bundle());
-
- assertEquals("window_start", 0, b.getLong("window_start"));
- assertEquals("window_end", 30, b.getLong("window_end"));
-
- // execution window (oneoff)
- JobTrigger.ExecutionWindowTrigger t = Trigger.executionWindow(631, 978);
- b = mWriter.writeToBundle(
- initializeDefaultBuilder().setTrigger(t).build(),
- new Bundle());
-
- assertEquals("window_start", t.getWindowStart(), b.getLong("window_start"));
- assertEquals("window_end", t.getWindowEnd(), b.getLong("window_end"));
-
- // execution window (periodic)
- b = mWriter.writeToBundle(
- initializeDefaultBuilder().setRecurring(true).setTrigger(t).build(),
- new Bundle());
-
- assertEquals("period", t.getWindowEnd(), b.getLong("period"));
- assertEquals("period_flex", t.getWindowEnd() - t.getWindowStart(), b.getLong("period_flex"));
- }
-
- @Test
- public void testWriteToBundle_contentUriTrigger() {
- ObservedUri observedUri = new ObservedUri(ContactsContract.AUTHORITY_URI,
- Flags.FLAG_NOTIFY_FOR_DESCENDANTS);
- ContentUriTrigger contentUriTrigger = Trigger.contentUriTrigger(Arrays.asList(observedUri));
- Bundle bundle = mWriter.writeToBundle(
- initializeDefaultBuilder().setTrigger(contentUriTrigger).build(), new Bundle());
- Uri[] uris =
- (Uri[]) bundle.getParcelableArray(BundleProtocol.PACKED_PARAM_CONTENT_URI_ARRAY);
- int[] flags = bundle.getIntArray(BundleProtocol.PACKED_PARAM_CONTENT_URI_FLAGS_ARRAY);
- assertTrue("Array size", uris.length == flags.length && flags.length == 1);
- assertEquals(BundleProtocol.PACKED_PARAM_CONTENT_URI_ARRAY,
- ContactsContract.AUTHORITY_URI, uris[0]);
- assertEquals(BundleProtocol.PACKED_PARAM_CONTENT_URI_FLAGS_ARRAY,
- Flags.FLAG_NOTIFY_FOR_DESCENDANTS, flags[0]);
- }
-
- @Test
- public void testWriteToBundle_extras() {
- Bundle extras = new Bundle();
-
- Bundle result = mWriter.writeToBundle(
- initializeDefaultBuilder().setExtras(extras).build(),
- new Bundle());
-
- assertEquals("extras", extras, result.getBundle("extras"));
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayMessageHandlerTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayMessageHandlerTest.java
deleted file mode 100644
index 31969a16..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayMessageHandlerTest.java
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static com.firebase.jobdispatcher.GooglePlayJobWriter.REQUEST_PARAM_TAG;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import com.firebase.jobdispatcher.JobInvocation.Builder;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-/**
- * Tests {@link GooglePlayMessageHandler}.
- */
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 21)
-public class GooglePlayMessageHandlerTest {
-
- @Mock
- Looper looper;
- @Mock
- GooglePlayReceiver receiverMock;
- @Mock
- Context context;
- @Mock
- AppOpsManager appOpsManager;
- @Mock
- Messenger messengerMock;
- @Mock
- ExecutionDelegator executionDelegatorMock;
-
- GooglePlayMessageHandler handler;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- handler = new GooglePlayMessageHandler(looper, receiverMock);
- when(receiverMock.getExecutionDelegator()).thenReturn(executionDelegatorMock);
- when(receiverMock.getApplicationContext()).thenReturn(context);
- when(context.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(appOpsManager);
- }
-
- @Test
- public void handleMessage_nullNoException() throws Exception {
- handler.handleMessage(null);
- }
-
- @Test
- public void handleMessage_ignoreIfSenderIsNotGcm() throws Exception {
- Message message = Message.obtain();
- message.what = GooglePlayMessageHandler.MSG_START_EXEC;
- Bundle data = new Bundle();
- data.putString(REQUEST_PARAM_TAG, "TAG");
- message.setData(data);
- message.replyTo = messengerMock;
- doThrow(new SecurityException()).when(appOpsManager)
- .checkPackage(message.sendingUid, GooglePlayDriver.BACKEND_PACKAGE);
- handler.handleMessage(message);
- verify(receiverMock, never()).prepareJob(any(GooglePlayMessengerCallback.class), eq(data));
- }
-
- @Test
- public void handleMessage_startExecution_noData() throws Exception {
- Message message = Message.obtain();
- message.what = GooglePlayMessageHandler.MSG_START_EXEC;
- message.replyTo = messengerMock;
-
- handler.handleMessage(message);
- verify(receiverMock, never())
- .prepareJob(any(GooglePlayMessengerCallback.class), any(Bundle.class));
- }
-
- @Test
- public void handleMessage_startExecution() throws Exception {
- Message message = Message.obtain();
- message.what = GooglePlayMessageHandler.MSG_START_EXEC;
- Bundle data = new Bundle();
- data.putString(REQUEST_PARAM_TAG, "TAG");
- message.setData(data);
- message.replyTo = messengerMock;
- JobInvocation jobInvocation = new Builder()
- .setTag("tag")
- .setService(TestJobService.class.getName())
- .setTrigger(Trigger.NOW).build();
- when(receiverMock.prepareJob(any(GooglePlayMessengerCallback.class), eq(data)))
- .thenReturn(jobInvocation);
-
- handler.handleMessage(message);
-
- verify(executionDelegatorMock).executeJob(jobInvocation);
- }
-
- @Test
- public void handleMessage_stopExecution() throws Exception {
- Message message = Message.obtain();
- message.what = GooglePlayMessageHandler.MSG_STOP_EXEC;
- JobCoder jobCoder = GooglePlayReceiver.getJobCoder();
- Bundle data = TestUtil.encodeContentUriJob(TestUtil.getContentUriTrigger(), jobCoder);
- JobInvocation jobInvocation = jobCoder.decode(data).build();
- message.setData(data);
- message.replyTo = messengerMock;
-
- handler.handleMessage(message);
-
- final ArgumentCaptor captor = ArgumentCaptor.forClass(JobInvocation.class);
-
- verify(executionDelegatorMock).stopJob(captor.capture());
-
- TestUtil.assertJobsEqual(jobInvocation, captor.getValue());
- }
-
- @Test
- public void handleMessage_stopExecution_invalidNoCrash() throws Exception {
- Message message = Message.obtain();
- message.what = GooglePlayMessageHandler.MSG_STOP_EXEC;
- message.replyTo = messengerMock;
-
- handler.handleMessage(message);
-
- verify(executionDelegatorMock, never()).stopJob(any(JobInvocation.class));
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayMessengerCallbackTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayMessengerCallbackTest.java
deleted file mode 100644
index b71cf003..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayMessengerCallbackTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static com.firebase.jobdispatcher.GooglePlayJobWriter.REQUEST_PARAM_TAG;
-import static org.junit.Assert.assertEquals;
-
-import android.os.Message;
-import android.os.Messenger;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-/**
- * Tests {@link GooglePlayMessengerCallback}.
- */
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 21)
-public class GooglePlayMessengerCallbackTest {
-
- @Mock
- Messenger messengerMock;
- GooglePlayMessengerCallback callback;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- callback = new GooglePlayMessengerCallback(messengerMock, "tag");
- }
-
- @Test
- public void jobFinished() throws Exception {
- final ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class);
-
- callback.jobFinished(JobService.RESULT_SUCCESS);
-
- Mockito.verify(messengerMock).send(messageCaptor.capture());
- Message message = messageCaptor.getValue();
- assertEquals(message.what, GooglePlayMessageHandler.MSG_RESULT);
- assertEquals(message.arg1, JobService.RESULT_SUCCESS);
- assertEquals(message.getData().getString(REQUEST_PARAM_TAG), "tag");
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayReceiverTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayReceiverTest.java
deleted file mode 100644
index 47d7da57..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/GooglePlayReceiverTest.java
+++ /dev/null
@@ -1,327 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static com.firebase.jobdispatcher.TestUtil.encodeContentUriJob;
-import static com.firebase.jobdispatcher.TestUtil.encodeRecurringContentUriJob;
-import static com.firebase.jobdispatcher.TestUtil.getContentUriTrigger;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNull;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-import android.app.Service;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Build.VERSION_CODES;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Messenger;
-import android.os.Parcel;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Contacts;
-import android.provider.MediaStore.Images.Media;
-import android.support.annotation.NonNull;
-import com.firebase.jobdispatcher.GooglePlayReceiverTest.ShadowMessenger;
-import com.firebase.jobdispatcher.JobInvocation.Builder;
-import com.firebase.jobdispatcher.TestUtil.InspectableBinder;
-import com.google.android.gms.gcm.PendingCallback;
-import java.util.ArrayList;
-import java.util.Arrays;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implements;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(
- constants = BuildConfig.class,
- manifest = Config.NONE,
- sdk = 21,
- shadows = {ShadowMessenger.class}
-)
-public class GooglePlayReceiverTest {
-
- /**
- * The default ShadowMessenger implementation causes NPEs when using the
- * {@link Messenger#Messenger(Handler)} constructor. We create our own empty Shadow so we can
- * just use the standard Android implementation, which is totally fine.
- *
- * @see Robolectric issue
- *
- */
- @Implements(Messenger.class)
- public static class ShadowMessenger {}
-
- GooglePlayReceiver receiver;
-
- JobCoder jobCoder = new JobCoder(BundleProtocol.PACKED_PARAM_BUNDLE_PREFIX, true);
-
- @Mock
- Messenger messengerMock;
- @Mock
- IBinder binderMock;
- @Mock
- JobCallback callbackMock;
- @Mock
- ExecutionDelegator executionDelegatorMock;
- @Mock
- Driver driverMock;
- @Captor
- ArgumentCaptor jobArgumentCaptor;
-
- ArrayList triggeredUris = new ArrayList<>();
-
- {
- triggeredUris.add(ContactsContract.AUTHORITY_URI);
- triggeredUris.add(Media.EXTERNAL_CONTENT_URI);
- }
-
- Builder jobInvocationBuilder = new Builder()
- .setTag("tag")
- .setService(TestJobService.class.getName())
- .setTrigger(Trigger.NOW);
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- receiver = spy(new GooglePlayReceiver());
- when(receiver.getExecutionDelegator()).thenReturn(executionDelegatorMock);
- receiver.driver = driverMock;
- receiver.validationEnforcer = new ValidationEnforcer(new NoopJobValidator());
- }
-
- @Test
- public void onJobFinished_unknownJobCallbackIsNotPresent_ignoreNoException() {
- receiver.onJobFinished(jobInvocationBuilder.build(), JobService.RESULT_SUCCESS);
-
- verifyZeroInteractions(driverMock);
- }
-
- @Test
- public void onJobFinished_notRecurringContentJob_sendResult() {
- jobInvocationBuilder.setTrigger(
- Trigger.contentUriTrigger(Arrays.asList(new ObservedUri(Contacts.CONTENT_URI, 0))));
-
- JobInvocation jobInvocation = receiver
- .prepareJob(callbackMock, getBundleForContentJobExecution());
-
- receiver.onJobFinished(jobInvocation, JobService.RESULT_SUCCESS);
- verify(callbackMock).jobFinished(JobService.RESULT_SUCCESS);
- verifyZeroInteractions(driverMock);
- }
-
- @Test
- public void onJobFinished_successRecurringContentJob_reschedule() {
- JobInvocation jobInvocation = receiver
- .prepareJob(callbackMock, getBundleForContentJobExecutionRecurring());
-
- receiver.onJobFinished(jobInvocation, JobService.RESULT_SUCCESS);
-
- verify(driverMock).schedule(jobArgumentCaptor.capture());
-
- // No need to callback when job finished.
- // Reschedule request is treated as two events: completion of old job and scheduling of new
- // job with the same parameters.
- verifyZeroInteractions(callbackMock);
-
- Job rescheduledJob = jobArgumentCaptor.getValue();
- TestUtil.assertJobsEqual(jobInvocation, rescheduledJob);
- }
-
- @Test
- public void onJobFinished_failWithRetryRecurringContentJob_sendResult() {
- JobInvocation jobInvocation = receiver
- .prepareJob(callbackMock, getBundleForContentJobExecutionRecurring());
-
- receiver.onJobFinished(jobInvocation, JobService.RESULT_FAIL_RETRY);
-
- // If a job finishes with RESULT_FAIL_RETRY we don't need to send a reschedule request.
- // Rescheduling will erase previously triggered URIs.
- verify(callbackMock).jobFinished(JobService.RESULT_FAIL_RETRY);
-
- verifyZeroInteractions(driverMock);
- }
-
- @Test
- public void prepareJob() {
- Intent intent = new Intent();
-
- Bundle encode = encodeContentUriJob(getContentUriTrigger(), jobCoder);
- intent.putExtra(GooglePlayJobWriter.REQUEST_PARAM_EXTRAS, encode);
-
- Parcel container = Parcel.obtain();
- container.writeStrongBinder(new Binder());
- PendingCallback pcb = new PendingCallback(container);
- intent.putExtra("callback", pcb);
-
- ArrayList uris = new ArrayList<>();
- uris.add(ContactsContract.AUTHORITY_URI);
- uris.add(Media.EXTERNAL_CONTENT_URI);
- intent.putParcelableArrayListExtra(BundleProtocol.PACKED_PARAM_TRIGGERED_URIS, uris);
-
- JobInvocation jobInvocation = receiver.prepareJob(intent);
- assertEquals(jobInvocation.getTriggerReason().getTriggeredContentUris(), uris);
- }
-
- @Test
- public void prepareJob_messenger() {
- JobInvocation jobInvocation = receiver.prepareJob(callbackMock, new Bundle());
- assertNull(jobInvocation);
- verify(callbackMock).jobFinished(JobService.RESULT_FAIL_NORETRY);
- }
-
- @Test
- public void prepareJob_messenger_noExtras() {
- Bundle bundle = getBundleForContentJobExecution();
-
- JobInvocation jobInvocation = receiver.prepareJob(callbackMock, bundle);
- assertEquals(jobInvocation.getTriggerReason().getTriggeredContentUris(), triggeredUris);
- }
-
- @NonNull
- private Bundle getBundleForContentJobExecution() {
- Bundle bundle = new Bundle();
-
- Bundle encode = encodeContentUriJob(getContentUriTrigger(), jobCoder);
- bundle.putBundle(GooglePlayJobWriter.REQUEST_PARAM_EXTRAS, encode);
-
- bundle.putParcelableArrayList(BundleProtocol.PACKED_PARAM_TRIGGERED_URIS, triggeredUris);
- return bundle;
- }
-
- @NonNull
- private Bundle getBundleForContentJobExecutionRecurring() {
- Bundle bundle = new Bundle();
-
- Bundle encode = encodeRecurringContentUriJob(getContentUriTrigger(), jobCoder);
- bundle.putBundle(GooglePlayJobWriter.REQUEST_PARAM_EXTRAS, encode);
-
- bundle.putParcelableArrayList(BundleProtocol.PACKED_PARAM_TRIGGERED_URIS, triggeredUris);
- return bundle;
- }
-
- @Test
- public void onBind() {
- Intent intent = new Intent(GooglePlayReceiver.ACTION_EXECUTE);
- IBinder binderA = receiver.onBind(intent);
- IBinder binderB = receiver.onBind(intent);
-
- assertEquals(binderA, binderB);
- }
-
- @Test
- public void onBind_nullIntent() {
- IBinder binder = receiver.onBind(null);
- assertNull(binder);
- }
-
- @Test
- public void onBind_wrongAction() {
- Intent intent = new Intent("test");
- IBinder binder = receiver.onBind(intent);
- assertNull(binder);
- }
-
- @Test
- @Config(sdk = VERSION_CODES.KITKAT)
- public void onBind_wrongBuild() {
- Intent intent = new Intent(GooglePlayReceiver.ACTION_EXECUTE);
- IBinder binder = receiver.onBind(intent);
- assertNull(binder);
- }
-
- @Test
- public void onStartCommand_nullIntent() {
- assertResultWasStartNotSticky(receiver.onStartCommand(null, 0, 101));
- verify(receiver).stopSelf(101);
- }
-
- @Test
- public void onStartCommand_initAction() {
- Intent initIntent = new Intent("com.google.android.gms.gcm.SERVICE_ACTION_INITIALIZE");
- assertResultWasStartNotSticky(receiver.onStartCommand(initIntent, 0, 101));
- verify(receiver).stopSelf(101);
- }
-
- @Test
- public void onStartCommand_unknownAction() {
- Intent unknownIntent = new Intent("com.example.foo.bar");
- assertResultWasStartNotSticky(receiver.onStartCommand(unknownIntent, 0, 101));
- assertResultWasStartNotSticky(receiver.onStartCommand(unknownIntent, 0, 102));
- assertResultWasStartNotSticky(receiver.onStartCommand(unknownIntent, 0, 103));
-
- InOrder inOrder = inOrder(receiver);
- inOrder.verify(receiver).stopSelf(101);
- inOrder.verify(receiver).stopSelf(102);
- inOrder.verify(receiver).stopSelf(103);
- }
-
- @Test
- public void onStartCommand_executeActionWithEmptyExtras() {
- Intent execIntent = new Intent("com.google.android.gms.gcm.ACTION_TASK_READY");
- assertResultWasStartNotSticky(receiver.onStartCommand(execIntent, 0, 101));
- verify(receiver).stopSelf(101);
- }
-
- @Test
- public void onStartCommand_executeAction() {
- JobInvocation job = new JobInvocation.Builder()
- .setTag("tag")
- .setService("com.example.foo.FooService")
- .setTrigger(Trigger.NOW)
- .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
- .setLifetime(Lifetime.UNTIL_NEXT_BOOT)
- .setConstraints(new int[]{Constraint.DEVICE_IDLE})
- .build();
-
- Intent execIntent = new Intent("com.google.android.gms.gcm.ACTION_TASK_READY")
- .putExtra("extras", new JobCoder(BundleProtocol.PACKED_PARAM_BUNDLE_PREFIX, true)
- .encode(job, new Bundle()))
- .putExtra("callback", new InspectableBinder().toPendingCallback());
-
- when(executionDelegatorMock.executeJob(any(JobInvocation.class))).thenReturn(true);
-
- assertResultWasStartNotSticky(receiver.onStartCommand(execIntent, 0, 101));
-
- verify(receiver, never()).stopSelf(anyInt());
- verify(executionDelegatorMock).executeJob(any(JobInvocation.class));
-
- receiver.onJobFinished(job, JobService.RESULT_SUCCESS);
-
- verify(receiver).stopSelf(101);
- }
-
- private void assertResultWasStartNotSticky(int result) {
- assertEquals(
- "Result for onStartCommand wasn't START_NOT_STICKY", Service.START_NOT_STICKY, result);
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ImmediateTriggerTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ImmediateTriggerTest.java
deleted file mode 100644
index 1cf57ee6..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ImmediateTriggerTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23)
-public class ImmediateTriggerTest {
- /**
- * Code coverage.
- */
- @Test
- public void testPrivateConstructor() throws Exception {
- TestUtil.assertHasSinglePrivateConstructor(JobTrigger.ImmediateTrigger.class);
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobBuilderTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobBuilderTest.java
deleted file mode 100644
index ffb4026e..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobBuilderTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23)
-public class JobBuilderTest {
- private static final int[] ALL_LIFETIMES = {Lifetime.UNTIL_NEXT_BOOT, Lifetime.FOREVER};
-
- private Job.Builder mBuilder;
-
- @Before
- public void setUp() throws Exception {
- mBuilder = TestUtil.getBuilderWithNoopValidator();
- }
-
- @Test
- public void testAddConstraints() {
- mBuilder.setConstraints()
- .addConstraint(Constraint.DEVICE_CHARGING)
- .addConstraint(Constraint.ON_UNMETERED_NETWORK);
-
- int[] expected = {Constraint.DEVICE_CHARGING, Constraint.ON_UNMETERED_NETWORK};
-
- assertEquals(Constraint.compact(expected), Constraint.compact(mBuilder.getConstraints()));
- }
-
- @Test
- public void testSetLifetime() {
- for (int lifetime : ALL_LIFETIMES) {
- mBuilder.setLifetime(lifetime);
- assertEquals(lifetime, mBuilder.getLifetime());
- }
- }
-
- @Test
- public void testSetShouldReplaceCurrent() {
- for (boolean replace : new boolean[]{true, false}) {
- mBuilder.setReplaceCurrent(replace);
- assertEquals(replace, mBuilder.shouldReplaceCurrent());
- }
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobCoderTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobCoderTest.java
deleted file mode 100644
index bd3ebfb9..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobCoderTest.java
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static com.firebase.jobdispatcher.TestUtil.encodeContentUriJob;
-import static com.firebase.jobdispatcher.TestUtil.getContentUriTrigger;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract;
-import android.provider.MediaStore.Images.Media;
-import com.firebase.jobdispatcher.Job.Builder;
-import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger;
-import java.util.ArrayList;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23)
-public class JobCoderTest {
- private final JobCoder mCoder = new JobCoder(PREFIX, true);
- private static final String PREFIX = "prefix";
- private Builder mBuilder;
-
- private static Builder setValidBuilderDefaults(Builder mBuilder) {
- return mBuilder
- .setTag("tag")
- .setTrigger(Trigger.NOW)
- .setService(TestJobService.class)
- .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL);
- }
-
- @Before
- public void setUp() throws Exception {
- mBuilder = TestUtil.getBuilderWithNoopValidator();
- }
-
- @Test
- public void testCodingIsLossless() {
- for (JobParameters input : TestUtil.getJobCombinations(mBuilder)) {
-
- JobParameters output = mCoder.decode(mCoder.encode(input, input.getExtras())).build();
-
- TestUtil.assertJobsEqual(input, output);
- }
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testEncode_throwsOnNullBundle() {
- mCoder.encode(mBuilder.build(), null);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testDecode_throwsOnNullBundle() {
- mCoder.decode(null);
- }
-
- @Test
- public void testDecode_failsWhenMissingFields() {
- assertNull("Expected null tag to cause decoding to fail",
- mCoder.decode(mCoder.encode(
- setValidBuilderDefaults(mBuilder).setTag(null).build(),
- new Bundle())));
-
- assertNull("Expected null service to cause decoding to fail",
- mCoder.decode(mCoder.encode(
- setValidBuilderDefaults(mBuilder).setService(null).build(),
- new Bundle())));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testDecode_failsUnsupportedTrigger() {
- mCoder.decode(mCoder.encode(setValidBuilderDefaults(mBuilder).setTrigger(null).build(),
- new Bundle()));
- }
-
- @Test
- public void testDecode_ignoresMissingRetryStrategy() {
- assertNotNull("Expected null retry strategy to cause decode to use a default",
- mCoder.decode(mCoder.encode(
- setValidBuilderDefaults(mBuilder).setRetryStrategy(null).build(),
- new Bundle())));
- }
-
- @Test
- public void encode_contentUriTrigger() {
- Bundle encode = TestUtil.encodeContentUriJob(TestUtil.getContentUriTrigger(), mCoder);
- int triggerType = encode.getInt(PREFIX + BundleProtocol.PACKED_PARAM_TRIGGER_TYPE);
- assertEquals("Trigger type", BundleProtocol.TRIGGER_TYPE_CONTENT_URI, triggerType);
-
- String json = encode.getString(PREFIX + BundleProtocol.PACKED_PARAM_OBSERVED_URI);
- String expectedJson = "{\"uri_flags\":[1,0],\"uris\":[\"content:\\/\\/com.android.contacts"
- + "\",\"content:\\/\\/media\\/external\\/images\\/media\"]}";
- assertEquals("Json trigger", expectedJson, json);
- }
-
- @Test
- public void decode_contentUriTrigger() {
- ContentUriTrigger contentUriTrigger = TestUtil.getContentUriTrigger();
- Bundle bundle = TestUtil.encodeContentUriJob(contentUriTrigger, mCoder);
- JobInvocation decode = mCoder.decode(bundle).build();
- ContentUriTrigger trigger = (ContentUriTrigger) decode.getTrigger();
- assertEquals(contentUriTrigger.getUris(), trigger.getUris());
- }
-
- @Test
- public void decode_addBundleAsExtras() {
- ContentUriTrigger contentUriTrigger = TestUtil.getContentUriTrigger();
- Bundle bundle = TestUtil.encodeContentUriJob(contentUriTrigger, mCoder);
- bundle.putString("test_key", "test_value");
- JobInvocation decode = mCoder.decode(bundle).build();
- assertEquals("test_value", decode.getExtras().getString("test_key"));
- }
-
- @Test
- public void decodeIntentBundle() {
- Bundle bundle = new Bundle();
-
- ContentUriTrigger uriTrigger = getContentUriTrigger();
- Bundle encode = encodeContentUriJob(uriTrigger, mCoder);
- bundle.putBundle(GooglePlayJobWriter.REQUEST_PARAM_EXTRAS, encode);
-
- ArrayList uris = new ArrayList<>();
- uris.add(ContactsContract.AUTHORITY_URI);
- uris.add(Media.EXTERNAL_CONTENT_URI);
- bundle.putParcelableArrayList(BundleProtocol.PACKED_PARAM_TRIGGERED_URIS, uris);
-
- JobInvocation jobInvocation = mCoder.decodeIntentBundle(bundle);
-
- assertEquals(uris, jobInvocation.getTriggerReason().getTriggeredContentUris());
- assertEquals("TAG", jobInvocation.getTag());
- assertEquals(uriTrigger.getUris(), ((ContentUriTrigger) jobInvocation.getTrigger())
- .getUris());
- assertEquals(TestJobService.class.getName(), jobInvocation.getService());
- assertEquals(RetryStrategy.DEFAULT_EXPONENTIAL.getPolicy(),
- jobInvocation.getRetryStrategy().getPolicy());
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobInvocationTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobInvocationTest.java
deleted file mode 100644
index 769dd19d..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobInvocationTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.os.Bundle;
-import com.firebase.jobdispatcher.JobInvocation.Builder;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23)
-public class JobInvocationTest {
- private Builder builder;
-
- @Before
- public void setUp() {
- builder = new Builder()
- .setTag("tag")
- .setService(TestJobService.class.getName())
- .setTrigger(Trigger.NOW);
- }
-
- @SuppressWarnings("ConstantConditions")
- @Test
- public void testShouldReplaceCurrent() throws Exception {
- assertTrue("Expected shouldReplaceCurrent() to return value passed in constructor",
- builder.setReplaceCurrent(true).build().shouldReplaceCurrent());
- assertFalse("Expected shouldReplaceCurrent() to return value passed in constructor",
- builder.setReplaceCurrent(false).build().shouldReplaceCurrent());
- }
-
- @Test
- public void extras() throws Exception {
- assertNotNull(builder.build().getExtras());
-
- Bundle bundle = new Bundle();
- bundle.putLong("test", 1L);
- Bundle extras = builder.addExtras(bundle).build().getExtras();
- assertEquals(1, extras.size());
- assertEquals(1L, extras.getLong("test"));
- }
-
- @Test
- public void contract_hashCode_equals() {
- JobInvocation jobInvocation = builder.build();
- assertEquals(jobInvocation, builder.build());
- assertEquals(jobInvocation.hashCode(), builder.build().hashCode());
- JobInvocation jobInvocationNew = builder.setTag("new").build();
- assertNotEquals(jobInvocation, jobInvocationNew);
- assertNotEquals(jobInvocation.hashCode(), jobInvocationNew.hashCode());
- }
-
- @Test
- public void contract_hashCode_equals_triggerShouldBeIgnored() {
- JobInvocation jobInvocation = builder.build();
- JobInvocation periodic = builder.setTrigger(Trigger.executionWindow(0, 1)).build();
- assertEquals(jobInvocation, periodic);
- assertEquals(jobInvocation.hashCode(), periodic.hashCode());
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobServiceConnectionTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobServiceConnectionTest.java
deleted file mode 100644
index 8a8be89f..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobServiceConnectionTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.os.IBinder;
-import android.os.Message;
-import com.firebase.jobdispatcher.JobInvocation.Builder;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-/**
- * Test for {@link JobServiceConnection}.
- */
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23)
-public class JobServiceConnectionTest {
-
- JobInvocation job = new Builder()
- .setTag("tag")
- .setService(TestJobService.class.getName())
- .setTrigger(Trigger.NOW)
- .build();
-
- @Mock
- Message messageMock;
- @Mock
- JobService.LocalBinder binderMock;
- @Mock
- JobService jobServiceMock;
-
- JobServiceConnection connection;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(binderMock.getService()).thenReturn(jobServiceMock);
- connection = new JobServiceConnection(job, messageMock);
- }
-
- @Test
- public void fullConnectionCycle() {
- assertFalse(connection.isBound());
-
- connection.onServiceConnected(null, binderMock);
- verify(jobServiceMock).start(job, messageMock);
- assertTrue(connection.isBound());
-
- connection.onStop();
- verify(jobServiceMock).stop(job);
- assertTrue(connection.isBound());
-
- connection.onServiceDisconnected(null);
- assertFalse(connection.isBound());
- }
-
- @Test
- public void onServiceConnected_shouldNotSendExecutionRequestTwice() {
- assertFalse(connection.isBound());
-
- connection.onServiceConnected(null, binderMock);
- verify(jobServiceMock).start(job, messageMock);
- assertTrue(connection.isBound());
- reset(jobServiceMock);
-
- connection.onServiceConnected(null, binderMock);
- verify(jobServiceMock, never()).start(job, messageMock); // start should not be called again
-
- connection.onStop();
- verify(jobServiceMock).stop(job);
- assertTrue(connection.isBound());
-
- connection.onServiceDisconnected(null);
- assertFalse(connection.isBound());
- }
-
- @Test
- public void stopOnUnboundConnection() {
- assertFalse(connection.isBound());
- connection.onStop();
- verify(jobServiceMock, never()).onStopJob(job);
- }
-
- @Test
- public void onServiceConnectedWrongBinder() {
- IBinder binder = mock(IBinder.class);
- connection.onServiceConnected(null, binder);
- assertFalse(connection.isBound());
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobServiceTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobServiceTest.java
deleted file mode 100644
index 83c7134f..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/JobServiceTest.java
+++ /dev/null
@@ -1,346 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
-import android.content.Intent;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Parcel;
-import com.firebase.jobdispatcher.JobInvocation.Builder;
-import com.google.android.gms.gcm.PendingCallback;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23)
-public class JobServiceTest {
- private static CountDownLatch countDownLatch;
-
- @Before
- public void setUp() throws Exception {}
-
- @After
- public void tearDown() throws Exception {
- countDownLatch = null;
- }
-
- @Test
- public void testOnStartCommand_handlesNullIntent() throws Exception {
- JobService service = spy(new ExampleJobService());
- int startId = 7;
-
- try {
- service.onStartCommand(null, 0, startId);
-
- verify(service).stopSelf(startId);
- } catch (NullPointerException npe) {
- fail("Unexpected NullPointerException after calling onStartCommand with a null Intent.");
- }
- }
-
- @Test
- public void testOnStartCommand_handlesNullAction() throws Exception {
- JobService service = spy(new ExampleJobService());
- int startId = 7;
-
- Intent nullActionIntent = new Intent();
- service.onStartCommand(nullActionIntent, 0, startId);
-
- verify(service).stopSelf(startId);
- }
-
- @Test
- public void testOnStartCommand_handlesEmptyAction() throws Exception {
- JobService service = spy(new ExampleJobService());
- int startId = 7;
-
- Intent emptyActionIntent = new Intent("");
- service.onStartCommand(emptyActionIntent, 0, startId);
-
- verify(service).stopSelf(startId);
- }
-
- @Test
- public void testOnStartCommand_handlesUnknownAction() throws Exception {
- JobService service = spy(new ExampleJobService());
- int startId = 7;
-
- Intent emptyActionIntent = new Intent("foo.bar.baz");
- service.onStartCommand(emptyActionIntent, 0, startId);
-
- verify(service).stopSelf(startId);
- }
-
- @Test
- public void testOnStartCommand_handlesStartJob_nullData() {
- JobService service = spy(new ExampleJobService());
- int startId = 7;
-
- Intent executeJobIntent = new Intent(JobService.ACTION_EXECUTE);
- service.onStartCommand(executeJobIntent, 0, startId);
-
- verify(service).stopSelf(startId);
- }
-
- @Test
- public void testOnStartCommand_handlesStartJob_noTag() {
- JobService service = spy(new ExampleJobService());
- int startId = 7;
-
- Intent executeJobIntent = new Intent(JobService.ACTION_EXECUTE);
- Parcel p = Parcel.obtain();
- p.writeStrongBinder(mock(IBinder.class));
- executeJobIntent.putExtra("callback", new PendingCallback(p));
-
- service.onStartCommand(executeJobIntent, 0, startId);
-
- verify(service).stopSelf(startId);
-
- p.recycle();
- }
-
- @Test
- public void testOnStartCommand_handlesStartJob_noCallback() {
- JobService service = spy(new ExampleJobService());
- int startId = 7;
-
- Intent executeJobIntent = new Intent(JobService.ACTION_EXECUTE);
- executeJobIntent.putExtra("tag", "foobar");
-
- service.onStartCommand(executeJobIntent, 0, startId);
-
- verify(service).stopSelf(startId);
- }
-
- @Test
- public void testOnStartCommand_handlesStartJob_validRequest() throws InterruptedException {
- JobService service = spy(new ExampleJobService());
-
- HandlerThread ht = new HandlerThread("handler");
- ht.start();
- Handler h = new Handler(ht.getLooper());
-
- Intent executeJobIntent = new Intent(JobService.ACTION_EXECUTE);
-
- Job jobSpec = TestUtil.getBuilderWithNoopValidator()
- .setTag("tag")
- .setService(ExampleJobService.class)
- .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
- .setTrigger(Trigger.NOW)
- .setLifetime(Lifetime.FOREVER)
- .build();
-
- countDownLatch = new CountDownLatch(1);
-
- ((JobService.LocalBinder) service.onBind(executeJobIntent))
- .getService()
- .start(jobSpec, h.obtainMessage(ExecutionDelegator.JOB_FINISHED, jobSpec));
-
- assertTrue("Expected job to run to completion", countDownLatch.await(5, TimeUnit.SECONDS));
- }
-
- @Test
- public void testOnStartCommand_handlesStartJob_doNotStartRunningJobAgain() {
- StoppableJobService service = new StoppableJobService(false);
-
- Job jobSpec = TestUtil.getBuilderWithNoopValidator()
- .setTag("tag")
- .setService(StoppableJobService.class)
- .setTrigger(Trigger.NOW)
- .build();
-
- ((JobService.LocalBinder) service.onBind(null)).getService().start(jobSpec, null);
- ((JobService.LocalBinder) service.onBind(null)).getService().start(jobSpec, null);
-
- assertEquals(1, service.getNumberOfExecutionRequestsReceived());
- }
-
- @Test
- public void stop_noCallback_finished() {
- JobService service = spy(new StoppableJobService(false));
- JobInvocation job = new Builder()
- .setTag("Tag")
- .setTrigger(Trigger.NOW)
- .setService(StoppableJobService.class.getName())
- .build();
- service.stop(job);
- verify(service, never()).onStopJob(job);
- }
-
- @Test
- public void stop_withCallback_retry() {
- JobService service = spy(new StoppableJobService(false));
-
- JobInvocation job = new Builder()
- .setTag("Tag")
- .setTrigger(Trigger.NOW)
- .setService(StoppableJobService.class.getName())
- .build();
-
- Handler handlerMock = mock(Handler.class);
- Message message = Message.obtain(handlerMock);
- service.start(job, message);
-
- service.stop(job);
- verify(service).onStopJob(job);
- verify(handlerMock).sendMessage(message);
- assertEquals(message.arg1, JobService.RESULT_SUCCESS);
- }
-
- @Test
- public void stop_withCallback_done() {
- JobService service = spy(new StoppableJobService(true));
-
- JobInvocation job = new Builder()
- .setTag("Tag")
- .setTrigger(Trigger.NOW)
- .setService(StoppableJobService.class.getName())
- .build();
-
- Handler handlerMock = mock(Handler.class);
- Message message = Message.obtain(handlerMock);
- service.start(job, message);
-
- service.stop(job);
- verify(service).onStopJob(job);
- verify(handlerMock).sendMessage(message);
- assertEquals(message.arg1, JobService.RESULT_FAIL_RETRY);
- }
-
- @Test
- public void onStartJob_jobFinishedReschedule() {
- // Verify that a retry request from within onStartJob will cause the retry result to be sent
- // to the bouncer service's handler, regardless of what value is ultimately returned from
- // onStartJob.
- JobService reschedulingService = new JobService() {
- @Override
- public boolean onStartJob(JobParameters job) {
- // Reschedules job.
- jobFinished(job, true /* retry this job */);
- return false;
- }
-
- @Override
- public boolean onStopJob(JobParameters job) {
- return false;
- }
- };
-
- Job jobSpec = TestUtil.getBuilderWithNoopValidator()
- .setTag("tag")
- .setService(reschedulingService.getClass())
- .setTrigger(Trigger.NOW)
- .build();
- Handler mock = mock(Handler.class);
- Message message = new Message();
- message.setTarget(mock);
- reschedulingService.start(jobSpec, message);
-
- verify(mock).sendMessage(message);
- assertEquals(message.arg1, JobService.RESULT_FAIL_RETRY);
- }
-
- @Test
- public void onStartJob_jobFinishedNotReschedule() {
- // Verify that a termination request from within onStartJob will cause the result to be sent
- // to the bouncer service's handler, regardless of what value is ultimately returned from
- // onStartJob.
- JobService reschedulingService = new JobService() {
- @Override
- public boolean onStartJob(JobParameters job) {
- jobFinished(job, false /* don't retry this job */);
- return false;
- }
-
- @Override
- public boolean onStopJob(JobParameters job) {
- return false;
- }
- };
-
- Job jobSpec = TestUtil.getBuilderWithNoopValidator()
- .setTag("tag")
- .setService(reschedulingService.getClass())
- .setTrigger(Trigger.NOW)
- .build();
- Handler mock = mock(Handler.class);
- Message message = new Message();
- message.setTarget(mock);
- reschedulingService.start(jobSpec, message);
-
- verify(mock).sendMessage(message);
- assertEquals(message.arg1, JobService.RESULT_SUCCESS);
- }
-
- public static class ExampleJobService extends JobService {
- @Override
- public boolean onStartJob(JobParameters job) {
- countDownLatch.countDown();
- return false;
- }
-
- @Override
- public boolean onStopJob(JobParameters job) {
- return false;
- }
- }
-
- public static class StoppableJobService extends JobService {
-
- private final boolean shouldReschedule;
-
- public int getNumberOfExecutionRequestsReceived() {
- return amountOfExecutionRequestReceived.get();
- }
-
- private final AtomicInteger amountOfExecutionRequestReceived = new AtomicInteger();
-
- public StoppableJobService(boolean shouldReschedule) {
- this.shouldReschedule = shouldReschedule;
- }
-
- @Override
- public boolean onStartJob(JobParameters job) {
- amountOfExecutionRequestReceived.incrementAndGet();
- return true;
- }
-
- @Override
- public boolean onStopJob(JobParameters job) {
- return shouldReschedule;
- }
-
-
- }
-}
diff --git a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ValidationEnforcerTest.java b/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ValidationEnforcerTest.java
deleted file mode 100644
index 2e4e939a..00000000
--- a/jobdispatcher/src/test/java/com/firebase/jobdispatcher/ValidationEnforcerTest.java
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Collections;
-import java.util.List;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23)
-public class ValidationEnforcerTest {
- private static final List ERROR_LIST = Collections.singletonList("error: foo");
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- @Mock
- private JobValidator mValidator;
-
- @Mock
- private JobParameters mMockJobParameters;
-
- @Mock
- private JobTrigger mMockTrigger;
-
- private ValidationEnforcer mEnforcer;
- private RetryStrategy mRetryStrategy = RetryStrategy.DEFAULT_EXPONENTIAL;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- mEnforcer = new ValidationEnforcer(mValidator);
- }
-
- @Test
- public void testValidate_retryStrategy() throws Exception {
- mEnforcer.validate(mRetryStrategy);
- verify(mValidator).validate(mRetryStrategy);
- }
-
- @Test
- public void testValidate_jobSpec() throws Exception {
- mEnforcer.validate(mMockJobParameters);
- verify(mValidator).validate(mMockJobParameters);
- }
-
- @Test
- public void testValidate_trigger() throws Exception {
- mEnforcer.validate(mMockTrigger);
- verify(mValidator).validate(mMockTrigger);
- }
-
- @Test
- public void testIsValid_retryStrategy_invalid() throws Exception {
- when(mValidator.validate(mRetryStrategy))
- .thenReturn(Collections.singletonList("error: foo"));
-
- assertFalse("isValid", mEnforcer.isValid(mRetryStrategy));
- }
-
- @Test
- public void testIsValid_retryStrategy_valid() throws Exception {
- when(mValidator.validate(mRetryStrategy)).thenReturn(null);
-
- assertTrue("isValid", mEnforcer.isValid(mRetryStrategy));
-
- }
-
- @Test
- public void testIsValid_trigger_invalid() throws Exception {
- when(mValidator.validate(mMockTrigger))
- .thenReturn(Collections.singletonList("error: foo"));
-
- assertFalse("isValid", mEnforcer.isValid(mMockTrigger));
- }
-
- @Test
- public void testIsValid_trigger_valid() throws Exception {
- when(mValidator.validate(mMockTrigger)).thenReturn(null);
-
- assertTrue("isValid", mEnforcer.isValid(mMockTrigger));
- }
-
- @Test
- public void testIsValid_jobSpec_invalid() throws Exception {
- when(mValidator.validate(mMockJobParameters)).thenReturn(ERROR_LIST);
-
- assertFalse("isValid", mEnforcer.isValid(mMockJobParameters));
- }
-
- @Test
- public void testIsValid_jobSpec_valid() throws Exception {
- when(mValidator.validate(mMockJobParameters)).thenReturn(null);
-
- assertTrue("isValid", mEnforcer.isValid(mMockJobParameters));
- }
-
- @Test
- public void testEnsureValid_retryStrategy_valid() throws Exception {
- when(mValidator.validate(mRetryStrategy)).thenReturn(null);
-
- mEnforcer.ensureValid(mRetryStrategy);
- }
-
- @Test
- public void testEnsureValid_trigger_valid() throws Exception {
- when(mValidator.validate(mMockTrigger)).thenReturn(null);
-
- mEnforcer.ensureValid(mMockTrigger);
- }
-
- @Test
- public void testEnsureValid_jobSpec_valid() throws Exception {
- when(mValidator.validate(mMockJobParameters)).thenReturn(null);
-
- mEnforcer.ensureValid(mMockJobParameters);
- }
-
- @Test
- public void testEnsureValid_retryStrategy_invalid() throws Exception {
- expectedException.expect(ValidationEnforcer.ValidationException.class);
-
- when(mValidator.validate(mRetryStrategy)).thenReturn(ERROR_LIST);
- mEnforcer.ensureValid(mRetryStrategy);
- }
-
- @Test
- public void testEnsureValid_trigger_invalid() throws Exception {
- expectedException.expect(ValidationEnforcer.ValidationException.class);
-
- when(mValidator.validate(mMockTrigger)).thenReturn(ERROR_LIST);
- mEnforcer.ensureValid(mMockTrigger);
- }
-
- @Test
- public void testEnsureValid_jobSpec_invalid() throws Exception {
- expectedException.expect(ValidationEnforcer.ValidationException.class);
-
- when(mValidator.validate(mMockJobParameters)).thenReturn(ERROR_LIST);
- mEnforcer.ensureValid(mMockJobParameters);
- }
-
- @Test
- public void testValidationMessages() throws Exception {
- when(mValidator.validate(mMockJobParameters)).thenReturn(ERROR_LIST);
-
- try {
- mEnforcer.ensureValid(mMockJobParameters);
-
- fail("Expected ensureValid to fail");
- } catch (ValidationEnforcer.ValidationException ve) {
- assertEquals("Expected ValidationException to have 1 error message",
- 1,
- ve.getErrors().size());
- }
- }
-}
diff --git a/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/NoopJobValidator.java b/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/NoopJobValidator.java
deleted file mode 100644
index 079a6eed..00000000
--- a/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/NoopJobValidator.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import android.support.annotation.Nullable;
-import java.util.List;
-
-/**
- * A very simple Validator that thinks that everything is ok. Used for testing.
- */
-class NoopJobValidator implements JobValidator {
-
- @Nullable
- @Override
- public List validate(JobParameters job) {
- return null;
- }
-
- @Nullable
- @Override
- public List validate(JobTrigger trigger) {
- return null;
- }
-
- @Nullable
- @Override
- public List validate(RetryStrategy retryStrategy) {
- return null;
- }
-}
diff --git a/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/TestJobService.java b/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/TestJobService.java
deleted file mode 100644
index 77b3604a..00000000
--- a/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/TestJobService.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-/** A very simple JobService that can be configured for individual tests. */
-public class TestJobService extends JobService {
-
- public interface JobServiceProxy {
- boolean onStartJob(JobParameters job);
-
- boolean onStopJob(JobParameters job);
- }
-
- public static final JobServiceProxy NOOP_PROXY =
- new JobServiceProxy() {
- @Override
- public boolean onStartJob(JobParameters job) {
- return false;
- }
-
- @Override
- public boolean onStopJob(JobParameters job) {
- return false;
- }
- };
-
- private static final Object lock = new Object();
-
- // GuardedBy("lock")
- private static JobServiceProxy currentProxy = NOOP_PROXY;
-
- public static void setProxy(JobServiceProxy proxy) {
- synchronized (lock) {
- currentProxy = proxy;
- }
- }
-
- public static void reset() {
- synchronized (lock) {
- currentProxy = NOOP_PROXY;
- }
- }
-
- @Override
- public boolean onStartJob(JobParameters job) {
- synchronized (lock) {
- return currentProxy.onStartJob(job);
- }
- }
-
- @Override
- public boolean onStopJob(JobParameters job) {
- synchronized (lock) {
- return currentProxy.onStopJob(job);
- }
- }
-}
diff --git a/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/TestUtil.java b/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/TestUtil.java
deleted file mode 100644
index 85f9fadc..00000000
--- a/jobdispatcher/src/testLib/java/com/firebase/jobdispatcher/TestUtil.java
+++ /dev/null
@@ -1,375 +0,0 @@
-// Copyright 2016 Google, Inc.
-//
-// 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.firebase.jobdispatcher;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.provider.ContactsContract;
-import android.provider.MediaStore.Images.Media;
-import android.support.annotation.NonNull;
-import com.firebase.jobdispatcher.Job.Builder;
-import com.firebase.jobdispatcher.JobTrigger.ContentUriTrigger;
-import com.firebase.jobdispatcher.ObservedUri.Flags;
-import com.google.android.gms.gcm.PendingCallback;
-import java.lang.reflect.Constructor;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Provides common utilities helpful for testing.
- */
-public class TestUtil {
-
- private static final String TAG = "TAG";
- private static final String[] TAG_COMBINATIONS = {"tag", "foobar", "fooooooo", "bz", "100"};
-
- private static final int[] LIFETIME_COMBINATIONS = {
- Lifetime.UNTIL_NEXT_BOOT,
- Lifetime.FOREVER};
-
- private static final JobTrigger[] TRIGGER_COMBINATIONS = {
- Trigger.executionWindow(0, 30),
- Trigger.executionWindow(300, 600),
- Trigger.executionWindow(86400, 86400 * 2),
- Trigger.NOW,
- Trigger.contentUriTrigger(
- Arrays.asList(new ObservedUri(ContactsContract.AUTHORITY_URI, 0))),
- Trigger.contentUriTrigger(Arrays.asList(new ObservedUri(ContactsContract.AUTHORITY_URI, 0),
- new ObservedUri(ContactsContract.AUTHORITY_URI, Flags.FLAG_NOTIFY_FOR_DESCENDANTS)))
- };
-
- @SuppressWarnings("unchecked")
- private static final List> SERVICE_COMBINATIONS =
- Arrays.asList(TestJobService.class);
-
- private static final RetryStrategy[] RETRY_STRATEGY_COMBINATIONS = {
- RetryStrategy.DEFAULT_LINEAR,
- new RetryStrategy(RetryStrategy.RETRY_POLICY_LINEAR, 60, 300),
- RetryStrategy.DEFAULT_EXPONENTIAL,
- new RetryStrategy(RetryStrategy.RETRY_POLICY_EXPONENTIAL, 300, 600),
- };
-
- public static void assertHasSinglePrivateConstructor(Class> cls) throws Exception {
- Constructor>[] constructors = cls.getDeclaredConstructors();
- assertEquals("expected number of constructors to be == 1", 1, constructors.length);
-
- Constructor> constructor = constructors[0];
- assertFalse("expected constructor to be inaccessible", constructor.isAccessible());
-
- constructor.setAccessible(true);
- constructor.newInstance();
- }
-
- static List> getAllConstraintCombinations() {
- List> combos = new LinkedList<>();
-
- combos.add(Collections.emptyList());
- for (Integer cur : Constraint.ALL_CONSTRAINTS) {
- for (int l = combos.size() - 1; l >= 0; l--) {
- List oldCombo = combos.get(l);
- List newCombo = Arrays.asList(new Integer[oldCombo.size() + 1]);
-
- Collections.copy(newCombo, oldCombo);
- newCombo.set(oldCombo.size(), cur);
- combos.add(newCombo);
- }
- combos.add(Collections.singletonList(cur));
- }
-
- return combos;
- }
-
- static int[] toIntArray(List list) {
- int[] input = new int[list.size()];
- for (int i = 0; i < list.size(); i++) {
- input[i] = list.get(i);
- }
- return input;
- }
-
- static List getJobCombinations(Builder builder) {
- return getCombination(new JobBuilder(builder));
- }
-
- static List getJobInvocationCombinations() {
- return getCombination(new JobInvocationBuilder());
- }
-
- private static List getCombination(
- JobParameterBuilder buildJobParam) {
-
- List result = new ArrayList<>();
- for (String tag : TAG_COMBINATIONS) {
- for (List constraintList : getAllConstraintCombinations()) {
- for (boolean recurring : new boolean[]{true, false}) {
- for (boolean replaceCurrent : new boolean[]{true, false}) {
- for (int lifetime : LIFETIME_COMBINATIONS) {
- for (JobTrigger trigger : TRIGGER_COMBINATIONS) {
- for (Class service : SERVICE_COMBINATIONS) {
- for (Bundle extras : getBundleCombinations()) {
- for (RetryStrategy rs : RETRY_STRATEGY_COMBINATIONS) {
- result.add(buildJobParam.build(
- tag,
- replaceCurrent,
- constraintList,
- recurring,
- lifetime,
- trigger,
- service,
- extras,
- rs));
- }
- }
- }
- }
- }
- }
- }
- }
- }
- return result;
- }
-
- private static Bundle[] getBundleCombinations() {
- List bundles = new LinkedList<>();
- bundles.add(new Bundle());
-
- Bundle b = new Bundle();
- b.putString("foo", "bar");
- b.putInt("bar", 1);
- b.putLong("baz", 3L);
- bundles.add(b);
-
- return bundles.toArray(new Bundle[bundles.size()]);
- }
-
- static void assertJobsEqual(JobParameters input, JobParameters output) {
- assertNotNull("input", input);
- assertNotNull("output", output);
-
- assertEquals("isRecurring()", input.isRecurring(), output.isRecurring());
- assertEquals("shouldReplaceCurrent()",
- input.shouldReplaceCurrent(),
- output.shouldReplaceCurrent());
- assertEquals("getLifetime()", input.getLifetime(), output.getLifetime());
- assertEquals("getTag()", input.getTag(), output.getTag());
- assertEquals("getService()", input.getService(), output.getService());
- assertEquals("getConstraints()",
- Constraint.compact(input.getConstraints()),
- Constraint.compact(output.getConstraints()));
-
- assertTriggersEqual(input.getTrigger(), output.getTrigger());
- assertBundlesEqual(input.getExtras(), output.getExtras());
- assertRetryStrategiesEqual(input.getRetryStrategy(), output.getRetryStrategy());
- }
-
- static void assertRetryStrategiesEqual(RetryStrategy in, RetryStrategy out) {
- String prefix = "getRetryStrategy().";
-
- assertEquals(prefix + "getPolicy()",
- in.getPolicy(), out.getPolicy());
- assertEquals(prefix + "getInitialBackoff()",
- in.getInitialBackoff(), out.getInitialBackoff());
- assertEquals(prefix + "getMaximumBackoff()",
- in.getMaximumBackoff(), out.getMaximumBackoff());
- }
-
- static void assertBundlesEqual(Bundle inExtras, Bundle outExtras) {
- if (inExtras == null || outExtras == null) {
- assertNull(inExtras);
- assertNull(outExtras);
- return;
- }
-
- assertEquals("getExtras().size()", inExtras.size(), outExtras.size());
- final Set inKeys = inExtras.keySet();
- for (String key : inKeys) {
- assertTrue("getExtras().containsKey(\"" + key + "\")", outExtras.containsKey(key));
- assertEquals("getExtras().get(\"" + key + "\")", inExtras.get(key), outExtras.get(key));
- }
- }
-
- static void assertTriggersEqual(JobTrigger inTrigger, JobTrigger outTrigger) {
- assertEquals("", inTrigger.getClass(), outTrigger.getClass());
-
- if (inTrigger instanceof JobTrigger.ExecutionWindowTrigger) {
- assertEquals("getTrigger().getWindowStart()",
- ((JobTrigger.ExecutionWindowTrigger) inTrigger).getWindowStart(),
- ((JobTrigger.ExecutionWindowTrigger) outTrigger).getWindowStart());
- assertEquals("getTrigger().getWindowEnd()",
- ((JobTrigger.ExecutionWindowTrigger) inTrigger).getWindowEnd(),
- ((JobTrigger.ExecutionWindowTrigger) outTrigger).getWindowEnd());
- } else if (inTrigger == Trigger.NOW) {
- assertEquals(inTrigger, outTrigger);
- } else if (inTrigger instanceof JobTrigger.ContentUriTrigger) {
- assertEquals("Collection of URIs",
- ((ContentUriTrigger) inTrigger).getUris(),
- ((ContentUriTrigger) outTrigger).getUris());
- } else {
- fail("Unknown Trigger class: " + inTrigger.getClass());
- }
- }
-
- @NonNull
- public static Builder getBuilderWithNoopValidator() {
- return new Builder(new ValidationEnforcer(new NoopJobValidator()));
- }
-
- @NonNull
- static Bundle encodeContentUriJob(ContentUriTrigger trigger, JobCoder coder) {
- Job job = getBuilderWithNoopValidator()
- .setTag(TAG)
- .setTrigger(trigger)
- .setService(TestJobService.class)
- .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
- .build();
- return coder.encode(job, new Bundle());
- }
-
- @NonNull
- static Bundle encodeRecurringContentUriJob(ContentUriTrigger trigger, JobCoder coder) {
- Job job = getBuilderWithNoopValidator()
- .setTag(TAG)
- .setTrigger(trigger)
- .setService(TestJobService.class)
- .setReplaceCurrent(true)
- .setRecurring(true)
- .build();
- return coder.encode(job, new Bundle());
- }
-
- static ContentUriTrigger getContentUriTrigger() {
- ObservedUri contactUri = new ObservedUri(
- ContactsContract.AUTHORITY_URI, Flags.FLAG_NOTIFY_FOR_DESCENDANTS);
- ObservedUri imageUri = new ObservedUri(Media.EXTERNAL_CONTENT_URI, 0);
- return Trigger.contentUriTrigger(Arrays.asList(contactUri, imageUri));
- }
-
- public static class TransactionArguments {
- public final int code;
- public final Parcel data;
- public final int flags;
-
- public TransactionArguments(int code, Parcel data, int flags) {
- this.code = code;
- this.data = data;
- this.flags = flags;
- }
- }
-
- public static class InspectableBinder extends Binder {
- private final List transactionArguments = new LinkedList<>();
-
- public InspectableBinder() {}
-
- @Override
- protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
- transactionArguments.add(new TransactionArguments(code, copyParcel(data), flags));
- return true;
- }
-
- public PendingCallback toPendingCallback() {
- Parcel container = Parcel.obtain();
- try {
- container.writeStrongBinder(this);
- container.setDataPosition(0);
- return new PendingCallback(container);
- } finally {
- container.recycle();
- }
- }
-
- private Parcel copyParcel(Parcel data) {
- Parcel clone = Parcel.obtain();
- clone.appendFrom(data, 0, data.dataSize());
- clone.setDataPosition(0);
- return clone;
- }
-
- public List getArguments() {
- return Collections.unmodifiableList(transactionArguments);
- }
- }
-
- private static class JobInvocationBuilder implements
- JobParameterBuilder {
-
- @Override
- public JobInvocation build(String tag, boolean replaceCurrent, List constraintList,
- boolean recurring, int lifetime, JobTrigger trigger, Class