Skip to content

Commit 29d333c

Browse files
authored
Merge pull request #4 from Trumeet/canary
Canary
2 parents c7a4de8 + 295b7f0 commit 29d333c

File tree

9 files changed

+225
-28
lines changed

9 files changed

+225
-28
lines changed

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,16 @@ If your push (message) is not received or display an error, you can feedback [he
3131
Don't forget to attach your logs by sharing logs zip (Main → Menu → Share logs) and your steps.
3232

3333
# Licenses
34-
GPL v3.0
34+
## The license for this project
35+
GPL v3.0
36+
## Licenses for third-party resources
37+
Licenses of libraries are used in Android client is attached into the app, you can go to Main Menu Open Source Licenses to view them.
38+
39+
Some icons and pictures comes from [icons8.com](https://icons8.com/license), which are free to use for Open Source (Established projects should get the icons for free.)
40+
41+
Licenses of libraries are used in the server:
42+
43+
* Vertx - Eclipse Public License 2.0 and Apache License 2.0
44+
* JUnit - EPL 1.0
45+
* Mockito - MIT
46+
* Power Mockito - Apache 2.0

app/build.gradle

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,6 @@ dependencies {
9191
testImplementation 'junit:junit:4.12'
9292
androidTestImplementation 'androidx.test:runner:1.1.1'
9393
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
94-
implementation 'com.elvishew:xlog:1.6.1'
95-
implementation project(':common')
96-
implementation 'moe.shizuku.preference:preference:3.0.0'
97-
implementation 'moe.shizuku.preference:preference-dialog-android:3.0.0'
98-
implementation 'moe.shizuku.preference:preference-simplemenu:3.0.0'
9994
implementation 'com.google.android.material:material:1.1.0-alpha02'
10095
def nav_version = "1.0.0-alpha08"
10196
implementation "android.arch.navigation:navigation-fragment:$nav_version"
@@ -107,4 +102,14 @@ dependencies {
107102
implementation 'com.google.android.gms:play-services-oss-licenses:16.0.1'
108103
// Fabric
109104
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8'
105+
// Stream support for Java8-
106+
implementation 'com.annimon:stream:1.2.1'
107+
// Preferences
108+
implementation 'moe.shizuku.preference:preference:3.0.0'
109+
implementation 'moe.shizuku.preference:preference-dialog-android:3.0.0'
110+
implementation 'moe.shizuku.preference:preference-simplemenu:3.0.0'
111+
// Logger
112+
implementation 'com.elvishew:xlog:1.6.1'
113+
// Common
114+
implementation project(':common')
110115
}

app/src/main/java/moe/yuuta/mipushtester/topic/TopicSubscriptionFragment.java

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,25 @@
55
import android.view.View;
66
import android.view.ViewGroup;
77

8+
import com.annimon.stream.Collectors;
9+
import com.annimon.stream.Stream;
810
import com.elvishew.xlog.Logger;
911
import com.elvishew.xlog.XLog;
10-
import com.google.android.material.snackbar.Snackbar;
1112
import com.xiaomi.mipush.sdk.MiPushClient;
1213

1314
import java.util.List;
14-
import java.util.stream.Collectors;
1515

1616
import androidx.annotation.NonNull;
1717
import androidx.annotation.Nullable;
18+
import androidx.core.content.ContextCompat;
19+
import androidx.databinding.DataBindingUtil;
1820
import androidx.fragment.app.Fragment;
1921
import androidx.recyclerview.widget.DiffUtil;
2022
import androidx.recyclerview.widget.RecyclerView;
2123
import moe.yuuta.mipushtester.R;
2224
import moe.yuuta.mipushtester.api.APIManager;
25+
import moe.yuuta.mipushtester.databinding.FragmentTopicSubscriptionBinding;
26+
import moe.yuuta.mipushtester.widgets.multi_state.State;
2327
import retrofit2.Call;
2428
import retrofit2.Callback;
2529
import retrofit2.Response;
@@ -29,6 +33,7 @@ public class TopicSubscriptionFragment extends Fragment {
2933

3034
private TopicListAdapter mAdapter;
3135
private Call<List<Topic>> mGetTopicListCall;
36+
private State mLoadingState;
3237

3338
@Override
3439
public void onCreate(@Nullable Bundle savedInstanceState) {
@@ -47,20 +52,34 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
4752
@Nullable
4853
@Override
4954
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
50-
final View view = inflater.inflate(R.layout.fragment_topic_subscription, container, false);
51-
RecyclerView recyclerView = view.findViewById(R.id.recycler_topic);
55+
final FragmentTopicSubscriptionBinding binding =
56+
DataBindingUtil.inflate(inflater, R.layout.fragment_topic_subscription, container, false);
57+
RecyclerView recyclerView = binding.recyclerTopic;
5258
recyclerView.setAdapter(mAdapter);
53-
return view;
59+
mLoadingState = new State();
60+
mLoadingState.onRetryListener = (v -> call());
61+
binding.setState(mLoadingState);
62+
return binding.getRoot();
5463
}
5564

5665
@Override
5766
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
5867
super.onViewCreated(view, savedInstanceState);
68+
call();
69+
}
70+
71+
private void call () {
72+
if (mGetTopicListCall != null) {
73+
mGetTopicListCall.cancel();
74+
mGetTopicListCall = null;
75+
}
5976
mGetTopicListCall = APIManager.getInstance().getAvailableTopics();
77+
mLoadingState.showProgress();
6078
mGetTopicListCall.enqueue(new Callback<List<Topic>>() {
6179
@Override
6280
public void onResponse(@NonNull Call<List<Topic>> call, @NonNull Response<List<Topic>> response) {
6381
if (call.isCanceled()) return;
82+
mLoadingState.hideProgress();
6483
if (!response.isSuccessful()) {
6584
onFailure(call, new Exception("Unsuccessful code " + response.code()));
6685
return;
@@ -72,14 +91,25 @@ public void onResponse(@NonNull Call<List<Topic>> call, @NonNull Response<List<T
7291
public void onFailure(@NonNull Call<List<Topic>> call, @NonNull Throwable t) {
7392
logger.e("Cannot retain topics", t);
7493
if (call.isCanceled()) return;
75-
Snackbar.make(getView(), R.string.error_load_topics, Snackbar.LENGTH_INDEFINITE).show();
94+
mLoadingState.hideProgress();
95+
mLoadingState.icon.set(ContextCompat.getDrawable(requireContext(), R.mipmap.illustration_fetal_error));
96+
mLoadingState.text.set(getString(R.string.error_load_topics));
97+
mLoadingState.description.set(getString(R.string.error_description_global));
7698
}
7799
});
78100
}
79101

80102
private void displayTopicsToUI (List<Topic> originalList) {
103+
if (originalList == null || originalList.size() <= 0) {
104+
mLoadingState.icon.set(ContextCompat.getDrawable(requireContext(), R.mipmap.illustration_list_is_empty));
105+
mLoadingState.text.set(getString(R.string.topic_empty_title));
106+
mLoadingState.showRetry.set(false);
107+
mLoadingState.description.set(getString(R.string.topic_empty_description));
108+
return;
109+
}
110+
mLoadingState.hideAll();
81111
List<String> localSubscribedTopics = MiPushClient.getAllTopic(requireContext());
82-
List<Topic> list = originalList.stream()
112+
List<Topic> list = Stream.of(originalList)
83113
.peek(topic -> topic.setSubscribed(localSubscribedTopics.contains(topic.getId())))
84114
.collect(Collectors.toList());
85115
DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package moe.yuuta.mipushtester.widgets.multi_state;
2+
3+
import android.graphics.drawable.Drawable;
4+
import android.view.View;
5+
6+
import androidx.databinding.ObservableBoolean;
7+
import androidx.databinding.ObservableField;
8+
9+
public class State {
10+
public final ObservableBoolean showProgress;
11+
public final ObservableField<Drawable> icon;
12+
public final ObservableField<CharSequence> text;
13+
public final ObservableField<CharSequence> description;
14+
public final ObservableBoolean showTitle;
15+
public final ObservableBoolean showIcon;
16+
public final ObservableBoolean showDescription;
17+
public View.OnClickListener onRetryListener;
18+
public final ObservableBoolean showRetry;
19+
public final ObservableField<CharSequence> contentDescription;
20+
21+
public State() {
22+
showProgress = new ObservableBoolean(false);
23+
icon = new ObservableField<>();
24+
text = new ObservableField<>();
25+
description = new ObservableField<>();
26+
showRetry = new ObservableBoolean(false);
27+
showTitle = new ObservableBoolean(true);
28+
showDescription = new ObservableBoolean(true);
29+
contentDescription = new ObservableField<>();
30+
showIcon = new ObservableBoolean(true);
31+
}
32+
33+
public void showProgress () {
34+
this.showProgress.set(true);
35+
this.showTitle.set(false);
36+
this.showDescription.set(false);
37+
this.showRetry.set(false);
38+
this.showIcon.set(false);
39+
}
40+
41+
public void hideProgress () {
42+
this.showProgress.set(false);
43+
this.showTitle.set(true);
44+
this.showDescription.set(true);
45+
this.showRetry.set(true);
46+
this.showIcon.set(true);
47+
}
48+
49+
public void hideAll () {
50+
this.showProgress.set(false);
51+
this.showTitle.set(false);
52+
this.showDescription.set(false);
53+
this.showRetry.set(false);
54+
this.showIcon.set(false);
55+
}
56+
}
Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<androidx.constraintlayout.widget.ConstraintLayout
3-
xmlns:android="http://schemas.android.com/apk/res/android"
4-
xmlns:app="http://schemas.android.com/apk/res-auto"
5-
android:layout_width="match_parent"
6-
android:layout_height="match_parent">
7-
<androidx.recyclerview.widget.RecyclerView
8-
android:id="@+id/recycler_topic"
9-
android:layout_width="0dp"
10-
android:layout_height="0dp"
11-
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
12-
app:layout_constraintTop_toTopOf="parent"
13-
app:layout_constraintStart_toStartOf="parent"
14-
app:layout_constraintEnd_toEndOf="parent"
15-
app:layout_constraintBottom_toBottomOf="parent"/>
16-
</androidx.constraintlayout.widget.ConstraintLayout>
2+
<layout xmlns:bind="http://schemas.android.com/tools">
3+
<data>
4+
<variable
5+
name="state"
6+
type="moe.yuuta.mipushtester.widgets.multi_state.State" />
7+
</data>
8+
<androidx.constraintlayout.widget.ConstraintLayout
9+
xmlns:android="http://schemas.android.com/apk/res/android"
10+
xmlns:app="http://schemas.android.com/apk/res-auto"
11+
android:layout_width="match_parent"
12+
android:layout_height="match_parent">
13+
<androidx.recyclerview.widget.RecyclerView
14+
android:id="@+id/recycler_topic"
15+
android:layout_width="0dp"
16+
android:layout_height="0dp"
17+
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
18+
app:layout_constraintTop_toTopOf="parent"
19+
app:layout_constraintStart_toStartOf="parent"
20+
app:layout_constraintEnd_toEndOf="parent"
21+
app:layout_constraintBottom_toBottomOf="parent"/>
22+
<include layout="@layout/layout_multi_state"
23+
android:layout_width="match_parent"
24+
android:layout_height="match_parent"
25+
app:layout_constraintTop_toTopOf="parent"
26+
app:layout_constraintBottom_toBottomOf="parent"
27+
app:layout_constraintStart_toStartOf="parent"
28+
app:layout_constraintEnd_toEndOf="parent"
29+
bind:state="@{state}" />
30+
</androidx.constraintlayout.widget.ConstraintLayout>
31+
</layout>
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<layout>
3+
<data>
4+
<variable
5+
name="state"
6+
type="moe.yuuta.mipushtester.widgets.multi_state.State" />
7+
<import type="android.view.View" />
8+
</data>
9+
<androidx.constraintlayout.widget.ConstraintLayout
10+
xmlns:android="http://schemas.android.com/apk/res/android"
11+
xmlns:app="http://schemas.android.com/apk/res-auto"
12+
xmlns:tools="http://schemas.android.com/tools"
13+
android:layout_width="match_parent"
14+
android:layout_height="match_parent">
15+
16+
<ImageView
17+
android:id="@android:id/icon"
18+
android:layout_width="256dp"
19+
android:layout_height="256dp"
20+
android:contentDescription="@{state.contentDescription}"
21+
android:scaleType="centerCrop"
22+
android:src="@{state.icon}"
23+
android:visibility="@{state.showIcon ? View.VISIBLE : View.GONE}"
24+
android:layout_marginTop="32dp"
25+
app:layout_constraintEnd_toEndOf="parent"
26+
app:layout_constraintStart_toStartOf="parent"
27+
app:layout_constraintTop_toTopOf="parent"
28+
tools:src="@mipmap/illustration_fetal_error" />
29+
<TextView
30+
android:id="@android:id/title"
31+
android:layout_width="wrap_content"
32+
android:layout_height="wrap_content"
33+
android:textAppearance="@style/TextAppearance.AppCompat.Title"
34+
android:layout_marginTop="16dp"
35+
android:text="@{state.text}"
36+
android:visibility="@{state.showTitle ? View.VISIBLE : View.GONE}"
37+
app:layout_constraintTop_toBottomOf="@android:id/icon"
38+
app:layout_constraintStart_toStartOf="parent"
39+
app:layout_constraintEnd_toEndOf="parent"
40+
tools:text="@string/app_name"/>
41+
<TextView
42+
android:id="@android:id/summary"
43+
android:layout_width="wrap_content"
44+
android:layout_height="wrap_content"
45+
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
46+
android:layout_marginTop="8dp"
47+
android:visibility="@{state.showDescription ? View.VISIBLE : View.GONE}"
48+
android:text="@{state.description}"
49+
app:layout_constraintTop_toBottomOf="@android:id/title"
50+
app:layout_constraintStart_toStartOf="parent"
51+
app:layout_constraintEnd_toEndOf="parent"
52+
tools:text="@string/app_name"/>
53+
<Button
54+
android:id="@android:id/button1"
55+
android:layout_width="wrap_content"
56+
android:layout_height="wrap_content"
57+
android:layout_marginTop="8dp"
58+
android:text="@string/retry"
59+
android:visibility="@{state.showRetry ? View.VISIBLE : View.GONE}"
60+
android:onClick="@{state.onRetryListener}"
61+
app:layout_constraintTop_toBottomOf="@android:id/summary"
62+
app:layout_constraintStart_toStartOf="parent"
63+
app:layout_constraintEnd_toEndOf="parent"
64+
style="@style/Widget.AppCompat.Button.Colored"/>
65+
<ProgressBar
66+
android:id="@android:id/progress"
67+
android:layout_width="wrap_content"
68+
android:layout_height="wrap_content"
69+
android:visibility="@{state.showProgress ? View.VISIBLE : View.GONE}"
70+
app:layout_constraintTop_toTopOf="parent"
71+
app:layout_constraintEnd_toEndOf="parent"
72+
app:layout_constraintBottom_toBottomOf="parent"
73+
app:layout_constraintStart_toStartOf="parent"/>
74+
</androidx.constraintlayout.widget.ConstraintLayout>
75+
</layout>
86.4 KB
Loading
106 KB
Loading

app/src/main/res/values/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,8 @@
105105
<string name="topic_action_title">Subscribe topics</string>
106106
<string name="topic_action_description">Subscribe some topics provided by the author, I\'ll send some messages to these topics</string>
107107
<string name="error_load_topics">Cannot retain topics</string>
108+
<string name="error_description_global">Check your internet access and try again later.</string>
109+
<string name="retry">Retry</string>
110+
<string name="topic_empty_title">Currently empty</string>
111+
<string name="topic_empty_description">We\'ll add more topics here, check it back later.</string>
108112
</resources>

0 commit comments

Comments
 (0)