diff --git a/app/build.gradle b/app/build.gradle index 1cf3f95..460278f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,9 +8,9 @@ android { defaultConfig { applicationId "com.monke.monkeybook" minSdkVersion 17 - targetSdkVersion 28 - versionCode 10 - versionName "1.2.5" + targetSdkVersion 26 + versionCode 11 + versionName "1.3.0" manifestPlaceholders = [UMENG_CHANNEL_VALUE: "debug"] } diff --git a/app/src/main/java/com/monke/monkeybook/MApplication.java b/app/src/main/java/com/monke/monkeybook/MApplication.java index 058a202..61e1345 100644 --- a/app/src/main/java/com/monke/monkeybook/MApplication.java +++ b/app/src/main/java/com/monke/monkeybook/MApplication.java @@ -28,6 +28,7 @@ public class MApplication extends Application { MobclickAgent.startWithConfigure(new MobclickAgent.UMAnalyticsConfig(this, getString(R.string.umeng_key), channel, MobclickAgent.EScenarioType.E_UM_NORMAL, true)); } instance = this; + ProxyManager.initProxy(); startService(new Intent(this, DownloadService.class)); } diff --git a/app/src/main/java/com/monke/monkeybook/ProxyManager.java b/app/src/main/java/com/monke/monkeybook/ProxyManager.java new file mode 100644 index 0000000..dc280c0 --- /dev/null +++ b/app/src/main/java/com/monke/monkeybook/ProxyManager.java @@ -0,0 +1,66 @@ +package com.monke.monkeybook; + +import android.content.SharedPreferences; +import android.content.pm.PackageManager; + +import org.jsoup.helper.StringUtil; + +import java.util.regex.Pattern; + +public class ProxyManager { + public static final String SP_KEY_PROXY_HTTP = "proxy_http"; + public static final String SP_KEY_PROXY_STATE = "proxy_state"; + public static final String PROXY_HTTP_DEFAULT = ""; + public static final boolean PROXY_STATE_DEFAULT = false; + + public static boolean proxyState; + public static String proxyHttp; + private static final String proxyHttpMatch = "(http|ftp|https):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/~\\+#])?";//http正则表达式 + private static final String encode = "代理包名加密key"; + public static String packAgeEncode; //加密后的包名 + public static void saveProxyState(boolean state){ + proxyState = state; + SharedPreferences.Editor editor = MApplication.getInstance().getSharedPreferences("CONFIG", 0).edit(); + editor.putBoolean(SP_KEY_PROXY_STATE,proxyState); + editor.commit(); + } + + private static void initProxyState(){ + try { + packAgeEncode = MApplication.getInstance().getPackageManager().getPackageInfo(MApplication.getInstance().getPackageName(), 0).packageName; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + System.out.println("=================包名获取失败,可能会影响代理请求功能"); + } + proxyState = MApplication.getInstance().getSharedPreferences("CONFIG", 0).getBoolean(SP_KEY_PROXY_STATE,PROXY_STATE_DEFAULT); + } + + public static void saveProxyHttp(String http){ + proxyHttp = http; + SharedPreferences.Editor editor = MApplication.getInstance().getSharedPreferences("CONFIG", 0).edit(); + editor.putString(SP_KEY_PROXY_HTTP,proxyHttp); + editor.commit(); + } + + private static void initProxyHttp(){ + proxyHttp = MApplication.getInstance().getSharedPreferences("CONFIG", 0).getString(SP_KEY_PROXY_HTTP,PROXY_HTTP_DEFAULT); + } + + public static void initProxy(){ + initProxyHttp(); + initProxyState(); + } + + public static boolean hasProxy(){ + if(!proxyState){ + return false; + } + Pattern pattern = Pattern.compile(proxyHttpMatch); + if(!StringUtil.isBlank(proxyHttp) && pattern.matcher(proxyHttp).matches()){ + return true; + }else{ + saveProxyState(false); + return false; + } + } +} diff --git a/app/src/main/java/com/monke/monkeybook/base/MBaseModelImpl.java b/app/src/main/java/com/monke/monkeybook/base/MBaseModelImpl.java new file mode 100644 index 0000000..2be2a60 --- /dev/null +++ b/app/src/main/java/com/monke/monkeybook/base/MBaseModelImpl.java @@ -0,0 +1,41 @@ +package com.monke.monkeybook.base; + +import com.monke.basemvplib.EncodoConverter; +import com.monke.basemvplib.impl.RetryIntercepter; + +import java.util.concurrent.TimeUnit; + +import okhttp3.OkHttpClient; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; +import retrofit2.converter.scalars.ScalarsConverterFactory; + +public class MBaseModelImpl { + protected OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder() + .connectTimeout(10, TimeUnit.SECONDS) + .writeTimeout(10, TimeUnit.SECONDS) + .readTimeout(10, TimeUnit.SECONDS) + .addNetworkInterceptor(new ProxyInterceptor()) + .addInterceptor(new RetryIntercepter(1)); + + protected Retrofit getRetrofitObject(String url) { + return new Retrofit.Builder().baseUrl(url) + //增加返回值为字符串的支持(以实体类返回) + .addConverterFactory(ScalarsConverterFactory.create()) + //增加返回值为Oservable的支持 + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .client(clientBuilder.build()) + .build(); + } + + protected Retrofit getRetrofitString(String url, String encode) { + return new Retrofit.Builder().baseUrl(url) + //增加返回值为字符串的支持(以实体类返回) + .addConverterFactory(EncodoConverter.create(encode)) + //增加返回值为Oservable的支持 + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .client(clientBuilder.build()) + .build(); + } + +} diff --git a/app/src/main/java/com/monke/monkeybook/base/ProxyInterceptor.java b/app/src/main/java/com/monke/monkeybook/base/ProxyInterceptor.java new file mode 100644 index 0000000..8746533 --- /dev/null +++ b/app/src/main/java/com/monke/monkeybook/base/ProxyInterceptor.java @@ -0,0 +1,41 @@ +package com.monke.monkeybook.base; + +import com.monke.monkeybook.ProxyManager; + +import org.jsoup.helper.StringUtil; + +import java.io.IOException; +import java.net.URLEncoder; + +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +public class ProxyInterceptor implements Interceptor { + public ProxyInterceptor() { + + } + + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + if (ProxyManager.hasProxy()) { //如果是代理模式则优先请求代理服务器,失败再自行本地请求 + String url = request.url().toString(); + if (!StringUtil.isBlank(url)) { + url = URLEncoder.encode("url", "utf-8"); + } + Request requestProxy = new Request.Builder() + .url(ProxyManager.proxyHttp) + .header("url", url) + .header("packagename",ProxyManager.packAgeEncode) + .get() + .build(); + Response responseProxy = chain.proceed(request); + if(responseProxy.isSuccessful()){ + return responseProxy; + } + } + Response response = chain.proceed(request); + return response; + } +} diff --git a/app/src/main/java/com/monke/monkeybook/model/impl/GxwztvBookModelImpl.java b/app/src/main/java/com/monke/monkeybook/model/impl/GxwztvBookModelImpl.java index 5e777eb..2f011b8 100644 --- a/app/src/main/java/com/monke/monkeybook/model/impl/GxwztvBookModelImpl.java +++ b/app/src/main/java/com/monke/monkeybook/model/impl/GxwztvBookModelImpl.java @@ -1,8 +1,8 @@ //Copyright (c) 2017. 章钦豪. All rights reserved. package com.monke.monkeybook.model.impl; -import com.monke.basemvplib.impl.BaseModelImpl; import com.monke.monkeybook.ErrorAnalyContentManager; +import com.monke.monkeybook.base.MBaseModelImpl; import com.monke.monkeybook.base.observer.SimpleObserver; import com.monke.monkeybook.bean.BookContentBean; import com.monke.monkeybook.bean.BookInfoBean; @@ -33,7 +33,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.functions.Function; import io.reactivex.schedulers.Schedulers; -public class GxwztvBookModelImpl extends BaseModelImpl implements IGxwztvBookModel { +public class GxwztvBookModelImpl extends MBaseModelImpl implements IGxwztvBookModel { public static final String TAG = "http://www.gxwztv.com"; public static GxwztvBookModelImpl getInstance() { diff --git a/app/src/main/java/com/monke/monkeybook/model/impl/ImportBookModelImpl.java b/app/src/main/java/com/monke/monkeybook/model/impl/ImportBookModelImpl.java index a77c4a1..2ba10c1 100644 --- a/app/src/main/java/com/monke/monkeybook/model/impl/ImportBookModelImpl.java +++ b/app/src/main/java/com/monke/monkeybook/model/impl/ImportBookModelImpl.java @@ -1,7 +1,7 @@ //Copyright (c) 2017. 章钦豪. All rights reserved. package com.monke.monkeybook.model.impl; -import com.monke.basemvplib.impl.BaseModelImpl; +import com.monke.monkeybook.base.MBaseModelImpl; import com.monke.monkeybook.bean.BookShelfBean; import com.monke.monkeybook.bean.ChapterListBean; import com.monke.monkeybook.bean.LocBookShelfBean; @@ -25,7 +25,7 @@ import io.reactivex.Observable; import io.reactivex.ObservableEmitter; import io.reactivex.ObservableOnSubscribe; -public class ImportBookModelImpl extends BaseModelImpl implements IImportBookModel { +public class ImportBookModelImpl extends MBaseModelImpl implements IImportBookModel { public static ImportBookModelImpl getInstance() { return new ImportBookModelImpl(); diff --git a/app/src/main/java/com/monke/monkeybook/model/impl/LingdiankanshuStationBookModelImpl.java b/app/src/main/java/com/monke/monkeybook/model/impl/LingdiankanshuStationBookModelImpl.java index ece3356..d10519a 100644 --- a/app/src/main/java/com/monke/monkeybook/model/impl/LingdiankanshuStationBookModelImpl.java +++ b/app/src/main/java/com/monke/monkeybook/model/impl/LingdiankanshuStationBookModelImpl.java @@ -1,8 +1,8 @@ //Copyright (c) 2017. 章钦豪. All rights reserved. package com.monke.monkeybook.model.impl; -import com.monke.basemvplib.impl.BaseModelImpl; import com.monke.monkeybook.ErrorAnalyContentManager; +import com.monke.monkeybook.base.MBaseModelImpl; import com.monke.monkeybook.base.observer.SimpleObserver; import com.monke.monkeybook.bean.BookContentBean; import com.monke.monkeybook.bean.BookInfoBean; @@ -28,7 +28,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.functions.Function; import io.reactivex.schedulers.Schedulers; -public class LingdiankanshuStationBookModelImpl extends BaseModelImpl implements IStationBookModel { +public class LingdiankanshuStationBookModelImpl extends MBaseModelImpl implements IStationBookModel { public static final String TAG = "http://www.lingdiankanshu.co"; public static LingdiankanshuStationBookModelImpl getInstance() { diff --git a/app/src/main/java/com/monke/monkeybook/view/impl/MainActivity.java b/app/src/main/java/com/monke/monkeybook/view/impl/MainActivity.java index 52d9ad7..423056c 100644 --- a/app/src/main/java/com/monke/monkeybook/view/impl/MainActivity.java +++ b/app/src/main/java/com/monke/monkeybook/view/impl/MainActivity.java @@ -21,12 +21,14 @@ import com.monke.monkeybook.presenter.impl.MainPresenterImpl; import com.monke.monkeybook.view.IMainView; import com.monke.monkeybook.view.adapter.BookShelfAdapter; import com.monke.monkeybook.view.popupwindow.DownloadListPop; +import com.monke.monkeybook.view.popupwindow.ProxyPop; import com.monke.monkeybook.widget.refreshview.OnRefreshWithProgressListener; import com.monke.monkeybook.widget.refreshview.RefreshRecyclerView; import java.util.List; public class MainActivity extends MBaseActivity implements IMainView { + private ImageView ivLogo; private ImageButton ibMoney; private ImageButton ibLibrary; private ImageButton ibAdd; @@ -39,6 +41,7 @@ public class MainActivity extends MBaseActivity implements IMain private ImageView ivWarnClose; private DownloadListPop downloadListPop; + private ProxyPop proxyPop; @Override protected IMainPresenter initInjector() { @@ -62,8 +65,10 @@ public class MainActivity extends MBaseActivity implements IMain @Override protected void bindView() { + proxyPop = new ProxyPop(MainActivity.this); downloadListPop = new DownloadListPop(MainActivity.this); + ivLogo = findViewById(R.id.iv_logo); rfRvShelf = (RefreshRecyclerView) findViewById(R.id.rf_rv_shelf); ibMoney = (ImageButton) findViewById(R.id.ib_money); @@ -80,6 +85,12 @@ public class MainActivity extends MBaseActivity implements IMain @Override protected void bindEvent() { bindRvShelfEvent(); + ivLogo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + proxyPop.showAsDropDown(ivLogo); + } + }); ibDownload.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/app/src/main/java/com/monke/monkeybook/view/popupwindow/DownloadListPop.java b/app/src/main/java/com/monke/monkeybook/view/popupwindow/DownloadListPop.java index 9dc764f..232b36a 100644 --- a/app/src/main/java/com/monke/monkeybook/view/popupwindow/DownloadListPop.java +++ b/app/src/main/java/com/monke/monkeybook/view/popupwindow/DownloadListPop.java @@ -44,7 +44,7 @@ public class DownloadListPop extends PopupWindow { private TextView tvDownload; public DownloadListPop(Context context) { - super(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + super(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); mContext = context; view = LayoutInflater.from(mContext).inflate(R.layout.view_pop_downloadlist, null); this.setContentView(view); diff --git a/app/src/main/java/com/monke/monkeybook/view/popupwindow/ProxyPop.java b/app/src/main/java/com/monke/monkeybook/view/popupwindow/ProxyPop.java new file mode 100644 index 0000000..5620b10 --- /dev/null +++ b/app/src/main/java/com/monke/monkeybook/view/popupwindow/ProxyPop.java @@ -0,0 +1,112 @@ +package com.monke.monkeybook.view.popupwindow; + +import android.content.Context; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.PopupWindow; +import android.widget.TextView; + +import com.daimajia.androidanimations.library.Techniques; +import com.daimajia.androidanimations.library.YoYo; +import com.kyleduo.switchbutton.SwitchButton; +import com.monke.monkeybook.ProxyManager; +import com.monke.monkeybook.R; + +public class ProxyPop extends PopupWindow { + private Context mContext; + private View view; + + private FrameLayout flProxyContent; + private EditText edtProxyHttp; + private SwitchButton sbProxyStart; + + public ProxyPop(Context context) { + super(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + mContext = context; + view = LayoutInflater.from(mContext).inflate(R.layout.view_pop_proxy, null); + this.setContentView(view); + bindView(); + bindEvent(); + + setBackgroundDrawable(mContext.getResources().getDrawable(R.drawable.shape_pop_checkaddshelf_bg)); + setFocusable(true); + setTouchable(true); + setAnimationStyle(R.style.anim_pop_checkaddshelf); + } + + private void bindEvent() { + edtProxyHttp.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (!hasFocus) { + finishInputProxyHttp(true); + } + } + }); + edtProxyHttp.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_DONE) { + finishInputProxyHttp(true); + } + return false; + } + }); + flProxyContent.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finishInputProxyHttp(true); + } + }); + edtProxyHttp.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + edtProxyHttp.setCursorVisible(true); + } + }); + + sbProxyStart.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + finishInputProxyHttp(false); + ProxyManager.saveProxyState(isChecked); + if (isChecked) { + if (!ProxyManager.hasProxy()) { + YoYo.with(Techniques.Shake).playOn(edtProxyHttp); + sbProxyStart.setCheckedImmediatelyNoEvent(false); + } + } + } + }); + } + + private void bindView() { + edtProxyHttp = view.findViewById(R.id.edt_proxy_http); + sbProxyStart = view.findViewById(R.id.sb_proxy_start); + flProxyContent = view.findViewById(R.id.fl_proxy_content); + } + + @Override + public void showAsDropDown(View anchor) { + edtProxyHttp.setText(ProxyManager.proxyHttp); + sbProxyStart.setCheckedImmediatelyNoEvent(ProxyManager.proxyState); + super.showAsDropDown(anchor); + } + + private void finishInputProxyHttp(boolean needUpdateState) { + ProxyManager.saveProxyHttp(edtProxyHttp.getText().toString().trim()); + if (needUpdateState) { + sbProxyStart.setCheckedImmediatelyNoEvent(ProxyManager.hasProxy()); + } + edtProxyHttp.setCursorVisible(false); + InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(edtProxyHttp.getWindowToken(), 0); + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 27b0caa..40253e9 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -20,7 +20,7 @@ diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index 5304a2e..f77fdb0 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -29,7 +29,8 @@ android:hint="搜书名、作者" android:textColorHint="#6f6f6f" android:textSize="13sp" - android:singleLine="true" + android:lines="1" + android:inputType="text" android:imeOptions="actionSearch" android:textColor="#767676" android:cursorVisible="false" diff --git a/app/src/main/res/layout/view_pop_proxy.xml b/app/src/main/res/layout/view_pop_proxy.xml new file mode 100644 index 0000000..c719748 --- /dev/null +++ b/app/src/main/res/layout/view_pop_proxy.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/basemvplib/build.gradle b/basemvplib/build.gradle index 9954a35..e79c94f 100644 --- a/basemvplib/build.gradle +++ b/basemvplib/build.gradle @@ -6,7 +6,7 @@ android { defaultConfig { minSdkVersion 14 - targetSdkVersion 28 + targetSdkVersion 26 versionCode 2 versionName "1.1.0" }