add: logout button on drawer menu
This commit is contained in:
@@ -1 +1 @@
|
||||
[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":34},"path":"inrt-release.apk","properties":{"packageId":"com.stardust.auojs.inrt","split":"","minSdkVersion":"17"}}]
|
||||
[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":239},"path":"release-3.0.0 Alpha39.apk","properties":{"packageId":"com.stardust.scriptdroid","split":"","minSdkVersion":"17"}}]
|
||||
@@ -95,11 +95,6 @@
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.stardust.autojs.core.image.ScreenCaptureRequestActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:taskAffinity="com.stardust.autojs.runtime.api.image.ScreenCaptureRequestActivity"
|
||||
android:theme="@style/AppTheme.Transparent"/>
|
||||
|
||||
<activity
|
||||
android:name=".ui.error.IssueReporterActivity"
|
||||
|
||||
@@ -66,6 +66,10 @@ public class NodeBB {
|
||||
|
||||
}
|
||||
|
||||
public void invalidateConfig() {
|
||||
mConfig = null;
|
||||
}
|
||||
|
||||
public static String getErrorMessage(Throwable e, Context context, String defaultMsg) {
|
||||
if (!(e instanceof HttpException)) {
|
||||
return defaultMsg;
|
||||
@@ -75,7 +79,7 @@ public class NodeBB {
|
||||
if (body == null)
|
||||
return defaultMsg;
|
||||
try {
|
||||
String errorMessage = getErrorMessage(context, e, body.string());
|
||||
String errorMessage = getErrorMessage(context, httpException, body.string());
|
||||
return errorMessage == null ? defaultMsg : errorMessage;
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
@@ -83,7 +87,7 @@ public class NodeBB {
|
||||
}
|
||||
}
|
||||
|
||||
private static String getErrorMessage(Context context, Throwable error, String errorBody) {
|
||||
private static String getErrorMessage(Context context, HttpException error, String errorBody) {
|
||||
if (errorBody == null)
|
||||
return null;
|
||||
if (errorBody.contains("invalid-login-credentials")) {
|
||||
@@ -99,6 +103,9 @@ public class NodeBB {
|
||||
if (errorBody.contains("email-taken")) {
|
||||
return context.getString(R.string.nodebb_error_email_taken);
|
||||
}
|
||||
if (error.code() == 403) {
|
||||
return context.getString(R.string.nodebb_error_forbidden);
|
||||
}
|
||||
Log.d(LOG_TAG, "unknown error: " + errorBody, error);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
package com.stardust.scriptdroid.network;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.stardust.scriptdroid.network.api.ConfigApi;
|
||||
import com.jakewharton.retrofit2.adapter.rxjava2.HttpException;
|
||||
import com.stardust.scriptdroid.network.api.UserApi;
|
||||
import com.stardust.scriptdroid.network.entity.user.User;
|
||||
import com.stardust.util.Objects;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.subjects.PublishSubject;
|
||||
import okhttp3.ResponseBody;
|
||||
import retrofit2.Retrofit;
|
||||
|
||||
@@ -17,8 +22,22 @@ import retrofit2.Retrofit;
|
||||
|
||||
public class UserService {
|
||||
|
||||
|
||||
public static class LoginStateChange {
|
||||
private final boolean mOnline;
|
||||
|
||||
public LoginStateChange(boolean online) {
|
||||
mOnline = online;
|
||||
}
|
||||
|
||||
public boolean isOnline() {
|
||||
return mOnline;
|
||||
}
|
||||
}
|
||||
|
||||
private static final UserService sInstance = new UserService();
|
||||
private final Retrofit mRetrofit;
|
||||
private volatile User mUser;
|
||||
|
||||
UserService() {
|
||||
mRetrofit = NodeBB.getInstance().getRetrofit();
|
||||
@@ -31,14 +50,20 @@ public class UserService {
|
||||
public Observable<ResponseBody> login(String userName, final String password) {
|
||||
return NodeBB.getInstance()
|
||||
.getConfig()
|
||||
.flatMap(config -> {
|
||||
return mRetrofit.create(UserApi.class)
|
||||
.login(Collections.singletonMap("x-csrf-token", config.getCsrfToken()),
|
||||
userName, password);
|
||||
});
|
||||
.flatMap(config ->
|
||||
mRetrofit.create(UserApi.class)
|
||||
.login(Collections.singletonMap("x-csrf-token", config.getCsrfToken()),
|
||||
userName, password)
|
||||
.doOnError(error -> {
|
||||
if (error instanceof HttpException && ((HttpException) error).code() == 403) {
|
||||
NodeBB.getInstance().invalidateConfig();
|
||||
}
|
||||
}))
|
||||
.doOnComplete(this::refreshOnlineStatus);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public Observable<ResponseBody> register(String email, String userName, String password) {
|
||||
return NodeBB.getInstance()
|
||||
.getConfig()
|
||||
@@ -46,4 +71,55 @@ public class UserService {
|
||||
.register(Collections.singletonMap("x-csrf-token", config.getCsrfToken()),
|
||||
email, userName, password, password));
|
||||
}
|
||||
|
||||
public boolean isOnline() {
|
||||
return mUser != null;
|
||||
}
|
||||
|
||||
private void setUser(User user) {
|
||||
User old = mUser;
|
||||
mUser = user;
|
||||
if (!Objects.equals(old, mUser)) {
|
||||
if (user == null) {
|
||||
NodeBB.getInstance().invalidateConfig();
|
||||
}
|
||||
EventBus.getDefault().post(new LoginStateChange(user != null));
|
||||
}
|
||||
}
|
||||
|
||||
public Observable<Boolean> refreshOnlineStatus() {
|
||||
PublishSubject<Boolean> online = PublishSubject.create();
|
||||
mRetrofit.create(UserApi.class)
|
||||
.me()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(user -> {
|
||||
setUser(user);
|
||||
online.onNext(true);
|
||||
online.onComplete();
|
||||
}, error -> {
|
||||
setUser(null);
|
||||
online.onNext(false);
|
||||
online.onComplete();
|
||||
});
|
||||
return online;
|
||||
}
|
||||
|
||||
public Observable<ResponseBody> logout() {
|
||||
return NodeBB.getInstance()
|
||||
.getConfig()
|
||||
.flatMap(config ->
|
||||
mRetrofit.create(UserApi.class)
|
||||
.logout(Collections.singletonMap("x-csrf-token", config.getCsrfToken()))
|
||||
)
|
||||
.doOnError(Throwable::printStackTrace)
|
||||
.doOnComplete(this::refreshOnlineStatus);
|
||||
}
|
||||
|
||||
public Observable<User> me() {
|
||||
return NodeBB.getInstance().getRetrofit()
|
||||
.create(UserApi.class)
|
||||
.me()
|
||||
.doOnNext(this::setUser)
|
||||
.doOnError(error -> setUser(null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,4 +31,6 @@ public interface UserApi {
|
||||
@Field("username") String userName, @Field("password") String password, @Field("password-confirm") String repeatPassword);
|
||||
|
||||
|
||||
@POST("/logout")
|
||||
Observable<ResponseBody> logout(@HeaderMap Map<String, String> csrfToken);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
package com.stardust.scriptdroid.ui.main.drawer;
|
||||
|
||||
import com.stardust.scriptdroid.R;
|
||||
import com.stardust.scriptdroid.network.UserService;
|
||||
import com.stardust.scriptdroid.ui.main.community.CommunityFragment;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
@@ -9,46 +10,105 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/10.
|
||||
*/
|
||||
|
||||
public class CommunityDrawerMenu {
|
||||
|
||||
private DrawerMenuItem mUnreadItem = new DrawerMenuItem(R.drawable.community_inbox, R.string.text_community_unread, this::showUnread);
|
||||
private DrawerMenuItem mLogoutItem = new DrawerMenuItem(R.drawable.ic_exit_to_app_black_24dp, R.string.text_logout, this::logout);
|
||||
|
||||
|
||||
private List<DrawerMenuItem> mDrawerMenuItems = new ArrayList<>(Arrays.asList(
|
||||
new DrawerMenuGroup(R.string.text_community),
|
||||
new DrawerMenuItem(R.drawable.community_list, R.string.text_community_category, this::showCategories),
|
||||
new DrawerMenuItem(R.drawable.community_inbox, R.string.text_community_unread, this::showUnread),
|
||||
mUnreadItem,
|
||||
new DrawerMenuItem(R.drawable.community_time, R.string.text_community_recent, this::showRecent),
|
||||
new DrawerMenuItem(R.drawable.community_fire, R.string.text_community_popular, this::showPopular),
|
||||
new DrawerMenuItem(R.drawable.community_tags, R.string.text_community_tags, this::showTags)
|
||||
new DrawerMenuItem(R.drawable.community_tags, R.string.text_community_tags, this::showTags),
|
||||
mLogoutItem
|
||||
));
|
||||
|
||||
private boolean mShown = false;
|
||||
|
||||
public List<DrawerMenuItem> getItems() {
|
||||
return mDrawerMenuItems;
|
||||
}
|
||||
|
||||
public void showCommunityMenu(DrawerMenuAdapter adapter) {
|
||||
mShown = true;
|
||||
List<DrawerMenuItem> items = adapter.getDrawerMenuItems();
|
||||
if (items.get(0) == mDrawerMenuItems.get(0)) {
|
||||
refreshUserStatus(adapter);
|
||||
return;
|
||||
}
|
||||
items.addAll(0, mDrawerMenuItems);
|
||||
adapter.notifyItemRangeInserted(0, mDrawerMenuItems.size());
|
||||
adapter.notifyItemChanged(mDrawerMenuItems.size());
|
||||
refreshUserStatus(adapter);
|
||||
}
|
||||
|
||||
private void refreshUserStatus(DrawerMenuAdapter adapter) {
|
||||
UserService.getInstance().refreshOnlineStatus()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(online -> setUserOnlineStatus(adapter, online));
|
||||
}
|
||||
|
||||
public void setUserOnlineStatus(DrawerMenuAdapter adapter, boolean online) {
|
||||
if (online) {
|
||||
addItem(adapter, R.string.text_community_category, mUnreadItem);
|
||||
addItem(adapter, R.string.text_community_tags, mLogoutItem);
|
||||
|
||||
} else {
|
||||
removeItem(adapter, R.string.text_community_unread);
|
||||
removeItem(adapter, R.string.text_logout);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addItem(DrawerMenuAdapter adapter, int title, DrawerMenuItem itemToAdd) {
|
||||
List<DrawerMenuItem> items = adapter.getDrawerMenuItems();
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
DrawerMenuItem item = items.get(i);
|
||||
if (item.getTitle() == title) {
|
||||
if (i >= items.size() || items.get(i + 1) != itemToAdd) {
|
||||
items.add(i + 1, itemToAdd);
|
||||
adapter.notifyItemInserted(i + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeItem(DrawerMenuAdapter adapter, int title) {
|
||||
List<DrawerMenuItem> items = adapter.getDrawerMenuItems();
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
DrawerMenuItem item = items.get(i);
|
||||
if (item.getTitle() == title) {
|
||||
items.remove(i);
|
||||
adapter.notifyItemRemoved(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void hideCommunityMenu(DrawerMenuAdapter adapter) {
|
||||
List<DrawerMenuItem> items = adapter.getDrawerMenuItems();
|
||||
mShown = false;
|
||||
if (items.isEmpty() || items.get(0) != mDrawerMenuItems.get(0)) {
|
||||
return;
|
||||
}
|
||||
items.subList(0, mDrawerMenuItems.size()).clear();
|
||||
adapter.notifyItemChanged(mDrawerMenuItems.size());
|
||||
adapter.notifyItemRangeRemoved(0, mDrawerMenuItems.size());
|
||||
for (int i = 1; i < items.size(); i++) {
|
||||
DrawerMenuItem item = items.get(i);
|
||||
if (item instanceof DrawerMenuGroup) {
|
||||
items.subList(0, i).clear();
|
||||
adapter.notifyItemRangeRemoved(0, i);
|
||||
adapter.notifyItemChanged(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showCategories(DrawerMenuItemViewHolder holder) {
|
||||
@@ -76,4 +136,14 @@ public class CommunityDrawerMenu {
|
||||
}
|
||||
|
||||
|
||||
private void logout(DrawerMenuItemViewHolder holder) {
|
||||
UserService.getInstance().logout()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe();
|
||||
EventBus.getDefault().post(new CommunityFragment.LoadUrl("/"));
|
||||
}
|
||||
|
||||
public boolean isShown() {
|
||||
return mShown;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.stardust.scriptdroid.App;
|
||||
import com.stardust.scriptdroid.Pref;
|
||||
import com.stardust.scriptdroid.R;
|
||||
import com.stardust.scriptdroid.network.GlideApp;
|
||||
import com.stardust.scriptdroid.network.UserService;
|
||||
import com.stardust.scriptdroid.pluginclient.DevPluginClient;
|
||||
import com.stardust.scriptdroid.ui.floating.CircularMenu;
|
||||
import com.stardust.scriptdroid.ui.floating.FloatyWindowManger;
|
||||
@@ -52,6 +53,7 @@ import org.androidannotations.annotations.EFragment;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -147,8 +149,7 @@ public class DrawerFragment extends android.support.v4.app.Fragment {
|
||||
|
||||
@Click(R.id.avatar)
|
||||
void loginOrShowUserInfo() {
|
||||
NodeBB.getInstance().getRetrofit()
|
||||
.create(UserApi.class)
|
||||
UserService.getInstance()
|
||||
.me()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
@@ -368,6 +369,13 @@ public class DrawerFragment extends android.support.v4.app.Fragment {
|
||||
mDrawerMenu.scrollToPosition(0);
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onLoginStateChange(UserService.LoginStateChange change) {
|
||||
syncUserInfo();
|
||||
if (mCommunityDrawerMenu.isShown()) {
|
||||
mCommunityDrawerMenu.setUserOnlineStatus(mDrawerMenuAdapter, change.isOnline());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
|
||||
@@ -6,8 +6,8 @@ package com.stardust.scriptdroid.ui.main.drawer;
|
||||
|
||||
public class DrawerMenuGroup extends DrawerMenuItem {
|
||||
|
||||
|
||||
public DrawerMenuGroup(int title) {
|
||||
super(0, title, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -77,4 +77,19 @@ public class DrawerMenuItem {
|
||||
if (mAction != null)
|
||||
mAction.onClick(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
DrawerMenuItem that = (DrawerMenuItem) o;
|
||||
|
||||
return mTitle == that.mTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mTitle;
|
||||
}
|
||||
}
|
||||
|
||||
BIN
app/src/main/res/drawable-hdpi/ic_exit_to_app_black_24dp.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_exit_to_app_black_24dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 251 B |
BIN
app/src/main/res/drawable-mdpi/ic_exit_to_app_black_24dp.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_exit_to_app_black_24dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 171 B |
BIN
app/src/main/res/drawable-xhdpi/ic_exit_to_app_black_24dp.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_exit_to_app_black_24dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 246 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_exit_to_app_black_24dp.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_exit_to_app_black_24dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 354 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_exit_to_app_black_24dp.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_exit_to_app_black_24dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 432 B |
@@ -336,4 +336,6 @@
|
||||
<string name="nodebb_error_change_password_error_length">密码太短</string>
|
||||
<string name="nodebb_error_email_taken">邮箱已被占用</string>
|
||||
<string name="nodebb_error_change_password_error_match">两次输入的密码不一致</string>
|
||||
<string name="text_logout">退出登录</string>
|
||||
<string name="nodebb_error_forbidden">操作失败,请稍后重试</string>
|
||||
</resources>
|
||||
|
||||
@@ -15,6 +15,12 @@
|
||||
android:name=".execution.ScriptExecuteActivity"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
|
||||
|
||||
<activity
|
||||
android:name="com.stardust.autojs.core.image.ScreenCaptureRequestActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:taskAffinity="com.stardust.autojs.runtime.api.image.ScreenCaptureRequestActivity"
|
||||
android:theme="@style/AppTheme.Transparent"/>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -4,6 +4,9 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Xfermode;
|
||||
import android.media.Image;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.RequiresApi;
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.media.Image;
|
||||
import android.os.Build;
|
||||
|
||||
@@ -13,6 +13,16 @@
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
<style name="AppTheme.Transparent" parent="AppTheme">
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowIsFloating">true</item>
|
||||
<item name="android:backgroundDimEnabled">false</item>
|
||||
</style>
|
||||
|
||||
<color name="colorPrimary">#009688</color>
|
||||
<color name="colorPrimaryDark">#009688</color>
|
||||
<color name="colorAccent">#03a9f4</color>
|
||||
|
||||
27
common/src/main/java/com/stardust/util/Objects.java
Normal file
27
common/src/main/java/com/stardust/util/Objects.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.stardust.util;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/12/31.
|
||||
*/
|
||||
|
||||
public class Objects {
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the arguments are equal to each other
|
||||
* and {@code false} otherwise.
|
||||
* Consequently, if both arguments are {@code null}, {@code true}
|
||||
* is returned and if exactly one argument is {@code null}, {@code
|
||||
* false} is returned. Otherwise, equality is determined by using
|
||||
* the {@link Object#equals equals} method of the first
|
||||
* argument.
|
||||
*
|
||||
* @param a an object
|
||||
* @param b an object to be compared with {@code a} for equality
|
||||
* @return {@code true} if the arguments are equal to each other
|
||||
* and {@code false} otherwise
|
||||
* @see Object#equals(Object)
|
||||
*/
|
||||
public static boolean equals(Object a, Object b) {
|
||||
return (a == b) || (a != null && a.equals(b));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user