Runt
2022-05-29 e407dd1f335aa9c716b89b3152bf363b898d28fa
接口请求框架
注册接口
1 files renamed
20 files modified
15 files added
1200 ■■■■ changed files
app/build.gradle 2 ●●●●● patch | view | raw | blame | history
app/src/main/AndroidManifest.xml 1 ●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/MainActivity.java 52 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/base/activities/BaseActivity.java 125 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/base/activities/BaseLoadPageActivity.java 33 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/base/fragments/BaseFragment.java 5 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/base/fragments/BaseLoadPageFragment.java 33 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/base/fragments/BaseTabFragment.java 4 ●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/base/model/BaseViewModel.java 6 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/data/ActiveBean.java 9 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/data/ActiveResult.java 9 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/data/ApkUpGradeResult.java 2 ●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/data/BaseApiResult.java 17 ●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/data/BasePageResult.java 2 ●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/retrofit/Interceptor/HttpLoggingInterceptor.java 57 ●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/retrofit/api/CollectApiCenter.java 79 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/retrofit/api/CommonApiCenter.java 2 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/retrofit/converter/DecryptGsonResponseBodyConverter.java 15 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/retrofit/observable/HttpObserver.java 64 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/retrofit/observable/LoadingHttpObserver.java 36 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/retrofit/utils/RetrofitUtils.java 3 ●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/service/FloatingWindowService.java 12 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/ui/collect/Collect.java 9 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/ui/collect/CollectActivity.java 16 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/ui/collect/CollectListResult.java 14 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/ui/collect/CollectViewModel.java 15 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/util/DeviceIdUtils.java 124 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/util/PreferencesUtils.java 234 ●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/util/SHA1UTIL.java 64 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/auto/lyric/vm/MainViewModel.java 43 ●●●●● patch | view | raw | blame | history
app/src/main/res/drawable-v21/bg_white_divider.xml 38 ●●●●● patch | view | raw | blame | history
app/src/main/res/layout/activity_collect.xml 31 ●●●●● patch | view | raw | blame | history
app/src/main/res/layout/activity_main.xml 44 ●●●● patch | view | raw | blame | history
app/src/main/res/mipmap-xhdpi/icon_white_back.png patch | view | raw | blame | history
app/src/main/res/mipmap-xxhdpi/icon_white_back.png patch | view | raw | blame | history
app/src/main/res/mipmap-xxxhdpi/icon_white_back.png patch | view | raw | blame | history
app/build.gradle
@@ -18,11 +18,13 @@
    buildTypes {
        debug {
            minifyEnabled false
            buildConfigField 'String','HOST_IP_ADDR','"https://test1.mydetao.cn/"'
            resValue "string", "app_name", "歌词工具测试"
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        release {
            minifyEnabled false
            buildConfigField 'String','HOST_IP_ADDR','"https://test1.mydetao.cn/"'
            resValue "string", "app_name", "歌词工具"
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
app/src/main/AndroidManifest.xml
@@ -33,6 +33,7 @@
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:networkSecurityConfig="@xml/network_security_config"
        android:theme="@style/Theme.AutoLyric">
        <activity
            android:name=".MainActivity"
app/src/main/java/com/auto/lyric/MainActivity.java
@@ -6,27 +6,79 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import com.auto.lyric.base.activities.BaseActivity;
import com.auto.lyric.data.ActiveResult;
import com.auto.lyric.data.LyricServer;
import com.auto.lyric.data.TextBean;
import com.auto.lyric.databinding.ActivityMainBinding;
import com.auto.lyric.retrofit.observable.LoadingHttpObserver;
import com.auto.lyric.service.AutoInputService;
import com.auto.lyric.service.FloatingWindowService;
import com.auto.lyric.util.DeviceUtil;
import com.auto.lyric.vm.MainViewModel;
import com.google.gson.Gson;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.regex.Pattern;
public class MainActivity extends BaseActivity<ActivityMainBinding, MainViewModel> {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    @Override
    public void initViews() {
        Intent floatService  = new Intent(getApplicationContext(), FloatingWindowService.class);
        String key = getStringProjectPrefrence("activeKey");
        ActiveResult result = new Gson().fromJson(key,ActiveResult.class);
        try {
            if(TextUtils.isEmpty(key) || TextUtils.isEmpty(result.validDate)||
                    new Date().getTime() > dateFormat.parse(result.validDate).getTime()){
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle("软件尚未激活或已过期");
                EditText editText = new EditText(this);
                editText.setHint("请输入激活码");
                ViewGroup.MarginLayoutParams params = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                params.setMargins(DeviceUtil.convertDpToPixel(30,this),DeviceUtil.convertDpToPixel(20,this),DeviceUtil.convertDpToPixel(30,this),0);
                editText.setLayoutParams(params);
                LinearLayout lin = new LinearLayout(this);
                lin.addView(editText);
                builder.setView(lin);
                builder.setCancelable(false);
                AlertDialog dialg = builder.create();
                dialg.setButton(AlertDialog.BUTTON_NEGATIVE,"激活", (dialog, which) -> {
                    viewModel.checkActive(editText.getText().toString().trim(),new LoadingHttpObserver<ActiveResult>(this) {
                        @Override
                        public void onError(ActiveResult error) {
                            super.onError(error);
                            dialg.show();
                        }
                        @Override
                        public void onComplete(ActiveResult result) {
                        }
                    });
                });
                dialg.setButton(AlertDialog.BUTTON_POSITIVE,"退出程序",(dialog,which) ->{
                    System.exit(0);
                });
                dialg.show();
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        binding.button.setOnClickListener(v -> {
            if(binding.edit.getText().toString().trim().length() == 0){
app/src/main/java/com/auto/lyric/base/activities/BaseActivity.java
@@ -25,10 +25,12 @@
import com.auto.lyric.R;
import com.auto.lyric.base.model.BaseViewModel;
import com.auto.lyric.base.model.ViewModelFactory;
import com.auto.lyric.util.PreferencesUtils;
import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.Set;
import dmax.dialog.SpotsDialog;
@@ -93,6 +95,7 @@
        }
        Class<VM> vmClass = (Class<VM>) type.getActualTypeArguments()[1];
        viewModel = new ViewModelProvider(this,getViewModelFactory()).get(vmClass);
        viewModel.onCreate(this);
        setContentView(binding.getRoot());
        mContext = this;
        try {
@@ -288,4 +291,126 @@
        return false;
    }
    public boolean getBooleanUserPrefrence(String key){
        return PreferencesUtils.getBoolean(this,key,false,PreferencesUtils.USER);
    }
    public boolean getBooleanProjectPrefrence(String key){
        return PreferencesUtils.getBoolean(this,key,false,PreferencesUtils.PROJECT);
    }
    public String getStringUserPrefrence(String key){
        return PreferencesUtils.getString(this,key,"",PreferencesUtils.USER);
    }
    public String getStringProjectPrefrence(String key){
        return PreferencesUtils.getString(this,key,"",PreferencesUtils.PROJECT);
    }
    public Integer getIntProjectPrefrence(String key){
        return PreferencesUtils.getInt(this,key,0,PreferencesUtils.PROJECT);
    }
    public Long getLongProjectPrefrence(String key){
        return PreferencesUtils.getLong(this,key,0,PreferencesUtils.PROJECT);
    }
    public float getFloatProjectPrefrence(String key){
        return PreferencesUtils.getFloat(this,key,0,PreferencesUtils.PROJECT);
    }
    public Set getStringSetProjectPrefrence(String key){
        return PreferencesUtils.getStringSet(this,key,PreferencesUtils.PROJECT);
    }
    public Integer getIntUserPrefrence(String key){
        return PreferencesUtils.getInt(this,key,0,PreferencesUtils.USER);
    }
    public Long getLongUserPrefrence(String key){
        return PreferencesUtils.getLong(this,key,0,PreferencesUtils.USER);
    }
    public float getFloatUserPrefrence(String key){
        return PreferencesUtils.getFloat(this,key,0,PreferencesUtils.USER);
    }
    public Set getStringSetUserPrefrence(String key){
        return PreferencesUtils.getStringSet(this,key,PreferencesUtils.USER);
    }
    public void putBooleanUserPrefrence(String key ,Boolean value){
        PreferencesUtils.putBoolean(this,key,value,PreferencesUtils.USER);
    }
    public void putBooleanProjectPrefrence(String key,Boolean value){
        PreferencesUtils.putBoolean(this,key,value,PreferencesUtils.PROJECT);
    }
    public void putStringUserPrefrence(String key,String value){
        PreferencesUtils.putString(this,key,value,PreferencesUtils.USER);
    }
    public void putStringProjectPrefrence(String key,String value){
        PreferencesUtils.putString(this,key,value,PreferencesUtils.PROJECT);
    }
    public void putIntProjectPrefrence(String key,int value){
        PreferencesUtils.putInt(this,key,value,PreferencesUtils.PROJECT);
    }
    public void putLongProjectPrefrence(String key,long value){
        PreferencesUtils.putLong(this,key,value,PreferencesUtils.PROJECT);
    }
    public void putFloatProjectPrefrence(String key,float value){
        PreferencesUtils.putFloat(this,key,value,PreferencesUtils.PROJECT);
    }
    public void putStringSetProjectPrefrence(String key, Set value){
        PreferencesUtils.putStringSet(this,key,value,PreferencesUtils.PROJECT);
    }
    public void putIntUserPrefrence(String key,int value){
        PreferencesUtils.putInt(this,key,value,PreferencesUtils.USER);
    }
    public void putLongUserPrefrence(String key,long value){
        PreferencesUtils.putLong(this,key,value,PreferencesUtils.USER);
    }
    public void putFloatUserPrefrence(String key,float value){
        PreferencesUtils.putFloat(this,key,value,PreferencesUtils.USER);
    }
    public void putStringSetUserPrefrence(String key, Set value){
        PreferencesUtils.putStringSet(this,key,value,PreferencesUtils.USER);
    }
    public void removeUserKey(String key){
        PreferencesUtils.removeKey(this,key,PreferencesUtils.USER);
    }
    public void removeProjectKey(String key){
        PreferencesUtils.removeKey(this,key,PreferencesUtils.PROJECT);
    }
    public void removeUserValue(String Value){
        PreferencesUtils.removeValue(this,Value,PreferencesUtils.USER);
    }
    public void removeProjectValue(String Value){
        PreferencesUtils.removeValue(this,Value,PreferencesUtils.PROJECT);
    }
    public void clearProjectData(){
        PreferencesUtils.clearData(this,PreferencesUtils.PROJECT);
    }
    public void clearUserData(){
        PreferencesUtils.clearData(this,PreferencesUtils.USER);
    }
}
app/src/main/java/com/auto/lyric/base/activities/BaseLoadPageActivity.java
@@ -65,27 +65,20 @@
    }
    private void finishFreshLoadmore(D result){
        if(result.code == 200){
            smartRefresh.setEnableRefresh(true);
            smartRefresh.finishRefresh();
            if(page == 1){
                adapter.getData().clear();
                adapter.setData(result.rows);
            }else{
                adapter.getData().addAll(result.rows);
                adapter.notifyDataSetChanged();
            }
            if(result.total <= adapter.getData().size()// 总数是否已经加载完
                    || result.rows.size() < SIZE // 最后一页数据的数量一般不满size
            ){//判断是否没有数据了
                smartRefresh.finishLoadMoreWithNoMoreData();
            }else {
                smartRefresh.finishLoadMore();
            }
        smartRefresh.setEnableRefresh(true);
        smartRefresh.finishRefresh();
        if(page == 1){
            adapter.getData().clear();
            adapter.setData(result.rows);
        }else{
            smartRefresh.setEnableRefresh(true);
            smartRefresh.finishRefresh();
            adapter.getData().addAll(result.rows);
            adapter.notifyDataSetChanged();
        }
        if(result.total <= adapter.getData().size()// 总数是否已经加载完
                || result.rows.size() < SIZE // 最后一页数据的数量一般不满size
        ){//判断是否没有数据了
            smartRefresh.finishLoadMoreWithNoMoreData();
        }else {
            smartRefresh.finishLoadMore();
        }
    }
app/src/main/java/com/auto/lyric/base/fragments/BaseFragment.java
@@ -7,11 +7,11 @@
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.viewbinding.ViewBinding;
import com.auto.lyric.base.activities.BaseActivity;
import com.auto.lyric.base.model.BaseViewModel;
import com.auto.lyric.base.model.ViewModelFactory;
import java.lang.reflect.Method;
@@ -21,7 +21,7 @@
 * fragment 封装
 * Created by Administrator on 2021/10/28 0028.
 */
public abstract class BaseFragment<B extends ViewBinding,VM extends ViewModel> extends Fragment {
public abstract class BaseFragment<B extends ViewBinding,VM extends BaseViewModel> extends Fragment {
    protected BaseActivity activity;
    protected  B binding;
@@ -52,6 +52,7 @@
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        activity = (BaseActivity) getActivity();
        viewModel.onCreate(activity);
        initViews();
    }
app/src/main/java/com/auto/lyric/base/fragments/BaseLoadPageFragment.java
@@ -77,27 +77,20 @@
    }
    private void finishFreshLoadmore(D result){
        if(result.code == 200){
            smartRefresh.setEnableRefresh(true);
            smartRefresh.finishRefresh();
            if(page == 1){
                adapter.getData().clear();
                adapter.setData(result.rows);
            }else{
                adapter.getData().addAll(result.rows);
                adapter.notifyDataSetChanged();
            }
            if(result.total <= adapter.getData().size()// 总数是否已经加载完
                    || result.rows.size() < SIZE // 最后一页数据的数量一般不满size
            ){//判断是否没有数据了
                smartRefresh.finishLoadMoreWithNoMoreData();
            }else {
                smartRefresh.finishLoadMore();
            }
        smartRefresh.setEnableRefresh(true);
        smartRefresh.finishRefresh();
        if(page == 1){
            adapter.getData().clear();
            adapter.setData(result.rows);
        }else{
            smartRefresh.setEnableRefresh(true);
            smartRefresh.finishRefresh();
            adapter.getData().addAll(result.rows);
            adapter.notifyDataSetChanged();
        }
        if(result.total <= adapter.getData().size()// 总数是否已经加载完
                || result.rows.size() < SIZE // 最后一页数据的数量一般不满size
        ){//判断是否没有数据了
            smartRefresh.finishLoadMoreWithNoMoreData();
        }else {
            smartRefresh.finishLoadMore();
        }
    }
app/src/main/java/com/auto/lyric/base/fragments/BaseTabFragment.java
@@ -1,10 +1,10 @@
package com.auto.lyric.base.fragments;
import androidx.lifecycle.ViewModel;
import androidx.viewbinding.ViewBinding;
import androidx.viewpager2.widget.ViewPager2;
import com.auto.lyric.base.adapter.FragmentAdapter;
import com.auto.lyric.base.model.BaseViewModel;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
@@ -16,7 +16,7 @@
 * 带有tablayout fragment封装
 * Created by Administrator on 2021/11/3 0003.
 */
public abstract class BaseTabFragment<B extends ViewBinding,VM extends ViewModel> extends BaseFragment<B,VM> {
public abstract class BaseTabFragment<B extends ViewBinding,VM extends BaseViewModel> extends BaseFragment<B,VM> {
    TabLayout tabLayout;
    FragmentAdapter fragmentAdapter;
app/src/main/java/com/auto/lyric/base/model/BaseViewModel.java
@@ -2,6 +2,7 @@
import androidx.lifecycle.ViewModel;
import com.auto.lyric.base.activities.BaseActivity;
import com.auto.lyric.retrofit.AndroidScheduler;
import com.auto.lyric.retrofit.observable.HttpObserver;
@@ -13,6 +14,11 @@
 */
public class BaseViewModel extends ViewModel {
    protected BaseActivity activity;
    public void onCreate(BaseActivity activity){
        this.activity = activity;
    }
    /**
     * 网络请求观察
app/src/main/java/com/auto/lyric/data/ActiveBean.java
New file
@@ -0,0 +1,9 @@
package com.auto.lyric.data;
/**
 * Created by Runt (qingingrunt2010@qq.com) on 2022/5/29.
 */
public class ActiveBean {
    public String activeKey, ip, phoneID, random,sign;
    public long timeStamp ; public int userId;
}
app/src/main/java/com/auto/lyric/data/ActiveResult.java
New file
@@ -0,0 +1,9 @@
package com.auto.lyric.data;
/**
 * Created by Runt (qingingrunt2010@qq.com) on 2022/5/29.
 */
public class ActiveResult extends BaseApiResult{
    public String userID,userClassName,validDate,permission;
    public int userClass;
}
app/src/main/java/com/auto/lyric/data/ApkUpGradeResult.java
@@ -3,7 +3,7 @@
/**
 * Created by Administrator on 2021/11/15 0015.
 */
public class ApkUpGradeResult extends BaseApiResult<ApkUpGradeResult.AppInfo>{
public class ApkUpGradeResult extends BaseApiResult{
    public class AppInfo {
        //以下为声明的参数
app/src/main/java/com/auto/lyric/data/BaseApiResult.java
@@ -5,19 +5,6 @@
/**
 * Created by Administrator on 2021/10/28 0028.
 */
public class BaseApiResult<D extends Object> implements Serializable {
    public String msg;
    public int code = 200;
    public D data;
    @Override
    public String toString() {
        return "BaseApiData{" +
                "msg='" + msg + '\'' +
                ", code=" + code +
                ", data=" + data +
                '}';
    }
public class BaseApiResult implements Serializable {
    public String result,reason,errorInfo;
}
app/src/main/java/com/auto/lyric/data/BasePageResult.java
@@ -5,7 +5,7 @@
/**
 * Created by Administrator on 2021/10/28 0028.
 */
public class BasePageResult<T> extends BaseApiResult<String>{
public class BasePageResult<T> extends BaseApiResult{
    public int pages;
    public int total;
    public int pageNum;
app/src/main/java/com/auto/lyric/retrofit/Interceptor/HttpLoggingInterceptor.java
@@ -5,7 +5,6 @@
import com.auto.lyric.retrofit.net.NetWorkCost;
import com.auto.lyric.retrofit.net.NetWorkListenear;
import com.auto.lyric.retrofit.utils.HttpPrintUtils;
import com.auto.lyric.util.GsonUtils;
import org.json.JSONObject;
@@ -111,33 +110,37 @@
                charset = contentType.charset(UTF8);
            }
            HashMap param = new HashMap();
            if(requestBody instanceof MultipartBody){
                logArrays.add("---------->REQUEST BODY[MultipartBody]<----------");
                MultipartBody body = (MultipartBody) requestBody;
                for(MultipartBody.Part part:body.parts()){
                    Buffer buffer1 = new Buffer();
                    part.body().writeTo(buffer1);
                    String str=buffer1.readString(charset).replaceAll("%(?![0-9a-fA-F]{2})","%25");
                    param.put(part.headers().get(part.headers().name(0)),URLDecoder.decode(str, "UTF-8"));
            try {
                if(requestBody instanceof MultipartBody){
                    logArrays.add("---------->REQUEST BODY[MultipartBody]<----------");
                    MultipartBody body = (MultipartBody) requestBody;
                    for(MultipartBody.Part part:body.parts()){
                        Buffer buffer1 = new Buffer();
                        part.body().writeTo(buffer1);
                        String str=buffer1.readString(charset).replaceAll("%(?![0-9a-fA-F]{2})","%25");
                        param.put(part.headers().get(part.headers().name(0)),URLDecoder.decode(str, "UTF-8"));
                    }
                    logArrays.add(new JSONObject(param).toString(4));
                }else if(requestBody instanceof FormBody){
                    logArrays.add("---------->REQUEST BODY[FormBody]<----------");
                    FormBody body = (FormBody) requestBody;
                    for(int i = 0 ; i < body.size() ; i ++ ){
                        param.put(body.name(i),body.value(i));
                    }
                    logArrays.add(new JSONObject(param).toString(4));
                }else{
                    Buffer buffer = new Buffer();
                    requestBody.writeTo(buffer);
                    logArrays.add("---------->REQUEST BODY<----------");
                    String str = buffer.readString(charset);
                    if(str.indexOf("{") == 0 ){
                        logArrays.add(new JSONObject(URLDecoder.decode(str, "UTF-8")).toString(4));
                    }else{
                        logArrays.add(str);
                    }
                }
                logArrays.add(GsonUtils.retractJson(new JSONObject(param).toString()));
            }else if(requestBody instanceof FormBody){
                logArrays.add("---------->REQUEST BODY[FormBody]<----------");
                FormBody body = (FormBody) requestBody;
                for(int i = 0 ; i < body.size() ; i ++ ){
                    param.put(body.name(i),body.value(i));
                }
                logArrays.add(GsonUtils.retractJson(new JSONObject(param).toString()));
            }else{
                Buffer buffer = new Buffer();
                requestBody.writeTo(buffer);
                logArrays.add("---------->REQUEST BODY<----------");
                String str = buffer.readString(charset);
                try{
                    logArrays.add(GsonUtils.retractJson(URLDecoder.decode(str, "UTF-8")));
                }catch (Exception e){
                    logArrays.add(str);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            logArrays.add("--> END " + request.method() + " " + contentType + " ( "
                    + requestBody.contentLength() + "-byte body )");
app/src/main/java/com/auto/lyric/retrofit/api/CollectApiCenter.java
New file
@@ -0,0 +1,79 @@
package com.auto.lyric.retrofit.api;
import com.auto.lyric.data.ActiveResult;
import com.auto.lyric.ui.collect.CollectListResult;
import io.reactivex.Observable;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
/**
 * Created by Runt (qingingrunt2010@qq.com) on 2022/5/29.
 */
public interface CollectApiCenter {
    /**
     * 收藏夹列表
     * @return
     */
    @FormUrlEncoded
    @POST("index.php?route=lrc/favorites/getFavoritesList")
    Observable<CollectListResult> getCollectList();
    /**
     * 歌词详情
     * @return
     */
    @FormUrlEncoded
    @POST("index.php?route=lrc/favorites/getLrc")
    Observable<CollectListResult> getLrcDetail(@Field("lrc_id") int lrc_id);
    /**
     * 删除歌词
     * @return
     */
    @FormUrlEncoded
    @POST("index.php?route=lrc/favorites/delLrc")
    Observable<CollectListResult> removeLrc(@Field("lrc_id") int lrc_id);
    /**
     * 添加歌词歌词
     * @return
     */
    @FormUrlEncoded
    @POST("index.php?route=lrc/favorites/delLrc")
    Observable<CollectListResult> addLrc(@Field("lrc_title") String lrc_title,@Field("lrc_text") String lrc_text);
    /**
     * 激活软件
     * @param activeKey
     * @param ip
     * @param random
     * @param time
     * @param userId
     * @param sign
     * @return
     */
    @FormUrlEncoded
    @POST("index.php?route=lrc/login_check")
    Observable<ActiveResult> loginCheck(@Field("activeKey") String activeKey, @Field("ip") String ip, @Field("random") String random,
                                        @Field("timeStamp") long time, @Field("userId") int userId, @Field("sign") String sign);
    /**
     * 激活软件
     * @param activeKey
     * @param ip
     * @param random
     * @param time
     * @param userId
     * @param sign
     * @return
     */
    @FormUrlEncoded
    @POST("index.php?route=lrc/register")
    Observable<ActiveResult> register(@Field("activeKey") String activeKey, @Field("ip") String ip,@Field("phoneID") String phoneID, @Field("random") String random,
                                      @Field("timeStamp") long time, @Field("userID") int userId, @Field("sign") String sign);
}
app/src/main/java/com/auto/lyric/retrofit/api/CommonApiCenter.java
@@ -2,6 +2,7 @@
import com.auto.lyric.data.ActiveResult;
import com.auto.lyric.data.ApkUpGradeResult;
import java.util.Map;
@@ -63,4 +64,5 @@
    Observable<ApkUpGradeResult> getAppUpdate();
}
app/src/main/java/com/auto/lyric/retrofit/converter/DecryptGsonResponseBodyConverter.java
@@ -1,5 +1,6 @@
package com.auto.lyric.retrofit.converter;
import android.text.TextUtils;
import android.util.Log;
import com.auto.lyric.data.BaseApiResult;
@@ -51,15 +52,15 @@
            response = decryptJsonStr(val);//解密
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            e.printStackTrace();
            BaseApiResult apiResult = new BaseApiResult<>();
            apiResult.code = 412;
            apiResult.msg = "解密数据出错"+e.getMessage();
            BaseApiResult apiResult = new BaseApiResult();
            apiResult.result = "412";
            apiResult.reason = "解密数据出错"+e.getMessage();
            response = new Gson().toJson(apiResult);
        } catch (JSONException e) {
            e.printStackTrace();
            BaseApiResult apiResult = new BaseApiResult<>();
            apiResult.code = 414;
            apiResult.msg = "非标准json";
            BaseApiResult apiResult = new BaseApiResult();
            apiResult.result = "414";
            apiResult.reason = "非标准json";
            response = new Gson().toJson(apiResult);
        }catch (Exception e){
            JsonReader jsonReader = gson.newJsonReader(value.charStream());
@@ -88,6 +89,8 @@
            JSONObject json = new JSONObject(body);
            body = json.toString();
            //body = RSAUtils.decrypt(json.getString(ENCRYPT), RSAUtils.getPublicKey(RSAUtils.PUBLIC_KEY));//
        }else if(TextUtils.isEmpty(body)){
            return body;
        }
        return transHump? GsonUtils.toHumpJson(body):body;
    }
app/src/main/java/com/auto/lyric/retrofit/observable/HttpObserver.java
@@ -2,10 +2,9 @@
import android.util.Log;
import androidx.lifecycle.MutableLiveData;
import com.auto.lyric.data.BaseApiResult;
import java.io.EOFException;
import java.lang.reflect.ParameterizedType;
import java.net.SocketTimeoutException;
@@ -19,50 +18,67 @@
    final String TAG = "HttpObserver";
    MutableLiveData<T> resultLive;
    public HttpObserver(MutableLiveData<T> resultLive) {
        this.resultLive = resultLive;
    }
    protected T value;
    @Override
    public void onNext(T value) {
        resultLive.setValue(value);
        this.value = value;
    }
    /**
     * 错误处理
     * @param throwable
     */
    @Override
    public void onError(Throwable throwable) {
        Log.i("subscribe","onError");
        Log.e("subscribe","onError "+throwable);
        T t = null;
        try {
            Log.e(TAG,this.getClass().getSimpleName()+" "+throwable.getMessage());
            Class<T> entityClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
            T t = entityClass.newInstance();//实例化一个泛型
            t.code = 410;
            t = entityClass.newInstance();//实例化一个泛型
            t.result = "410";
            if( throwable instanceof SocketTimeoutException){
                t.msg = "服务请求超时,请稍候再试";//设置错误信息
                t.reason = "服务请求超时,请稍候再试";//设置错误信息
            }else if(throwable instanceof EOFException){
                t.reason = "服务未反馈任何内容";//设置错误信息
            }else{
                t.msg = "网络连接不畅,请检查您的网络设置";//设置错误信息
                t.reason = "网络连接不畅,请检查您的网络设置";//设置错误信息
            }
            resultLive.setValue(t);
        } catch (ClassCastException e) {
            e.printStackTrace();
            T t = (T) new BaseApiResult<String>();
            t.code = 409;
            t.msg = "实例化对象未指定泛型实体类";
            resultLive.setValue(t);
            t = (T) new BaseApiResult();
            t.result = "409";
            t.reason = "实例化对象未指定泛型实体类";
        } catch (Exception e) {
            e.printStackTrace();
            T t = (T) new BaseApiResult<String>();
            t.code = 409;
            t.msg = e.getMessage();
            resultLive.setValue(t);
            t.result = "409";
            t.reason = e.getMessage();
        }
        onError(t);
    }
    @Override
    public void onComplete() {
        if(value.result.equals("success")){
            //返回成功
            onComplete(value);
        }else{
            //返回失败
            onError(value);
        }
        Log.i("subscribe","onComplete");
    }
    /**
     * 接口返回成功
     * @param result
     */
    public abstract void onError(T result) ;
    /**
     * 接口返回失败
     * @param error
     */
    public abstract void onComplete(T error) ;
}
app/src/main/java/com/auto/lyric/retrofit/observable/LoadingHttpObserver.java
New file
@@ -0,0 +1,36 @@
package com.auto.lyric.retrofit.observable;
import android.text.TextUtils;
import com.auto.lyric.base.activities.BaseActivity;
import com.auto.lyric.data.BaseApiResult;
/**
 * Created by Runt (qingingrunt2010@qq.com) on 2022/5/29.
 */
public abstract class LoadingHttpObserver<T extends BaseApiResult> extends HttpObserver<T> {
    final String TAG = "HttpObserver";
    BaseActivity activity;
    public LoadingHttpObserver(BaseActivity activity) {
        this.activity = activity;
    }
    @Override
    protected void onStart() {
        activity.showLoadingDialog("正在请求中...");
    }
    @Override
    public void onComplete() {
        activity.dissmissLoadingDialog();
        super.onComplete();
    }
    @Override
    public void onError(T error) {
        activity.dissmissLoadingDialog();
        activity.showToast(error == null?"未知错误": TextUtils.isEmpty(error.reason)?error.errorInfo: error.reason);
    }
}
app/src/main/java/com/auto/lyric/retrofit/utils/RetrofitUtils.java
@@ -23,7 +23,6 @@
 */
public class RetrofitUtils {
    public static String HOST_IP_ADDR;
    static RetrofitUtils instance;
    Retrofit retrofit/*log输出,驼峰转换*/,unHumpRetrofit/*log输出,不强制驼峰转换*/,
            unLogRetrofit/*log不输出,驼峰转换*/,unLogHumpRetorfit/*log不输出,不强制驼峰转换*/;
@@ -108,7 +107,7 @@
                //设置OKHttpClient
                .client(client)
                //设置baseUrl,注意,baseUrl必须后缀"/"
                //.baseUrl(BuildConfig.ENVIRONMENT.equals("develop")?HOST_IP_ADDR:BuildConfig.HOST_IP_ADDR)
                .baseUrl(BuildConfig.HOST_IP_ADDR)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
    }
app/src/main/java/com/auto/lyric/service/FloatingWindowService.java
@@ -17,7 +17,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -28,9 +27,7 @@
import com.auto.lyric.databinding.FloatViewBinding;
import com.auto.lyric.util.DeviceUtil;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
@@ -45,7 +42,6 @@
    FloatViewBinding binding;
    final String TAG = "FloatingWindowService";
    final int THREAD_STOP = 0, KEYBOARD_SEND = 100;
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    SimpleDateFormat msFormat = new SimpleDateFormat("mm:ss.SSS");
    boolean pause;//是否暂停
    int progress;//时间进度
@@ -136,14 +132,6 @@
        binding.btnFast.setOnClickListener(v -> progress+=700);
        binding.btnBack.setOnClickListener(v -> progress-=700);
        binding.btnStart.setOnClickListener(v -> {
            try {
                if(new Date().getTime() > dateFormat.parse("2022-09-30 04:00:00").getTime()){
                    Toast.makeText(getApplicationContext(),"软件使用时间已过期", Toast.LENGTH_SHORT).show();
                    return;
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }
            if(binding.btnStart.getText().equals("开启")){
                start();
            }else{
app/src/main/java/com/auto/lyric/ui/collect/Collect.java
New file
@@ -0,0 +1,9 @@
package com.auto.lyric.ui.collect;
/**
 * Created by Runt (qingingrunt2010@qq.com) on 2022/5/29.
 */
public class Collect {
    public int lrdId;
    public String lrcTitle, lrdText;
}
app/src/main/java/com/auto/lyric/ui/collect/CollectActivity.java
New file
@@ -0,0 +1,16 @@
package com.auto.lyric.ui.collect;
import com.auto.lyric.base.activities.BaseTitleBarActivity;
import com.auto.lyric.databinding.ActivityCollectBinding;
/**
 * Created by Runt (qingingrunt2010@qq.com) on 2022/5/29.
 */
public class CollectActivity extends BaseTitleBarActivity<ActivityCollectBinding,CollectViewModel> {
    @Override
    public void initViews() {
    }
}
app/src/main/java/com/auto/lyric/ui/collect/CollectListResult.java
New file
@@ -0,0 +1,14 @@
package com.auto.lyric.ui.collect;
import com.auto.lyric.data.BaseApiResult;
import java.util.List;
/**
 * Created by Runt (qingingrunt2010@qq.com) on 2022/5/29.
 */
public class CollectListResult extends BaseApiResult {
    public List<Collect> list;
}
app/src/main/java/com/auto/lyric/ui/collect/CollectViewModel.java
New file
@@ -0,0 +1,15 @@
package com.auto.lyric.ui.collect;
import com.auto.lyric.base.model.BaseViewModel;
import com.auto.lyric.retrofit.observable.LoadingHttpObserver;
/**
 * Created by Runt (qingingrunt2010@qq.com) on 2022/5/29.
 */
public class CollectViewModel extends BaseViewModel {
    public void getLrcList(LoadingHttpObserver<CollectListResult> observer){
    }
}
app/src/main/java/com/auto/lyric/util/DeviceIdUtils.java
New file
@@ -0,0 +1,124 @@
package com.auto.lyric.util;
import android.annotation.SuppressLint;
import android.content.Context;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import org.json.JSONObject;
import java.lang.reflect.Method;
import java.util.HashSet;
/**
 * copy from: http://docs.aiduoyou.com/web/#/100/1495
 */
public class DeviceIdUtils {
    public static String getDeviceId(Context context, int slotId) {
        try {
            //实例化TelephonyManager对象
            TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            Method method = telephonyManager.getClass().getMethod("getDeviceId", int.class);
            return (String) method.invoke(telephonyManager, slotId);
        } catch (Exception e) {
            //e.printStackTrace();
        }
        return "";
    }
    public static String getImei(Context context, int slotId) {
        try {
            //实例化TelephonyManager对象
            TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            Method method = telephonyManager.getClass().getMethod("getImei", int.class);
            return (String) method.invoke(telephonyManager, slotId);
        } catch (Exception e) {
            //e.printStackTrace();
        }
        return "";
    }
    @SuppressLint("MissingPermission")
    public static String getDeviceId(Context context) {
        try {
            TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            return tm.getDeviceId();
        } catch (Exception e) {
            //e.printStackTrace();
        }
        return "";
    }
    public static String getImei(Context context) {
        try {
            //实例化TelephonyManager对象
            TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            Method method = telephonyManager.getClass().getMethod("getImei");
            return (String) method.invoke(telephonyManager);
        } catch (Exception e) {
            //e.printStackTrace();
        }
        return "";
    }
    public static String getAndroidId(Context context) {
        try {
            return Settings.System.getString(context.getContentResolver(), Settings.System.ANDROID_ID);
        } catch (Exception e) {
            //e.printStackTrace();
        }
        return "";
    }
    public static JSONObject getDeviceIds(Context context) {
        try {
            String imei1 = getDeviceId(context, 0);
            String imei2 = getDeviceId(context, 1);
            String imei3 = getImei(context, 0);
            String imei4 = getImei(context, 1);
            String imei5 = getDeviceId(context);
            String imei6 = getImei(context);
            HashSet<String> hashSet = new HashSet();
            if (!TextUtils.isEmpty(imei1)) {
                hashSet.add(imei1);
            }
            if (!TextUtils.isEmpty(imei2)) {
                hashSet.add(imei2);
            }
            if (!TextUtils.isEmpty(imei3)) {
                hashSet.add(imei3);
            }
            if (!TextUtils.isEmpty(imei4)) {
                hashSet.add(imei4);
            }
            if (!TextUtils.isEmpty(imei5)) {
                hashSet.add(imei5);
            }
            if (!TextUtils.isEmpty(imei6)) {
                hashSet.add(imei6);
            }
            JSONObject jsonObject = new JSONObject();
            int i = 0;
            for (String value : hashSet) {
                i ++;
                jsonObject.put(String.valueOf(i), value);
            }
            String androidId = getAndroidId(context);
            if (!TextUtils.isEmpty(androidId)) {
                jsonObject.put("6", androidId);
            }
            return jsonObject;
        } catch (Exception e) {
            //e.printStackTrace();
        }
        return null;
    }
}
app/src/main/java/com/auto/lyric/util/PreferencesUtils.java
File was renamed from app/src/main/java/com/auto/lyric/util/SpUtils.java
@@ -8,36 +8,35 @@
import java.util.Set;
/**
 * Created by Administrator on 2021/10/28 0028.
 * PreferencesUtils, easy to get or put data
 * <ul>
 * <strong>Preference Name</strong>
 * <li>you can change preference name by {@link #PREFERENCE_NAME}</li>
 * </ul>
 * <ul>
 * <strong>Put Value</strong>
 * </ul>
 *
 * @author <a href="http://www.trinea.cn" target="_blank">Trinea</a> 2013-3-6
 */
public class SpUtils {
public class PreferencesUtils {
    static SpUtils instance;
    /**
     * 获取SP实例
     *
     * @return {@link SpUtils}
     */
    public static SpUtils getInstance() {
        if(instance == null){
            instance = new SpUtils();
        }
        return instance;
    public static final String PREFERENCE_NAME="zipper";
    public static final String PROJECT = "project";
    public static final String USER = "user";
    public static final String VISITOR = "visitor";
    private PreferencesUtils() {
        throw new AssertionError();
    }
    public final static String PROJECT = "project";
    public final static String USER = "user";
    public boolean clearData(Context context, String keyShared){
    public static boolean clearData(Context context, String keyShared){
        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
        return settings.edit().clear().commit();
    }
    public boolean clearData(Context context, String key, String keyShared){
    public static boolean clearData(Context context, String key, String keyShared){
        putString(context,key,null,keyShared);
        return true;
    }
@@ -51,7 +50,7 @@
     * @param key The name of the preference to modify
     * @return True if the new values were successfully written to persistent storage.
     */
    public void  removeKey(Context context, String key, String keyShared){
    public static void  removeKey(Context context, String key, String keyShared){
        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = settings.edit();
@@ -65,7 +64,7 @@
     * @param value The name of the preference to modify
     * @return True if the new values were successfully written to persistent storage.
     */
    public void  removeValue(Context context, String value, String keyShared){
    public static void  removeValue(Context context, String value, String keyShared){
        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = settings.edit();
        editor.remove(value);
@@ -74,13 +73,13 @@
    /**
     * put string preferences
     *
     *
     * @param context
     * @param key The name of the preference to modify
     * @param value The new value for the preference
     * @return True if the new values were successfully written to persistent storage.
     */
    public boolean putString(Context context, String key, String value, String keyShared) {
    public static boolean putString(Context context, String key, String value, String keyShared) {
        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = settings.edit();
        editor.putString(key, value);
@@ -89,40 +88,40 @@
    /**
     * get string preferences
     *
     *
     * @param context
     * @param key The name of the preference to retrieve
     * @return The preference value if it exists, or null. Throws ClassCastException if there is a preference with this
     *         name that is not a string
     * @see #getString(Context, String, String)
     */
    public String getString(Context context, String key, String keyShared) {
    public static String getString(Context context, String key, String keyShared) {
        return getString(context, key, null,keyShared);
    }
    /**
     * get string preferences
     *
     *
     * @param context
     * @param key The name of the preference to retrieve
     * @param defaultValue Value to return if this preference does not exist
     * @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
     *         this name that is not a string
     */
    public String getString(Context context, String key, String defaultValue, String keyShared) {
    public static String getString(Context context, String key, String defaultValue, String keyShared) {
        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
        return settings.getString(key, defaultValue);
    }
    /**
     * put int preferences
     *
     *
     * @param context
     * @param key The name of the preference to modify
     * @param value The new value for the preference
     * @return True if the new values were successfully written to persistent storage.
     */
    public boolean putInt(Context context, String key, int value, String keyShared) {
    public static boolean putInt(Context context, String key, int value, String keyShared) {
        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = settings.edit();
        editor.putInt(key, value);
@@ -131,26 +130,26 @@
    /**
     * get int preferences
     *
     *
     * @param context
     * @param key The name of the preference to retrieve
     * @return The preference value if it exists, or -1. Throws ClassCastException if there is a preference with this
     *         name that is not a int
     */
    public int getInt(Context context, String key, String keyShared) {
    public static int getInt(Context context, String key, String keyShared) {
        return getInt(context, key, -1,keyShared);
    }
    /**
     * get int preferences
     *
     *
     * @param context
     * @param key The name of the preference to retrieve
     * @param defaultValue Value to return if this preference does not exist
     * @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
     *         this name that is not a int
     */
    public int getInt(Context context, String key, int defaultValue, String keyShared) {
    public static int getInt(Context context, String key, int defaultValue, String keyShared) {
        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
        try {
            return settings.getInt(key, defaultValue);
@@ -165,13 +164,13 @@
    /**
     * put long preferences
     *
     *
     * @param context
     * @param key The name of the preference to modify
     * @param value The new value for the preference
     * @return True if the new values were successfully written to persistent storage.
     */
    public boolean putLong(Context context, String key, long value, String keyShared) {
    public static boolean putLong(Context context, String key, long value, String keyShared) {
        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = settings.edit();
        editor.putLong(key, value);
@@ -180,26 +179,26 @@
    /**
     * get long preferences
     *
     *
     * @param context
     * @param key The name of the preference to retrieve
     * @return The preference value if it exists, or -1. Throws ClassCastException if there is a preference with this
     *         name that is not a long
     */
    public long getLong(Context context, String key, String keyShared) {
    public static long getLong(Context context, String key, String keyShared) {
        return getLong(context, key, -1,keyShared);
    }
    /**
     * get long preferences
     *
     *
     * @param context
     * @param key The name of the preference to retrieve
     * @param defaultValue Value to return if this preference does not exist
     * @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
     *         this name that is not a long
     */
    public long getLong(Context context, String key, long defaultValue, String keyShared) {
    public static long getLong(Context context, String key, long defaultValue, String keyShared) {
        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
        try {
            return settings.getLong(key, defaultValue);
@@ -214,13 +213,13 @@
    /**
     * put float preferences
     *
     *
     * @param context
     * @param key The name of the preference to modify
     * @param value The new value for the preference
     * @return True if the new values were successfully written to persistent storage.
     */
    public boolean putFloat(Context context, String key, float value, String keyShared) {
    public static boolean putFloat(Context context, String key, float value, String keyShared) {
        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = settings.edit();
        editor.putFloat(key, value);
@@ -229,26 +228,26 @@
    /**
     * get float preferences
     *
     *
     * @param context
     * @param key The name of the preference to retrieve
     * @return The preference value if it exists, or -1. Throws ClassCastException if there is a preference with this
     *         name that is not a float
     */
    public float getFloat(Context context, String key, String keyShared) {
    public static float getFloat(Context context, String key, String keyShared) {
        return getFloat(context, key, -1,keyShared);
    }
    /**
     * get float preferences
     *
     *
     * @param context
     * @param key The name of the preference to retrieve
     * @param defaultValue Value to return if this preference does not exist
     * @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
     *         this name that is not a float
     */
    public float getFloat(Context context, String key, float defaultValue, String keyShared) {
    public static float getFloat(Context context, String key, float defaultValue, String keyShared) {
        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
        try {
            return settings.getFloat(key, defaultValue);
@@ -263,13 +262,13 @@
    /**
     * put boolean preferences
     *
     *
     * @param context
     * @param key The name of the preference to modify
     * @param value The new value for the preference
     * @return True if the new values were successfully written to persistent storage.
     */
    public boolean putBoolean(Context context, String key, boolean value, String keyShared) {
    public static boolean putBoolean(Context context, String key, boolean value, String keyShared) {
        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = settings.edit();
        editor.putBoolean(key, value);
@@ -278,26 +277,26 @@
    /**
     * get boolean preferences, default is false
     *
     *
     * @param context
     * @param key The name of the preference to retrieve
     * @return The preference value if it exists, or false. Throws ClassCastException if there is a preference with this
     *         name that is not a boolean
     */
    public boolean getBoolean(Context context, String key, String keyShared) {
    public static boolean getBoolean(Context context, String key, String keyShared) {
        return getBoolean(context, key, false,keyShared);
    }
    /**
     * get boolean preferences
     *
     *
     * @param context
     * @param key The name of the preference to retrieve
     * @param defaultValue Value to return if this preference does not exist
     * @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
     *         this name that is not a boolean
     */
    public boolean getBoolean(Context context, String key, boolean defaultValue, String keyShared) {
    public static boolean getBoolean(Context context, String key, boolean defaultValue, String keyShared) {
        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
        try {
            return settings.getBoolean(key, defaultValue);
@@ -318,7 +317,7 @@
     * @param value The new value for the preference ,  the value of set ,canot be the other class out of java collection
     * @return True if the new values were successfully written to persistent storage.
     */
    public boolean putStringSet(Context context, String key, Set value, String keyShared) {
    public static boolean putStringSet(Context context, String key, Set value, String keyShared) {
        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = settings.edit();
        editor.putStringSet(key, value);
@@ -333,7 +332,7 @@
     * @return The preference value if it exists, or false. Throws ClassCastException if there is a preference with this
     *         name that is not a boolean 获取出来的值最终被转换为hashset类型
     */
    public Set getStringSet(Context context, String key, String keyShared) {
    public static Set getStringSet(Context context, String key, String keyShared) {
        return getStringSet(context, key,new ArraySet(),keyShared);
    }
@@ -346,131 +345,8 @@
     * @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
     *         this name that is not a boolean 获取出来的值最终被转换为hashset类型
     */
    public Set getStringSet(Context context, String key, Set defaultValue, String keyShared) {
    public static Set getStringSet(Context context, String key, Set defaultValue, String keyShared) {
        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
        return settings.getStringSet(key, defaultValue);
    }
    public boolean getBooleanValOfUser(Context context, String key){
        return getBoolean(context,key,false,USER);
    }
    public boolean getBooleanValOfProject(Context context, String key){
        return getBoolean(context,key,false,PROJECT);
    }
    public String getStringValOfUser(Context context, String key){
        return getString(context,key,"",USER);
    }
    public String getStringValOfProject(Context context, String key){
        return getString(context,key,"",PROJECT);
    }
    public int getIntValOfProject(Context context, String key){
        return getInt(context,key,0,PROJECT);
    }
    public Long getLongValOfProject(Context context, String key){
        return getLong(context,key,0,PROJECT);
    }
    public float getFloatValOfProject(Context context, String key){
        return getFloat(context,key,0,PROJECT);
    }
    public Set getStringSetValOfProject(Context context, String key){
        return getStringSet(context,key,PROJECT);
    }
    public int getIntValOfUser(Context context, String key){
        return getInt(context,key,0,USER);
    }
    public Long getLongValOfUser(Context context, String key){
        return getLong(context,key,0,USER);
    }
    public float getFloatValOfUser(Context context, String key){
        return getFloat(context,key,0,USER);
    }
    public Set getStringSetValOfUser(Context context, String key){
        return getStringSet(context,key,USER);
    }
    public void putBooleanValOfUser(Context context, String key ,Boolean value){
        putBoolean(context,key,value,USER);
    }
    public void putBooleanValOfProject(Context context, String key,Boolean value){
        putBoolean(context,key,value,PROJECT);
    }
    public void putStringValOfUser(Context context,String key,String value){
        putString(context,key,value,USER);
    }
    public void putStringValOfProject(Context context,String key,String value){
        putString(context,key,value,PROJECT);
    }
    public void putIntValOfProject(Context context,String key,int value){
        putInt(context,key,value,PROJECT);
    }
    public void putLongValOfProject(Context context,String key,long value){
        putLong(context,key,value,PROJECT);
    }
    public void putFloatValOfProject(Context context,String key,float value){
        putFloat(context,key,value,PROJECT);
    }
    public void putStringSetValOfProject(Context context,String key,Set value){
        putStringSet(context,key,value,PROJECT);
    }
    public void putIntValOfUser(Context context,String key,int value){
        putInt(context,key,value,USER);
    }
    public void putLongValOfUser(Context context,String key,long value){
        putLong(context,key,value,USER);
    }
    public void putFloatValOfUser(Context context,String key,float value){
        putFloat(context,key,value,USER);
    }
    public void putStringSetValOfUser(Context context,String key, Set value){
        putStringSet(context,key,value,USER);
    }
    public void removeUserKey(Context context, String key){
        removeKey(context,key,USER);
    }
    public void removeProjectKey(Context context, String key){
        removeKey(context,key,PROJECT);
    }
    public void removeUserValue(Context context, String Value){
        removeValue(context,Value,USER);
    }
    public void removeProjectValue(Context context, String Value){
        removeValue(context,Value,PROJECT);
    }
    public void clearProjectData(Context context){
        clearData(context,PROJECT);
    }
    public void clearUserData(Context context){
        clearData(context,USER);
    }
}
app/src/main/java/com/auto/lyric/util/SHA1UTIL.java
New file
@@ -0,0 +1,64 @@
package com.auto.lyric.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
 * Created by Runt (qingingrunt2010@qq.com) on 2022/5/29.
 */
public class SHA1UTIL {
    public static String getSHA(String info) {
        byte[] bytesSHA = null;
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
            messageDigest.update(info.getBytes());
            bytesSHA = messageDigest.digest();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        String strSHA = byteToHex(bytesSHA);
        return strSHA;
    }
    private static String byteToHex(byte[] bytes) {
        String hs = "";
        String temp;
        for (byte b : bytes) {
            temp = (Integer.toHexString(b & 0XFF));
            if (temp.length() == 1) {
                hs = hs + "0" + temp;
            } else {
                hs = hs + temp;
            }
        }
        return hs;
    }
    /**
     * MD5加密之方法二
     * @explain java实现
     * @param str
     *            待加密字符串
     * @return 16进制加密字符串
     */
    public static String MD5(String str) {
        // 加密后的16进制字符串
        String hexStr = "";
        try {
            // 此 MessageDigest 类为应用程序提供信息摘要算法的功能
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            // 转换为MD5码
            byte[] digest = md5.digest(str.getBytes("utf-8"));
            hexStr = byteToHex(digest);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return hexStr;
    }
}
app/src/main/java/com/auto/lyric/vm/MainViewModel.java
@@ -1,9 +1,52 @@
package com.auto.lyric.vm;
import com.auto.lyric.base.model.BaseViewModel;
import com.auto.lyric.data.ActiveResult;
import com.auto.lyric.retrofit.api.CollectApiCenter;
import com.auto.lyric.retrofit.observable.LoadingHttpObserver;
import com.auto.lyric.retrofit.utils.RetrofitUtils;
import com.auto.lyric.util.DeviceIdUtils;
import com.auto.lyric.util.NetWorkUtils;
import com.auto.lyric.util.SHA1UTIL;
import java.util.Date;
import java.util.UUID;
import io.reactivex.Observable;
/**
 * Created by Runt (qingingrunt2010@qq.com) on 2022/3/20.
 */
public class MainViewModel extends BaseViewModel {
    CollectApiCenter apiCenter;
    public MainViewModel(){
        apiCenter = RetrofitUtils.getInstance().getRetrofit(CollectApiCenter.class);
    }
    public void checkActive(String activeKey,LoadingHttpObserver<ActiveResult> observer){
        String ip = NetWorkUtils.getNetIp();
        String random = UUID.randomUUID().toString();
        long time = new Date().getTime()/1000;
        int userId = 0;
        String deviceId = DeviceIdUtils.getAndroidId(activity);
        String sign = String.format("%s%s%s%s%s%s",activeKey,ip,deviceId ,random,time,userId);
        Observable<ActiveResult> observable = apiCenter.register(activeKey, ip,deviceId, random, time, userId, SHA1UTIL.MD5(SHA1UTIL.getSHA(sign)));
        httpObserverOn(observable,observer);
    }
    public void register(String activeKey,LoadingHttpObserver<ActiveResult> observer){
        String ip = NetWorkUtils.getNetIp();
        String random = UUID.randomUUID().toString();
        long time = new Date().getTime()/1000;
        int userId = 0;
        String deviceId = DeviceIdUtils.getAndroidId(activity);
        String sign = String.format("%s%s%s%s%s%s",activeKey,ip,deviceId ,random,time,userId);
        Observable<ActiveResult> observable = apiCenter.register(activeKey, ip,deviceId, random, time, userId, SHA1UTIL.MD5(SHA1UTIL.getSHA(sign)));
        httpObserverOn(observable,observer);
    }
}
app/src/main/res/drawable-v21/bg_white_divider.xml
New file
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/color_gray">
    <item>
        <selector>
            <item android:state_pressed="true">
                <shape android:shape="rectangle">
                    <solid android:color="@color/gray_pressed" />
                </shape>
            </item>
            <item android:state_enabled="false">
                <shape android:shape="rectangle">
                    <solid android:color="@color/cut_off_line" />
                </shape>
            </item>
            <item>
                <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
                    <!-- 主体背景颜色值 -->
                    <item >
                        <shape>
                            <solid android:color="@color/white"/>
                        </shape>
                    </item>
                    <!-- 连框颜色值 -->
                    <item android:height="1dp" android:gravity="bottom">
                        <shape>
                            <solid android:color="@color/black"/>
                        </shape>
                    </item>
                </layer-list>
            </item>
        </selector>
    </item>
</ripple>
app/src/main/res/layout/activity_collect.xml
New file
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <com.auto.lyric.widgets.TitleBarView
        android:id="@+id/titlebar"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/black"
        app:titleTextColor="@color/white"
        app:titleText="标题"
        app:leftDrawable="@mipmap/icon_white_back"
        android:paddingLeft="20dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintRight_toRightOf="parent" />
    <include layout="@layout/refresh_recycler"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintTop_toBottomOf="@id/titlebar"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
app/src/main/res/layout/activity_main.xml
@@ -27,30 +27,58 @@
        android:layout_height="wrap_content"
        android:text="确认"
        android:layout_margin="10dp"
        app:layout_constraintBottom_toBottomOf="@id/edit"
        app:layout_constraintTop_toBottomOf="@id/edit"
        app:layout_constraintRight_toRightOf="@id/edit"/>
    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/clear"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="清空"
        android:layout_margin="10dp"
        app:layout_constraintRight_toLeftOf="@id/button"
        app:layout_constraintTop_toBottomOf="@id/edit" />
    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/collect"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="收藏"
        android:layout_margin="10dp"
        app:layout_constraintRight_toLeftOf="@id/clear"
        app:layout_constraintTop_toBottomOf="@id/edit" />
    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/save"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="保存"
        android:layout_margin="10dp"
        app:layout_constraintRight_toLeftOf="@id/collect"
        app:layout_constraintLeft_toLeftOf="@id/edit"
        app:layout_constraintTop_toBottomOf="@id/edit" />
    <TextView
        android:id="@+id/txt_local"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_height="50dp"
        android:text="本地歌词"
        android:gravity="center_vertical"
        android:paddingLeft="16dp"
        android:textSize="16sp"
        android:background="@drawable/bg_white"
        app:layout_constraintTop_toBottomOf="@id/edit"
        android:background="@drawable/bg_white_divider"
        app:layout_constraintTop_toBottomOf="@id/save"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"/>
    <TextView
        android:id="@+id/txt_favorite"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_height="50dp"
        android:text="我的收藏"
        android:gravity="center_vertical"
        android:paddingLeft="16dp"
        android:textSize="16sp"
        android:background="@drawable/bg_white"
        android:background="@drawable/bg_white_divider"
        app:layout_constraintTop_toBottomOf="@id/txt_local"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"/>
@@ -59,12 +87,12 @@
    <TextView
        android:id="@+id/txt_setting"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_height="50dp"
        android:text="设置"
        android:gravity="center_vertical"
        android:paddingLeft="16dp"
        android:textSize="16sp"
        android:background="@drawable/bg_white"
        android:background="@drawable/bg_white_divider"
        app:layout_constraintTop_toBottomOf="@id/txt_favorite"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"/>
app/src/main/res/mipmap-xhdpi/icon_white_back.png
app/src/main/res/mipmap-xxhdpi/icon_white_back.png
app/src/main/res/mipmap-xxxhdpi/icon_white_back.png