diff --git a/CHANGLOG.md b/CHANGLOG.md new file mode 100644 index 0000000..2a59cd3 --- /dev/null +++ b/CHANGLOG.md @@ -0,0 +1,17 @@ +# Release note for Android Digest + +## Version 0.5 +---------------- +> 1. [增加]设置模块 +> 2. [增加]搜索功能 +> 3. [增加]集成了友盟统计 +> 4. [修复]重构了网络请求的代码,极大的精简优化了SpiceRequest的使用方式 +> 5. [修复]改善了工具箱的UI +> 6. [修复]优化了列表中图片的展示效果 +> 7. [修复]修复了大量的其他bug + +## Version 0.3 +---------------- +> 1. [增加]投稿功能 +> 2. [增加]自动更新 +> 3. [修复]开启开发者选项"不保留活动"导致应用从后台切回来后底部TAB点击无反应问题 diff --git a/README.md b/README.md index 31993bc..93c8473 100644 --- a/README.md +++ b/README.md @@ -3,30 +3,27 @@ ##Next plan -### 0.3 -> 1. 投稿 -> 2. 自动更新 - -###0.4 -> 1. 搜索 - -###0.5 -> 1. 设置模块 - ###0.6 -> 1. 订阅 +> 1. 采用Material Design + +###0.7 +> 1. 重构Server端 ##Screenshot -![app-0.2-screenshot](https://raw.githubusercontent.com/openproject/AndroidDigest/master/release/screenshot-0.2.png) +![screenshot](release/Screen_Capture.png) ##Demo -[下载地址](https://raw.githubusercontent.com/openproject/AndroidDigest/master/release/app-release-0.2.apk) +[下载地址](http://jayfeng-files.stor.sinaapp.com/androiddigest/android_digest_release_v0.5.apk) ##编译 -1. Android Studio - Import Project -2. 重命名gradle.properties.release为gradle.properties或者把里面的内容拷贝到你的gradle.properties,可解决编译时需要release的singingConfig的错误 +1. 复制gradle.properties.release为gradle.properties或者把里面的内容拷贝到你的gradle.properties,可解决编译时需要release的singingConfig的错误 3. 如果提示找不到 sdk xx version 或者build Tool version, 更新一下sdk即可 -#加入我们 +##加入我们 > 1. 提交:Pull Request or Patch > 2. 讨论:QQ群 - 451634330 + +##代码规范 +> 1. 尽量用英文,避免乱码和提高逼格 +> 2. 尽量参考Google Java Style: [中文版](http://www.hawstein.com/posts/google-java-style.html) +> 3. But, if you want, any language, any style is ok, whatever, just do it!!! diff --git a/app/build.gradle b/app/build.gradle index 97605a0..b7953fc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,10 +6,10 @@ android { defaultConfig { applicationId "com.jayfeng.androiddigest" - minSdkVersion 15 + minSdkVersion 11 targetSdkVersion 22 - versionCode 2 - versionName "0.2" + versionCode 4 + versionName "V0.5" } signingConfigs { @@ -38,16 +38,17 @@ android { variant.outputs.each { output -> output.outputFile = new File( output.outputFile.parent, - output.outputFile.name.replace(".apk", "-${variant.versionName}.apk")) + "android_digest_${variant.buildType.name}_${variant.versionName}.apk".toLowerCase()) } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:22.0.0' - compile 'com.jayfeng.lesscode:lesscode-core:0.1.7' - compile 'in.srain.cube:ultra-ptr:1.0.9' + compile 'com.android.support:appcompat-v7:22.2.0' + compile 'com.android.support:design:22.2.0' + compile 'com.jayfeng:lesscode-core:0.4.4' + compile 'in.srain.cube:ultra-ptr:1.0.10' compile('com.octo.android.robospice:robospice-google-http-client:1.4.14') { exclude(group: 'org.apache.httpcomponents', module: 'httpclient') //by both name and group } @@ -57,6 +58,9 @@ dependencies { exclude(group: 'junit', module: 'junit') //by both name and group exclude(group: 'com.google.android', module: 'android') //by both name and group } - compile 'com.facebook.fresco:fresco:0.2.0' + compile 'com.facebook.fresco:fresco:0.5.2+' compile 'com.umeng:fb:5.1.0' + compile 'com.umeng.analytics:analytics:latest.integration' + debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' + releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 999fc68..fcc395a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,9 +2,9 @@ - - - + + + @@ -17,11 +17,11 @@ + android:name="UMENG_APPKEY" + android:value="5527fcbbfd98c5d7d2000383" /> + android:name="UMENG_CHANNEL" + android:value="official" /> - - - + android:name=".activity.AddReviewDigestActivity" + android:label="@string/title_activity_review_digest_add"> + + + + + + + + + diff --git a/app/src/main/java/com/jayfeng/androiddigest/MyApp.java b/app/src/main/java/com/jayfeng/androiddigest/MyApp.java index 7494d69..cc05caa 100644 --- a/app/src/main/java/com/jayfeng/androiddigest/MyApp.java +++ b/app/src/main/java/com/jayfeng/androiddigest/MyApp.java @@ -1,21 +1,43 @@ package com.jayfeng.androiddigest; import android.app.Application; +import android.content.Context; import com.facebook.drawee.backends.pipeline.Fresco; import com.jayfeng.lesscode.core.$; +import com.squareup.leakcanary.LeakCanary; +import com.squareup.leakcanary.RefWatcher; +import com.umeng.analytics.MobclickAgent; public class MyApp extends Application { + private RefWatcher refWatcher; + + public static RefWatcher getRefWatcher(Context context) { + MyApp application = (MyApp) context.getApplicationContext(); + return application.refWatcher; + } + + @Override public void onCreate() { super.onCreate(); + refWatcher = LeakCanary.install(this); + + // umeng analysis + MobclickAgent.updateOnlineConfig(getApplicationContext()); + MobclickAgent.openActivityDurationTrack(false); + Fresco.initialize(this); + // lesscode config $.getInstance() .context(getApplicationContext()) - .log(BuildConfig.DEBUG, "DIGEST") + .log(BuildConfig.DEBUG, "Digest") + .update(null, 3) .build(); + + } } diff --git a/app/src/main/java/com/jayfeng/androiddigest/activity/AboutUsActivity.java b/app/src/main/java/com/jayfeng/androiddigest/activity/AboutUsActivity.java new file mode 100644 index 0000000..2720d6a --- /dev/null +++ b/app/src/main/java/com/jayfeng/androiddigest/activity/AboutUsActivity.java @@ -0,0 +1,34 @@ +package com.jayfeng.androiddigest.activity; + +import android.os.Bundle; +import android.text.TextUtils; +import android.text.method.LinkMovementMethod; +import android.widget.TextView; + +import com.jayfeng.androiddigest.R; +import com.jayfeng.lesscode.core.ViewLess; + +/** + * About us + * Created by Codywang on 2015年4月15日. + */ +public class AboutUsActivity extends BaseActivity { + private String versionName; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_about_us); + showToolbar(); + versionName = getIntent().getStringExtra("versionName"); + initView(); + } + + private void initView() { + if (!TextUtils.isEmpty(versionName)) { + TextView about_us_versionname = ViewLess.$(this, R.id.about_us_versionname); + about_us_versionname.setText(getResources().getString(R.string.settings_update_description) + " " +versionName); + } + TextView about_us_githubpage = ViewLess.$(this, R.id.about_us_githubpage); + about_us_githubpage.setMovementMethod(LinkMovementMethod.getInstance()); + } +} diff --git a/app/src/main/java/com/jayfeng/androiddigest/activity/AddReviewDigestActivity.java b/app/src/main/java/com/jayfeng/androiddigest/activity/AddReviewDigestActivity.java new file mode 100644 index 0000000..4a9e259 --- /dev/null +++ b/app/src/main/java/com/jayfeng/androiddigest/activity/AddReviewDigestActivity.java @@ -0,0 +1,119 @@ +package com.jayfeng.androiddigest.activity; + +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; + +import com.jayfeng.androiddigest.R; +import com.jayfeng.androiddigest.config.Config; +import com.jayfeng.androiddigest.service.HttpClientSpiceService; +import com.jayfeng.androiddigest.webservices.JsonRequest; +import com.jayfeng.androiddigest.webservices.json.ReviewDigestJson; +import com.jayfeng.lesscode.core.ToastLess; +import com.jayfeng.lesscode.core.ViewLess; +import com.octo.android.robospice.SpiceManager; +import com.octo.android.robospice.persistence.exception.SpiceException; +import com.octo.android.robospice.request.listener.RequestListener; + +import java.util.HashMap; + +public class AddReviewDigestActivity extends BaseActivity { + + private SpiceManager spiceManager = new SpiceManager(HttpClientSpiceService.class); + + private EditText titleView; + private EditText abstractView; + private EditText thumbnailView; + private EditText urlView; + private Button submitBtn; + private EditText deliverView; + private EditText reviewerView; + + private String title; + private String abstracts; + private String thumbnail; + private String url; + private String deliver; + private String reviewer; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_review_digest_add); + + showToolbar(); + + initView(); + } + + private void initView() { + titleView = ViewLess.$(this, R.id.title); + abstractView = ViewLess.$(this, R.id.abstracts); + thumbnailView = ViewLess.$(this, R.id.thumbnail); + urlView = ViewLess.$(this, R.id.url); + submitBtn = ViewLess.$(this, R.id.submit); + deliverView = ViewLess.$(this, R.id.deliver); + reviewerView = ViewLess.$(this, R.id.reviewer); + + submitBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + submit(); + } + }); + } + + private void submit() { + title = titleView.getText().toString(); + abstracts = abstractView.getText().toString(); + thumbnail = thumbnailView.getText().toString(); + url = urlView.getText().toString(); + deliver = deliverView.getText().toString(); + reviewer = reviewerView.getText().toString(); + + HashMap postParamters = new HashMap(); + postParamters.put("title", title); + postParamters.put("abstract", abstracts); + postParamters.put("thumbnail", thumbnail); + postParamters.put("url", url); + postParamters.put("deliver", deliver); + postParamters.put("reviewer", reviewer); + + JsonRequest request = new JsonRequest(ReviewDigestJson.class); + request.setUrl(Config.getAddReviewDigestUrl()); + request.setPostParameters(postParamters); + spiceManager.execute(request, new RequestListener() { + @Override + public void onRequestFailure(SpiceException spiceException) { + ToastLess.$(AddReviewDigestActivity.this, "error:" + spiceException.toString()); + } + + @Override + public void onRequestSuccess(ReviewDigestJson reviewDigestJson) { + if ("OK".equals(reviewDigestJson.getS_status())) { + ToastLess.$(AddReviewDigestActivity.this, "success."); + } else { + + ToastLess.$(AddReviewDigestActivity.this, "faillure:" + reviewDigestJson.getS_message()); + } + } + }); + + } + + @Override + public void onStart() { + spiceManager.start(this); + super.onStart(); + } + + @Override + public void onStop() { + if (spiceManager.isStarted()) { + spiceManager.shouldStop(); + } + super.onStop(); + } + +} diff --git a/app/src/main/java/com/jayfeng/androiddigest/activity/BaseActivity.java b/app/src/main/java/com/jayfeng/androiddigest/activity/BaseActivity.java index 457aeab..52c393f 100644 --- a/app/src/main/java/com/jayfeng/androiddigest/activity/BaseActivity.java +++ b/app/src/main/java/com/jayfeng/androiddigest/activity/BaseActivity.java @@ -2,16 +2,25 @@ import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.Toolbar; +import android.text.TextUtils; import android.view.View; import com.jayfeng.androiddigest.R; +import com.umeng.analytics.MobclickAgent; public class BaseActivity extends ActionBarActivity { protected Toolbar toolbar; protected void showToolbar() { + showToolbar(null); + } + + protected void showToolbar(String title) { toolbar = (Toolbar) findViewById(R.id.toolbar); + if (!TextUtils.isEmpty(title)) { + toolbar.setTitle(title); + } toolbar.setTitleTextColor(getResources().getColor(android.R.color.white)); toolbar.showOverflowMenu(); setSupportActionBar(toolbar); @@ -30,4 +39,15 @@ public void onClick(View v) { protected boolean showNavigationIcon() { return true; } + + @Override + protected void onResume() { + super.onResume(); + MobclickAgent.onResume(this); + } + @Override + public void onPause() { + super.onPause(); + MobclickAgent.onPause(this); + } } diff --git a/app/src/main/java/com/jayfeng/androiddigest/activity/DeveloperActivity.java b/app/src/main/java/com/jayfeng/androiddigest/activity/DeveloperActivity.java new file mode 100644 index 0000000..eadd3c2 --- /dev/null +++ b/app/src/main/java/com/jayfeng/androiddigest/activity/DeveloperActivity.java @@ -0,0 +1,16 @@ +package com.jayfeng.androiddigest.activity; + +import android.os.Bundle; + +import com.jayfeng.androiddigest.R; + +public class DeveloperActivity extends BaseActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_developer); + + showToolbar(); + } +} diff --git a/app/src/main/java/com/jayfeng/androiddigest/activity/DigestDetailActivity.java b/app/src/main/java/com/jayfeng/androiddigest/activity/DigestDetailActivity.java index 9d67c51..fcf164f 100644 --- a/app/src/main/java/com/jayfeng/androiddigest/activity/DigestDetailActivity.java +++ b/app/src/main/java/com/jayfeng/androiddigest/activity/DigestDetailActivity.java @@ -6,7 +6,7 @@ import com.jayfeng.androiddigest.R; import com.jayfeng.androiddigest.config.Config; import com.jayfeng.androiddigest.service.HttpClientSpiceService; -import com.jayfeng.androiddigest.webservices.DigestRequest; +import com.jayfeng.androiddigest.webservices.JsonRequest; import com.jayfeng.androiddigest.webservices.json.DigestJson; import com.jayfeng.lesscode.core.ViewLess; import com.octo.android.robospice.SpiceManager; @@ -38,7 +38,7 @@ protected void onCreate(Bundle savedInstanceState) { } private void requestNetworkData() { - DigestRequest request = new DigestRequest(); + JsonRequest request = new JsonRequest(DigestJson.class); request.setUrl(Config.getDigestDetailUrl(id)); spiceManager.getFromCacheAndLoadFromNetworkIfExpired(request, "joke_detail_id_" + id, diff --git a/app/src/main/java/com/jayfeng/androiddigest/activity/MainActivity.java b/app/src/main/java/com/jayfeng/androiddigest/activity/MainActivity.java index 2fb0070..17258bd 100644 --- a/app/src/main/java/com/jayfeng/androiddigest/activity/MainActivity.java +++ b/app/src/main/java/com/jayfeng/androiddigest/activity/MainActivity.java @@ -1,5 +1,7 @@ package com.jayfeng.androiddigest.activity; +import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; @@ -13,16 +15,26 @@ import android.widget.RadioButton; import com.jayfeng.androiddigest.R; -import com.jayfeng.androiddigest.fragment.BlogFragment; -import com.jayfeng.androiddigest.fragment.HomeFragment; -import com.jayfeng.androiddigest.fragment.ToolFragment; +import com.jayfeng.androiddigest.config.Config; +import com.jayfeng.androiddigest.fragment.TabBlogFragment; +import com.jayfeng.androiddigest.fragment.TabHomeFragment; +import com.jayfeng.androiddigest.fragment.TabToolFragment; +import com.jayfeng.androiddigest.service.HttpClientSpiceService; +import com.jayfeng.androiddigest.webservices.JsonRequest; +import com.jayfeng.androiddigest.webservices.json.UpdateJson; +import com.jayfeng.lesscode.core.UpdateLess; import com.jayfeng.lesscode.core.ViewLess; +import com.octo.android.robospice.SpiceManager; +import com.octo.android.robospice.persistence.exception.SpiceException; +import com.octo.android.robospice.request.listener.RequestListener; import com.umeng.fb.FeedbackAgent; public class MainActivity extends BaseActivity implements RadioButton.OnCheckedChangeListener, View.OnClickListener { + private SpiceManager spiceManager = new SpiceManager(HttpClientSpiceService.class); + private FragmentManager fragmentManager; private static final String TAG_HOME = "home"; @@ -46,10 +58,15 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - showToolbar(); - toolbar.setNavigationIcon(R.mipmap.ic_launcher); - toolbar.setSubtitle(R.string.app_description); - toolbar.setSubtitleTextColor(getResources().getColor(android.R.color.white)); + showToolbar(getString(R.string.app_description)); + + //set navigationBar color for Android 5.0 or above + if (android.os.Build.VERSION.SDK_INT>= Build.VERSION_CODES.LOLLIPOP) { + getWindow().setNavigationBarColor(getResources().getColor(R.color.primary_dark)); + } + + //add this to optimize OverDraw + this.getWindow().setBackgroundDrawableResource(android.R.color.transparent); fragmentManager = getSupportFragmentManager(); @@ -58,6 +75,8 @@ protected void onCreate(Bundle savedInstanceState) { // receive the feedback notification agent = new FeedbackAgent(this); agent.sync(); + + requestUpdateData(); } private void init() { @@ -71,9 +90,9 @@ private void init() { toolTabBtn.setOnCheckedChangeListener(this); moreTabBtn.setOnClickListener(this); - currentFragment = homeFragment = new HomeFragment(); - blogFragment = new BlogFragment(); - toolFragment = new ToolFragment(); + currentFragment = homeFragment = new TabHomeFragment(); + blogFragment = new TabBlogFragment(); + toolFragment = new TabToolFragment(); initFragment(); } @@ -83,7 +102,7 @@ private void initFragment() { fragmentTransaction.add(R.id.fragments, homeFragment, TAG_HOME).commit(); } - public void changeFrament(Fragment fragment) { + public void changeFrament(Fragment fragment, String fragmentTag) { if (fragment == currentFragment) { return; @@ -91,7 +110,7 @@ public void changeFrament(Fragment fragment) { FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); if (!fragment.isAdded()) { - fragmentTransaction.hide(currentFragment).add(R.id.fragments, fragment, fragment.getTag()).commit(); + fragmentTransaction.hide(currentFragment).add(R.id.fragments, fragment, fragmentTag).commit(); } else { fragmentTransaction.hide(currentFragment).show(fragment).commit(); } @@ -104,22 +123,13 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { switch (buttonView.getId()) { case R.id.tab_home_btn: - if (homeFragment == null) { - homeFragment = fragmentManager.findFragmentByTag(TAG_HOME); - } - changeFrament(homeFragment); + changeFrament(homeFragment, TAG_HOME); break; case R.id.tab_blog_btn: - if (blogFragment == null) { - blogFragment = fragmentManager.findFragmentByTag(TAG_BLOG); - } - changeFrament(blogFragment); + changeFrament(blogFragment, TAG_BLOG); break; case R.id.tab_tool_btn: - if (toolFragment == null) { - toolFragment = fragmentManager.findFragmentByTag(TAG_TOOL); - } - changeFrament(toolFragment); + changeFrament(toolFragment, TAG_TOOL); break; default: break; @@ -143,8 +153,20 @@ private void popupMoreMenu() { popup.getMenuInflater().inflate(R.menu.more_popup_menu, popup.getMenu()); popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { - if (item.getItemId() == R.id.more_menu_post) { - agent.startFeedbackActivity(); + switch (item.getItemId()) { + case R.id.more_menu_post: + agent.startFeedbackActivity(); + break; + case R.id.more_menu_setting: + Intent settingsIntent = new Intent(MainActivity.this, SettingsActivity.class); + startActivity(settingsIntent); + break; + case R.id.more_menu_developers: + Intent developerIntent = new Intent(MainActivity.this, DeveloperActivity.class); + startActivity(developerIntent); + break; + default: + break; } return true; } @@ -157,6 +179,41 @@ protected boolean showNavigationIcon() { return false; } + + /* + * ============================================================= + * check update + * ============================================================= + */ + + public void requestUpdateData() { + JsonRequest request = new JsonRequest(UpdateJson.class); + request.setUrl(Config.getCheckUpdateUrl()); + spiceManager.execute(request, new RequestListener() { + @Override + public void onRequestFailure(SpiceException spiceException) { + + } + + @Override + public void onRequestSuccess(UpdateJson updateJson) { + UpdateLess.$check(MainActivity.this, + updateJson.getVercode(), + updateJson.getVername(), + updateJson.getDownload(), + updateJson.getLog()); + } + }); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + // when turn on developer option "don't keep activity" + // the fragments show and hide method will be invalid + // and, remove the onSaveInstranceState can fix that + // super.onSaveInstanceState(outState); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. @@ -168,10 +225,28 @@ public boolean onCreateOptionsMenu(Menu menu) { public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.menu_add) { + Intent intent = new Intent(this, AddReviewDigestActivity.class); + startActivity(intent); return true; } else if (id == R.id.menu_search) { + Intent intent = new Intent(this, SearchActivity.class); + startActivity(intent); return true; } return super.onOptionsItemSelected(item); } + + @Override + public void onStart() { + spiceManager.start(this); + super.onStart(); + } + + @Override + public void onStop() { + if (spiceManager.isStarted()) { + spiceManager.shouldStop(); + } + super.onStop(); + } } diff --git a/app/src/main/java/com/jayfeng/androiddigest/activity/SearchActivity.java b/app/src/main/java/com/jayfeng/androiddigest/activity/SearchActivity.java new file mode 100644 index 0000000..0f100ba --- /dev/null +++ b/app/src/main/java/com/jayfeng/androiddigest/activity/SearchActivity.java @@ -0,0 +1,140 @@ +package com.jayfeng.androiddigest.activity; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.RadioButton; + +import com.jayfeng.androiddigest.R; +import com.jayfeng.androiddigest.fragment.DigestListFragment; +import com.jayfeng.androiddigest.fragment.ToolListFragment; +import com.jayfeng.androiddigest.listener.Searchable; +import com.jayfeng.lesscode.core.ViewLess; + + +public class SearchActivity extends BaseActivity + implements RadioButton.OnCheckedChangeListener { + + public static final String KEY_TYPE = "type"; + public static final String TYPE_SEARCH = "search"; + + private static final String TAG_DIGEST = "digest"; + private static final String TAG_TOOL = "tool"; + + private FragmentManager fragmentManager; + + private EditText searchEdit; + private ImageView searchIcon; + private RadioButton digestTabBtn; + private RadioButton toolTabBtn; + + private Fragment currentFragment; + private Fragment digestFragment; + private Fragment toolFragment; + + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_search); + + showToolbar(); + + fragmentManager = getSupportFragmentManager(); + + init(); + } + + private void init() { + searchEdit = ViewLess.$(this, R.id.search_edit); + searchIcon = ViewLess.$(this, R.id.search_icon); + digestTabBtn = ViewLess.$(this, R.id.search_digest_btn); + toolTabBtn = ViewLess.$(this, R.id.search_tool_btn); + + digestTabBtn.setOnCheckedChangeListener(this); + toolTabBtn.setOnCheckedChangeListener(this); + + Bundle bundle = new Bundle(); + bundle.putString(KEY_TYPE, TYPE_SEARCH); + currentFragment = digestFragment = new DigestListFragment(); + toolFragment = new ToolListFragment(); + digestFragment.setArguments(bundle); + toolFragment.setArguments(bundle); + + initFragment(); + + searchIcon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String key = searchEdit.getText().toString(); + ((Searchable)digestFragment).search(key); + // if the toolFragment is not added + // it will crash when searching + if (toolFragment.isAdded()) { + ((Searchable)toolFragment).search(key); + } + } + }); + } + + private void initFragment() { + FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); + fragmentTransaction.add(R.id.fragments, digestFragment, TAG_DIGEST).commit(); + } + + public void changeFrament(Fragment fragment, String fragmentTag) { + + if (fragment == currentFragment) { + return; + } + + FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); + if (!fragment.isAdded()) { + fragmentTransaction.hide(currentFragment).add(R.id.fragments, fragment, fragmentTag).commit(); + } else { + fragmentTransaction.hide(currentFragment).show(fragment).commit(); + } + currentFragment = fragment; + + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + switch (buttonView.getId()) { + case R.id.search_digest_btn: + changeFrament(digestFragment, TAG_DIGEST); + break; + case R.id.search_tool_btn: + // if not search, to search when click the tool tab + if (!toolFragment.isAdded()) { + toolbar.postDelayed(new Runnable() { + @Override + public void run() { + String key = searchEdit.getText().toString(); + if (toolFragment.isAdded()) { + ((Searchable)toolFragment).search(key); + } + } + },1000); + + } + changeFrament(toolFragment, TAG_TOOL); + break; + default: + break; + } + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + // when turn on developer option "don't keep activity" + // the fragments show and hide method will be invalid + // and, remove the onSaveInstranceState can fix that + // super.onSaveInstanceState(outState); + } +} diff --git a/app/src/main/java/com/jayfeng/androiddigest/activity/SettingsActivity.java b/app/src/main/java/com/jayfeng/androiddigest/activity/SettingsActivity.java new file mode 100644 index 0000000..685b3bd --- /dev/null +++ b/app/src/main/java/com/jayfeng/androiddigest/activity/SettingsActivity.java @@ -0,0 +1,237 @@ +package com.jayfeng.androiddigest.activity; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.jayfeng.androiddigest.R; +import com.jayfeng.androiddigest.config.Config; +import com.jayfeng.androiddigest.service.HttpClientSpiceService; +import com.jayfeng.androiddigest.webservices.JsonRequest; +import com.jayfeng.androiddigest.webservices.json.UpdateJson; +import com.jayfeng.lesscode.core.AppLess; +import com.jayfeng.lesscode.core.ToastLess; +import com.jayfeng.lesscode.core.UpdateLess; +import com.jayfeng.lesscode.core.ViewLess; +import com.octo.android.robospice.SpiceManager; +import com.octo.android.robospice.persistence.exception.SpiceException; +import com.octo.android.robospice.request.listener.RequestListener; + +import java.io.File; +import java.text.DecimalFormat; + +/** + * Settings + * Created by Codywang on 2015年4月15日. + */ +public class SettingsActivity extends BaseActivity implements View.OnClickListener { + + public static final int MSG_CLEAR_CACHE_COMPLET = 100; + + private SpiceManager spiceManager = new SpiceManager(HttpClientSpiceService.class); + + private TextView cacheSizeView; + + private Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + switch (msg.what) { + case MSG_CLEAR_CACHE_COMPLET: + cacheSizeView.setText(getResources().getString(R.string.settings_wipe_cache_description) + " " + getCacheSize(SettingsActivity.this)); + break; + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_settings); + showToolbar(); + + initView(); + } + + private void initView() { + LinearLayout ratingAppContainer = ViewLess.$(this, R.id.ratingapp_container); + LinearLayout updateAppContainer = ViewLess.$(this, R.id.updateapp_container); + LinearLayout wipeCacheContainer = ViewLess.$(this, R.id.wipecache_container); + LinearLayout aboutusContainer = ViewLess.$(this, R.id.aboutus_container); + TextView currentVersionView = ViewLess.$(this, R.id.version_current); + cacheSizeView = ViewLess.$(this, R.id.settings_cachesize_tv); + + ratingAppContainer.setOnClickListener(this); + updateAppContainer.setOnClickListener(this); + wipeCacheContainer.setOnClickListener(this); + aboutusContainer.setOnClickListener(this); + + cacheSizeView.setText(getResources().getString(R.string.settings_wipe_cache_description) + " " + getCacheSize(this)); + currentVersionView.setText(getResources().getString(R.string.settings_update_description) + " " + AppLess.$vername(this)); + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.ratingapp_container: + ratingApp(); + break; + case R.id.updateapp_container: + requestUpdateData(); + break; + case R.id.wipecache_container: + wipeAppCahce(); + break; + case R.id.aboutus_container: + Intent intent = new Intent(this, AboutUsActivity.class); + intent.putExtra("versionName", AppLess.$vername(this)); + startActivity(intent); + break; + default: + break; + } + } + + /** + * clean the cache + */ + private void wipeAppCahce() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.settings_confirm_wipecache); + builder.setPositiveButton(R.string.action_confirm, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + new Thread(new Runnable() { + @Override + public void run() { + try { + deleteFile(SettingsActivity.this.getCacheDir()); + } catch (Exception e) { + e.printStackTrace(); + } + mHandler.sendEmptyMessage(MSG_CLEAR_CACHE_COMPLET); + } + }).start(); + } + }); + builder.setNegativeButton(R.string.action_negative, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + builder.create().show(); + + } + + /** + * open the app in market + */ + private void ratingApp() { + Uri uri = Uri.parse("market://details?id=" + getPackageName()); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (intent.resolveActivity(getPackageManager()) != null) { + startActivity(intent); + } else { + Toast.makeText(this, R.string.settings_rating_no_market, Toast.LENGTH_SHORT).show(); + } + } + + /** + * check update + */ + private void requestUpdateData() { + JsonRequest request = new JsonRequest(UpdateJson.class); + request.setUrl(Config.getCheckUpdateUrl()); + spiceManager.execute(request, new RequestListener() { + @Override + public void onRequestFailure(SpiceException spiceException) { + + } + + @Override + public void onRequestSuccess(UpdateJson updateJson) { + boolean hasUpdate = UpdateLess.$check(SettingsActivity.this, + updateJson.getVercode(), + updateJson.getVername(), + updateJson.getDownload(), + updateJson.getLog()); + // if no update, toast a no update tips + if (!hasUpdate) { + ToastLess.$(SettingsActivity.this, R.string.settings_update_no_update); + } + } + }); + } + + private String getCacheSize(Context context) { + File cacheDir = context.getCacheDir(); + double size = 0; + long dirSize = getDirSize(cacheDir); + size = (dirSize + 0.0) / (1024 * 1024); + + DecimalFormat df = new DecimalFormat("0.0"); + String filesize = df.format(size); + return filesize + " M"; + } + + private long getDirSize(File dir) { + if (dir == null) { + return 0; + } + if (!dir.isDirectory()) { + return 0; + } + long dirSize = 0; + File[] files = dir.listFiles(); + for (File file : files) { + if (file.isFile()) { + dirSize += file.length(); + } else if (file.isDirectory()) { + dirSize += file.length(); + dirSize += getDirSize(file); + } + } + return dirSize; + } + + private void deleteFile(File file) throws Exception { + if (file != null && file.exists() && file.isDirectory()) { + File[] listFile = file.listFiles(); + if (listFile != null) { + for (File file1 : listFile) { + if (file1.isDirectory()) { + deleteFile(file1); + file1.delete(); + } else { + file1.delete(); + } + } + } + } + } + + @Override + protected void onStart() { + spiceManager.start(this); + super.onStart(); + } + + @Override + protected void onStop() { + if (spiceManager.isStarted()) { + spiceManager.shouldStop(); + } + super.onStop(); + } +} diff --git a/app/src/main/java/com/jayfeng/androiddigest/activity/ToolListActivity.java b/app/src/main/java/com/jayfeng/androiddigest/activity/ToolListActivity.java index 233f67c..64388f0 100644 --- a/app/src/main/java/com/jayfeng/androiddigest/activity/ToolListActivity.java +++ b/app/src/main/java/com/jayfeng/androiddigest/activity/ToolListActivity.java @@ -1,19 +1,35 @@ package com.jayfeng.androiddigest.activity; +import android.content.Intent; +import android.graphics.drawable.Animatable; +import android.net.Uri; import android.os.Bundle; +import android.support.annotation.Nullable; +import android.text.TextUtils; +import android.view.ContextMenu; +import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.controller.BaseControllerListener; +import com.facebook.drawee.controller.ControllerListener; +import com.facebook.drawee.interfaces.DraweeController; +import com.facebook.drawee.view.SimpleDraweeView; +import com.facebook.imagepipeline.image.ImageInfo; import com.jayfeng.androiddigest.R; import com.jayfeng.androiddigest.config.Config; import com.jayfeng.androiddigest.service.HttpClientSpiceService; -import com.jayfeng.androiddigest.webservices.ToolListRequest; +import com.jayfeng.androiddigest.webservices.JsonRequest; +import com.jayfeng.androiddigest.webservices.json.OfflineJson; import com.jayfeng.androiddigest.webservices.json.ToolJson; import com.jayfeng.androiddigest.webservices.json.ToolListJson; import com.jayfeng.lesscode.core.AdapterLess; +import com.jayfeng.lesscode.core.DisplayLess; import com.jayfeng.lesscode.core.EncodeLess; import com.jayfeng.lesscode.core.ViewLess; import com.octo.android.robospice.SpiceManager; @@ -29,6 +45,9 @@ public class ToolListActivity extends BaseActivity { + private static final int CONTEXT_ITEM_OPEN_IN_BROWSER = 0; + + public static final String KEY_TITLE = "title"; public static final String KEY_TYPE = "type"; private SpiceManager spiceManager = new SpiceManager(HttpClientSpiceService.class); @@ -40,6 +59,7 @@ public class ToolListActivity extends BaseActivity { private PtrClassicFrameLayout ptrFrame; private View errorView; + private String title; private String type; @Override @@ -49,7 +69,11 @@ protected void onCreate(Bundle savedInstanceState) { showToolbar(); + title = getIntent().getStringExtra(KEY_TITLE); type = getIntent().getStringExtra(KEY_TYPE); + if (title != null) { + setTitle(title); + } listView = ViewLess.$(this, R.id.listview); errorView = ViewLess.$(this, R.id.error); @@ -80,21 +104,19 @@ public void run() { listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { -// String type = listData.get(position).getType(); -// String url = listData.get(position).getUrl(); -// String title = listData.get(position).getTitle(); -// if (Config.OFFLINE_TYPE_DIR.equals(type)) { -// Intent intent = new Intent(ToolListActivity.this, OfflineActivity.class); -// intent.putExtra(OfflineActivity.KEY_URL, url); -// intent.putExtra(OfflineActivity.KEY_TITLE, title); -// startActivity(intent); -// } else if (Config.OFFLINE_TYPE_HTML.equals(type)) { -// Intent intent = new Intent(ToolListActivity.this, WebViewActivity.class); -// intent.putExtra(WebViewActivity.KEY_URL, url); -// startActivity(intent); -// } + if (position < 0 || position>= listData.size()) { + return; + } + String url = listData.get(position).getHomepage(); + if (TextUtils.isEmpty(url)) { + url = listData.get(position).getUrl(); + } + Intent intent = new Intent(ToolListActivity.this, WebViewActivity.class); + intent.putExtra(WebViewActivity.KEY_URL, url); + startActivity(intent); } }); + registerForContextMenu(listView); } /* @@ -104,7 +126,7 @@ public void onItemClick(AdapterView parent, View view, int position, long id) */ private void requestNetworkData() { - ToolListRequest request = new ToolListRequest(); + JsonRequest request = new JsonRequest(ToolListJson.class); request.setUrl(getListUrl()); spiceManager.getFromCacheAndLoadFromNetworkIfExpired(request, getCacheKey(), @@ -159,9 +181,50 @@ private void fillAdapterToListView(ToolListJson toolListJson) { public View getView(int i, View view, AdapterLess.ViewHolder viewHolder, ToolJson toolJson) { TextView titleView = viewHolder.$view(view, R.id.title); TextView descriptionView = viewHolder.$view(view, R.id.description); + final SimpleDraweeView draweeView = viewHolder.$view(view,R.id.thumbnail); titleView.setText(toolJson.getTitle()); descriptionView.setText(toolJson.getDescription()); + if (!TextUtils.isEmpty(toolJson.getThumbnail())) { + Uri uri = Uri.parse(toolJson.getThumbnail()); + ControllerListener controllerListener = new BaseControllerListener() { + @Override + public void onFinalImageSet( + String id, + @Nullable ImageInfo imageInfo, + @Nullable Animatable anim) { + if (imageInfo.getWidth()> imageInfo.getHeight()) { + draweeView.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT; + } else { + draweeView.getLayoutParams().width = DisplayLess.$dp2px(200); + } + draweeView.setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight()); + } + + @Override + public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) { + if (imageInfo.getWidth()> imageInfo.getHeight()) { + draweeView.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT; + } else { + draweeView.getLayoutParams().width = DisplayLess.$dp2px(200); + } + draweeView.setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight()); + } + + @Override + public void onFailure(String id, Throwable throwable) { + } + }; + DraweeController controller = Fresco.newDraweeControllerBuilder() + .setControllerListener(controllerListener) + .setUri(uri) + .setAutoPlayAnimations(true) + .build(); + draweeView.setController(controller); + draweeView.setVisibility(View.VISIBLE); + } else { + draweeView.setVisibility(View.GONE); + } return view; } }); @@ -176,6 +239,33 @@ private String getCacheKey() { return EncodeLess.$md5(getListUrl()); } + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + menu.setHeaderTitle("More"); + menu.add(0, CONTEXT_ITEM_OPEN_IN_BROWSER, 0, "Open in browser"); + super.onCreateContextMenu(menu, v, menuInfo); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + switch (item.getItemId()) { + case CONTEXT_ITEM_OPEN_IN_BROWSER: + ToolJson toolJson = listData.get(menuInfo.position); + String url = toolJson.getHomepage(); + if (TextUtils.isEmpty(url)) { + url = toolJson.getUrl(); + } + Intent intent = new Intent(); + intent.setAction("android.intent.action.VIEW"); + Uri content_url = Uri.parse(url); + intent.setData(content_url); + startActivity(intent); + return true; + } + return super.onContextItemSelected(item); + } + @Override public void onStart() { spiceManager.start(this); diff --git a/app/src/main/java/com/jayfeng/androiddigest/config/Config.java b/app/src/main/java/com/jayfeng/androiddigest/config/Config.java index f21e062..68abe24 100644 --- a/app/src/main/java/com/jayfeng/androiddigest/config/Config.java +++ b/app/src/main/java/com/jayfeng/androiddigest/config/Config.java @@ -11,8 +11,8 @@ public class Config { public static final int PAGE_SIZE = 15; public static final int PAGE_START = 1; - public static final String JOKE_TYPE_TEXT = "text"; - public static final String JOKE_TYPE_HTML = "html"; + public static final String DIGEST_TYPE_TEXT = "text"; + public static final String DIGEST_TYPE_HTML = "html"; public static final String BLOG_TYPE_HEADER = "header"; @@ -45,6 +45,18 @@ public static String getDigestDetailUrl(int id) { return sDomain + "android/digest/detail.php?id=" + id; } + public static String getSearchDigestListUrl(String key, int page, int size) { + return sDomain + "android/digest/search.php?key=" + key + "&page=" + page + "&size=" + size; + } + + public static String getSearchToolListUrl(String key, int page, int size) { + return sDomain + "android/tool/search.php?key=" + key + "&page=" + page + "&size=" + size; + } + + public static String getAddReviewDigestUrl() { + return sDomain + "android/digest/review_post.php"; + } + public static String getBlogListUrl() { return sDomain + "android/blog/list.json"; } @@ -61,10 +73,6 @@ public static String getReviewDigestListUrl(int page, int size) { return sDomain + "android/digest/list.php?table=android_digest_review&page=" + page + "&size=" + size; } - public static String postReviewDigestUrl() { - return sDomain + "android/digest/review_post.php"; - } - public static String getOfflineListUrl() { return sDomain + "android/offline/list.json"; } diff --git a/app/src/main/java/com/jayfeng/androiddigest/fragment/BaseFragment.java b/app/src/main/java/com/jayfeng/androiddigest/fragment/BaseFragment.java new file mode 100644 index 0000000..7022d1d --- /dev/null +++ b/app/src/main/java/com/jayfeng/androiddigest/fragment/BaseFragment.java @@ -0,0 +1,28 @@ +package com.jayfeng.androiddigest.fragment; + +import android.support.v4.app.Fragment; + +import com.jayfeng.androiddigest.MyApp; +import com.squareup.leakcanary.RefWatcher; +import com.umeng.analytics.MobclickAgent; + +public class BaseFragment extends Fragment { + + public void onResume() { + super.onResume(); + MobclickAgent.onPageStart(getClass().getSimpleName()); + } + + public void onPause() { + super.onPause(); + MobclickAgent.onPageEnd(getClass().getSimpleName()); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + RefWatcher refWatcher = MyApp.getRefWatcher(getActivity()); + refWatcher.watch(this); + } +} diff --git a/app/src/main/java/com/jayfeng/androiddigest/fragment/DigestListFragment.java b/app/src/main/java/com/jayfeng/androiddigest/fragment/DigestListFragment.java index 2c89d04..031cc31 100644 --- a/app/src/main/java/com/jayfeng/androiddigest/fragment/DigestListFragment.java +++ b/app/src/main/java/com/jayfeng/androiddigest/fragment/DigestListFragment.java @@ -2,13 +2,16 @@ import android.content.Intent; +import android.graphics.drawable.Animatable; import android.net.Uri; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.text.TextPaint; import android.text.TextUtils; +import android.view.ContextMenu; import android.view.LayoutInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; @@ -19,21 +22,26 @@ import android.widget.ListView; import android.widget.TextView; +import com.facebook.common.logging.FLog; import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.controller.BaseControllerListener; +import com.facebook.drawee.controller.ControllerListener; import com.facebook.drawee.interfaces.DraweeController; import com.facebook.drawee.view.SimpleDraweeView; +import com.facebook.imagepipeline.image.ImageInfo; +import com.facebook.imagepipeline.image.QualityInfo; import com.jayfeng.androiddigest.R; import com.jayfeng.androiddigest.activity.DigestDetailActivity; +import com.jayfeng.androiddigest.activity.SearchActivity; import com.jayfeng.androiddigest.activity.WebViewActivity; import com.jayfeng.androiddigest.config.Config; +import com.jayfeng.androiddigest.listener.Searchable; import com.jayfeng.androiddigest.service.HttpClientSpiceService; -import com.jayfeng.androiddigest.webservices.DigestListRequest; -import com.jayfeng.androiddigest.webservices.UpdateRequest; +import com.jayfeng.androiddigest.webservices.JsonRequest; import com.jayfeng.androiddigest.webservices.json.DigestJson; import com.jayfeng.androiddigest.webservices.json.DigestListJson; -import com.jayfeng.androiddigest.webservices.json.UpdateJson; import com.jayfeng.lesscode.core.AdapterLess; -import com.jayfeng.lesscode.core.UpdateLess; +import com.jayfeng.lesscode.core.DisplayLess; import com.jayfeng.lesscode.core.ViewLess; import com.octo.android.robospice.SpiceManager; import com.octo.android.robospice.persistence.DurationInMillis; @@ -46,7 +54,9 @@ import in.srain.cube.views.ptr.PtrDefaultHandler; import in.srain.cube.views.ptr.PtrFrameLayout; -public class DigestListFragment extends Fragment implements OnScrollListener { +public class DigestListFragment extends BaseFragment implements OnScrollListener, Searchable { + + private static final int CONTEXT_ITEM_OPEN_IN_BROWSER = 0; private SpiceManager spiceManager = new SpiceManager(HttpClientSpiceService.class); @@ -63,6 +73,9 @@ public class DigestListFragment extends Fragment implements OnScrollListener { private int page = Config.PAGE_START; private int size = Config.PAGE_SIZE; + private boolean isSearch = false; + private String searchKey; + public DigestListFragment() { // Required empty public constructor } @@ -71,7 +84,10 @@ public DigestListFragment() { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - requestUpdateData(); + if (getArguments() != null + &&SearchActivity.TYPE_SEARCH.equals(getArguments().getString(SearchActivity.KEY_TYPE))) { + isSearch = true; + } } @Override @@ -98,33 +114,38 @@ public void onRefreshBegin(PtrFrameLayout frame) { @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { - return PtrDefaultHandler.checkContentCanBePulledDown(frame, listView, header) ; + return PtrDefaultHandler.checkContentCanBePulledDown(frame, listView, header); } }); + registerForContextMenu(listView); return contentView; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - showCacheData(); - ptrFrame.postDelayed(new Runnable() { - @Override - public void run() { - ptrFrame.autoRefresh(); - } - }, 100); + if (!isSearch) { + showCacheData(); + ptrFrame.postDelayed(new Runnable() { + @Override + public void run() { + ptrFrame.autoRefresh(); + } + }, 100); + } listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { + if (position < 0 || position>= listData.size()) { + return; + } DigestJson digestJson = listData.get(position); String type = digestJson.getType(); - if (Config.JOKE_TYPE_HTML.equals(type)) { + if (Config.DIGEST_TYPE_HTML.equals(type)) { String url = digestJson.getUrl(); Intent intent = new Intent(getActivity(), WebViewActivity.class); -// url = "http://www.baidu.com"; intent.putExtra(WebViewActivity.KEY_URL, url); startActivity(intent); } else { @@ -145,10 +166,10 @@ public void onItemClick(AdapterView parent, View view, int position, long id) */ private void requestNetworkData() { - DigestListRequest request = new DigestListRequest(); - request.setUrl(Config.getDigestListUrl(page, size)); + JsonRequest request = new JsonRequest(DigestListJson.class); + request.setUrl(getUrl(page, size)); spiceManager.getFromCacheAndLoadFromNetworkIfExpired(request, - "digest_list_page_" + page + "_size_" + size, + getCacheKey(page, size), DurationInMillis.NEVER, new RequestListener() { @Override public void onRequestFailure(SpiceException spiceException) { @@ -175,7 +196,7 @@ public void onRequestSuccess(DigestListJson digestListJson) { private void showCacheData() { spiceManager.getFromCache(DigestListJson.class, - "digest_list_page_" + page + "_size_" + size, + getCacheKey(page, size), DurationInMillis.ALWAYS_RETURNED, new RequestListener() { @Override public void onRequestFailure(SpiceException spiceException) { @@ -202,7 +223,7 @@ private void fillAdapterToListView(DigestListJson digestListJson) { public View getView(int i, View view, AdapterLess.ViewHolder viewHolder, DigestJson digestJson) { TextView titleView = viewHolder.$view(view, R.id.title); TextView abstractView = viewHolder.$view(view, R.id.abstracts); - SimpleDraweeView draweeView = viewHolder.$view(view,R.id.thumbnail); + final SimpleDraweeView draweeView = viewHolder.$view(view,R.id.thumbnail); ImageView moreView = viewHolder.$view(view, R.id.more); if (TextUtils.isEmpty(digestJson.getTitle())) { @@ -223,7 +244,36 @@ public View getView(int i, View view, AdapterLess.ViewHolder viewHolder, DigestJ if (!TextUtils.isEmpty(digestJson.getThumbnail())) { Uri uri = Uri.parse(digestJson.getThumbnail()); + ControllerListener controllerListener = new BaseControllerListener() { + @Override + public void onFinalImageSet( + String id, + @Nullable ImageInfo imageInfo, + @Nullable Animatable anim) { + if (imageInfo.getWidth()> imageInfo.getHeight()) { + draweeView.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT; + } else { + draweeView.getLayoutParams().width = DisplayLess.$dp2px(200); + } + draweeView.setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight()); + } + + @Override + public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) { + if (imageInfo.getWidth()> imageInfo.getHeight()) { + draweeView.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT; + } else { + draweeView.getLayoutParams().width = DisplayLess.$dp2px(200); + } + draweeView.setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight()); + } + + @Override + public void onFailure(String id, Throwable throwable) { + } + }; DraweeController controller = Fresco.newDraweeControllerBuilder() + .setControllerListener(controllerListener) .setUri(uri) .setAutoPlayAnimations(true) .build(); @@ -238,6 +288,12 @@ public View getView(int i, View view, AdapterLess.ViewHolder viewHolder, DigestJ } }); listView.setAdapter(adapter); + if (listData.size() < Config.PAGE_SIZE) { + if (listView.getFooterViewsCount()> 0) { + listView.removeFooterView(footerView); + } + noMoreData = true; + } } /* @@ -248,8 +304,8 @@ public View getView(int i, View view, AdapterLess.ViewHolder viewHolder, DigestJ private void moreNetworkData() { int nextPage = page + 1; - DigestListRequest request = new DigestListRequest(); - request.setUrl(Config.getDigestListUrl(nextPage, size)); + JsonRequest request = new JsonRequest(DigestListJson.class); + request.setUrl(getUrl(nextPage, size)); spiceManager.execute(request, new RequestListener() { @Override public void onRequestFailure(SpiceException spiceException) { @@ -286,6 +342,35 @@ private void resetPage() { } } + /* + * ============================================================= + * search page data + * ============================================================= + */ + + @Override + public void search(String key) { + searchKey = key; + + ptrFrame.postDelayed(new Runnable() { + @Override + public void run() { + ptrFrame.autoRefresh(); + } + }, 100); + } + + private String getUrl(int page, int size) { + if (isSearch) { + return Config.getSearchDigestListUrl(searchKey, page, size); + } + return Config.getDigestListUrl(page, size); + } + + private String getCacheKey(int page, int size) { + return (isSearch ? "search_" + searchKey + "_" : "") + "digest_list_page_" + page + "_size_" + size; + } + @Override public void onScrollStateChanged(AbsListView view, int scrollState) { int lastIndex = adapter.getCount(); @@ -301,30 +386,30 @@ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCoun visibleLastIndex = firstVisibleItem + visibleItemCount - 1; } - /* - * ============================================================= - * check update - * ============================================================= - */ - - public void requestUpdateData() { - UpdateRequest request = new UpdateRequest(); - request.setUrl(Config.getCheckUpdateUrl()); - spiceManager.execute(request, new RequestListener() { - @Override - public void onRequestFailure(SpiceException spiceException) { - - } + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + menu.setHeaderTitle("More"); + menu.add(0, CONTEXT_ITEM_OPEN_IN_BROWSER, 0, "Open in browser"); + super.onCreateContextMenu(menu, v, menuInfo); + } - @Override - public void onRequestSuccess(UpdateJson updateJson) { - UpdateLess.$check(getActivity(), - updateJson.getVercode(), - updateJson.getVername(), - updateJson.getDownload(), - updateJson.getLog()); - } - }); + @Override + public boolean onContextItemSelected(MenuItem item) { + AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + switch (item.getItemId()) { + case CONTEXT_ITEM_OPEN_IN_BROWSER: + DigestJson digestJson = listData.get(menuInfo.position); + String type = digestJson.getType(); + if (Config.DIGEST_TYPE_HTML.equals(type)) { + Intent intent = new Intent(); + intent.setAction("android.intent.action.VIEW"); + Uri content_url = Uri.parse(digestJson.getUrl()); + intent.setData(content_url); + startActivity(intent); + } + return true; + } + return super.onContextItemSelected(item); } @Override diff --git a/app/src/main/java/com/jayfeng/androiddigest/fragment/OfflineFragment.java b/app/src/main/java/com/jayfeng/androiddigest/fragment/OfflineFragment.java index 7da71bf..b1189d2 100644 --- a/app/src/main/java/com/jayfeng/androiddigest/fragment/OfflineFragment.java +++ b/app/src/main/java/com/jayfeng/androiddigest/fragment/OfflineFragment.java @@ -19,7 +19,7 @@ import com.jayfeng.androiddigest.activity.WebViewActivity; import com.jayfeng.androiddigest.config.Config; import com.jayfeng.androiddigest.service.HttpClientSpiceService; -import com.jayfeng.androiddigest.webservices.OfflineListRequest; +import com.jayfeng.androiddigest.webservices.JsonRequest; import com.jayfeng.androiddigest.webservices.json.OfflineJson; import com.jayfeng.androiddigest.webservices.json.OfflineListJson; import com.jayfeng.lesscode.core.AdapterLess; @@ -36,7 +36,7 @@ import in.srain.cube.views.ptr.PtrDefaultHandler; import in.srain.cube.views.ptr.PtrFrameLayout; -public class OfflineFragment extends Fragment { +public class OfflineFragment extends BaseFragment { private SpiceManager spiceManager = new SpiceManager(HttpClientSpiceService.class); @@ -99,6 +99,9 @@ public void run() { listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { + if (position < 0 || position>= listData.size()) { + return; + } String type = listData.get(position).getType(); String url = listData.get(position).getUrl(); String title = listData.get(position).getTitle(); @@ -124,7 +127,7 @@ public void onItemClick(AdapterView parent, View view, int position, long id) */ private void requestNetworkData() { - OfflineListRequest request = new OfflineListRequest(); + JsonRequest request = new JsonRequest(OfflineListJson.class); request.setUrl(getListUrl()); spiceManager.getFromCacheAndLoadFromNetworkIfExpired(request, getCacheKey(), diff --git a/app/src/main/java/com/jayfeng/androiddigest/fragment/ReviewDigestListFragment.java b/app/src/main/java/com/jayfeng/androiddigest/fragment/ReviewDigestListFragment.java index fc8608c..d5fcdf9 100644 --- a/app/src/main/java/com/jayfeng/androiddigest/fragment/ReviewDigestListFragment.java +++ b/app/src/main/java/com/jayfeng/androiddigest/fragment/ReviewDigestListFragment.java @@ -22,14 +22,14 @@ import com.facebook.drawee.interfaces.DraweeController; import com.facebook.drawee.view.SimpleDraweeView; import com.jayfeng.androiddigest.R; -import com.jayfeng.androiddigest.activity.DigestDetailActivity; import com.jayfeng.androiddigest.activity.WebViewActivity; import com.jayfeng.androiddigest.config.Config; import com.jayfeng.androiddigest.service.HttpClientSpiceService; -import com.jayfeng.androiddigest.webservices.ReviewDigestListRequest; +import com.jayfeng.androiddigest.webservices.JsonRequest; import com.jayfeng.androiddigest.webservices.json.ReviewDigestJson; import com.jayfeng.androiddigest.webservices.json.ReviewDigestListJson; import com.jayfeng.lesscode.core.AdapterLess; +import com.jayfeng.lesscode.core.ToastLess; import com.jayfeng.lesscode.core.ViewLess; import com.octo.android.robospice.SpiceManager; import com.octo.android.robospice.persistence.DurationInMillis; @@ -42,7 +42,7 @@ import in.srain.cube.views.ptr.PtrDefaultHandler; import in.srain.cube.views.ptr.PtrFrameLayout; -public class ReviewDigestListFragment extends Fragment implements OnScrollListener { +public class ReviewDigestListFragment extends BaseFragment implements OnScrollListener { private SpiceManager spiceManager = new SpiceManager(HttpClientSpiceService.class); @@ -113,20 +113,17 @@ public void run() { listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { + if (position < 0 || position>= listData.size()) { + return; + } ReviewDigestJson reviewDigestJson = listData.get(position); - String type = reviewDigestJson.getType(); - if (Config.JOKE_TYPE_HTML.equals(type)) { - String url = reviewDigestJson.getUrl(); + String url = reviewDigestJson.getUrl(); + if (!TextUtils.isEmpty(url)) { Intent intent = new Intent(getActivity(), WebViewActivity.class); -// url = "http://www.baidu.com"; intent.putExtra(WebViewActivity.KEY_URL, url); startActivity(intent); } else { - // default type text - int detailId = reviewDigestJson.getId(); - Intent intent = new Intent(getActivity(), DigestDetailActivity.class); - intent.putExtra(DigestDetailActivity.KEY_ID, detailId); - startActivity(intent); + ToastLess.$(getActivity(), "文章地址为空,无法打开此详情页。"); } } }); @@ -139,7 +136,7 @@ public void onItemClick(AdapterView parent, View view, int position, long id) */ private void requestNetworkData() { - ReviewDigestListRequest request = new ReviewDigestListRequest(); + JsonRequest request = new JsonRequest(ReviewDigestListJson.class); request.setUrl(Config.getReviewDigestListUrl(page, size)); spiceManager.getFromCacheAndLoadFromNetworkIfExpired(request, "review_digest_list_page_" + page + "_size_" + size, @@ -240,7 +237,7 @@ public View getView(int i, View view, AdapterLess.ViewHolder viewHolder, ReviewD private void moreNetworkData() { int nextPage = page + 1; - ReviewDigestListRequest request = new ReviewDigestListRequest(); + JsonRequest request = new JsonRequest(ReviewDigestListJson.class); request.setUrl(Config.getReviewDigestListUrl(nextPage, size)); spiceManager.execute(request, new RequestListener() { @Override diff --git a/app/src/main/java/com/jayfeng/androiddigest/fragment/BlogFragment.java b/app/src/main/java/com/jayfeng/androiddigest/fragment/TabBlogFragment.java similarity index 85% rename from app/src/main/java/com/jayfeng/androiddigest/fragment/BlogFragment.java rename to app/src/main/java/com/jayfeng/androiddigest/fragment/TabBlogFragment.java index 02761c6..5bd3e60 100644 --- a/app/src/main/java/com/jayfeng/androiddigest/fragment/BlogFragment.java +++ b/app/src/main/java/com/jayfeng/androiddigest/fragment/TabBlogFragment.java @@ -2,10 +2,12 @@ import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; +import android.view.ContextMenu; import android.view.LayoutInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; @@ -19,7 +21,8 @@ import com.jayfeng.androiddigest.activity.WebViewActivity; import com.jayfeng.androiddigest.config.Config; import com.jayfeng.androiddigest.service.HttpClientSpiceService; -import com.jayfeng.androiddigest.webservices.OfflineListRequest; +import com.jayfeng.androiddigest.webservices.JsonRequest; +import com.jayfeng.androiddigest.webservices.json.DigestJson; import com.jayfeng.androiddigest.webservices.json.OfflineJson; import com.jayfeng.androiddigest.webservices.json.OfflineListJson; import com.jayfeng.lesscode.core.AdapterLess; @@ -36,7 +39,9 @@ import in.srain.cube.views.ptr.PtrDefaultHandler; import in.srain.cube.views.ptr.PtrFrameLayout; -public class BlogFragment extends Fragment { +public class TabBlogFragment extends BaseFragment { + + private static final int CONTEXT_ITEM_OPEN_IN_BROWSER = 0; private SpiceManager spiceManager = new SpiceManager(HttpClientSpiceService.class); @@ -49,7 +54,7 @@ public class BlogFragment extends Fragment { private String url; - public BlogFragment() { + public TabBlogFragment() { // Required empty public constructor } @@ -82,6 +87,8 @@ public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header } }); + registerForContextMenu(listView); + return contentView; } @@ -124,7 +131,7 @@ public void onItemClick(AdapterView parent, View view, int position, long id) */ private void requestNetworkData() { - OfflineListRequest request = new OfflineListRequest(); + JsonRequest request = new JsonRequest(OfflineListJson.class); request.setUrl(getListUrl()); spiceManager.getFromCacheAndLoadFromNetworkIfExpired(request, getCacheKey(), @@ -222,6 +229,29 @@ private String getCacheKey() { return EncodeLess.$md5(getListUrl()); } + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + menu.setHeaderTitle("More"); + menu.add(0, CONTEXT_ITEM_OPEN_IN_BROWSER, 0, "Open in browser"); + super.onCreateContextMenu(menu, v, menuInfo); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + switch (item.getItemId()) { + case CONTEXT_ITEM_OPEN_IN_BROWSER: + OfflineJson offlineJson = listData.get(menuInfo.position); + Intent intent = new Intent(); + intent.setAction("android.intent.action.VIEW"); + Uri content_url = Uri.parse(offlineJson.getUrl()); + intent.setData(content_url); + startActivity(intent); + return true; + } + return super.onContextItemSelected(item); + } + @Override public void onStart() { spiceManager.start(getActivity()); diff --git a/app/src/main/java/com/jayfeng/androiddigest/fragment/HomeFragment.java b/app/src/main/java/com/jayfeng/androiddigest/fragment/TabHomeFragment.java similarity index 93% rename from app/src/main/java/com/jayfeng/androiddigest/fragment/HomeFragment.java rename to app/src/main/java/com/jayfeng/androiddigest/fragment/TabHomeFragment.java index 73e95ca..0a6eb2c 100644 --- a/app/src/main/java/com/jayfeng/androiddigest/fragment/HomeFragment.java +++ b/app/src/main/java/com/jayfeng/androiddigest/fragment/TabHomeFragment.java @@ -13,12 +13,12 @@ import com.jayfeng.androiddigest.adapter.HomePagerAdapter; import com.jayfeng.lesscode.core.ViewLess; -public class HomeFragment extends Fragment { +public class TabHomeFragment extends BaseFragment { private ViewPager viewPager; private HomePagerAdapter pagerAdapter; - public HomeFragment() { + public TabHomeFragment() { // Required empty public constructor } diff --git a/app/src/main/java/com/jayfeng/androiddigest/fragment/ToolFragment.java b/app/src/main/java/com/jayfeng/androiddigest/fragment/TabToolFragment.java similarity index 95% rename from app/src/main/java/com/jayfeng/androiddigest/fragment/ToolFragment.java rename to app/src/main/java/com/jayfeng/androiddigest/fragment/TabToolFragment.java index 333b4de..0584a8c 100644 --- a/app/src/main/java/com/jayfeng/androiddigest/fragment/ToolFragment.java +++ b/app/src/main/java/com/jayfeng/androiddigest/fragment/TabToolFragment.java @@ -23,13 +23,13 @@ import java.util.ArrayList; import java.util.List; -public class ToolFragment extends Fragment { +public class TabToolFragment extends BaseFragment { private ListView listView; private List listData; private BaseAdapter adapter; - public ToolFragment() { + public TabToolFragment() { // Required empty public constructor } @@ -58,7 +58,9 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { String key = listData.get(position).getKey(); + String title = listData.get(position).getTitle(); Intent intent = new Intent(getActivity(), ToolListActivity.class); + intent.putExtra(ToolListActivity.KEY_TITLE, title); intent.putExtra(ToolListActivity.KEY_TYPE, key); startActivity(intent); } diff --git a/app/src/main/java/com/jayfeng/androiddigest/fragment/ToolListFragment.java b/app/src/main/java/com/jayfeng/androiddigest/fragment/ToolListFragment.java new file mode 100644 index 0000000..9b1dd30 --- /dev/null +++ b/app/src/main/java/com/jayfeng/androiddigest/fragment/ToolListFragment.java @@ -0,0 +1,373 @@ +package com.jayfeng.androiddigest.fragment; + + +import android.content.Intent; +import android.graphics.drawable.Animatable; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.text.TextPaint; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.AbsListView.OnScrollListener; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; + +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.controller.BaseControllerListener; +import com.facebook.drawee.controller.ControllerListener; +import com.facebook.drawee.interfaces.DraweeController; +import com.facebook.drawee.view.SimpleDraweeView; +import com.facebook.imagepipeline.image.ImageInfo; +import com.jayfeng.androiddigest.R; +import com.jayfeng.androiddigest.activity.DigestDetailActivity; +import com.jayfeng.androiddigest.activity.SearchActivity; +import com.jayfeng.androiddigest.activity.WebViewActivity; +import com.jayfeng.androiddigest.config.Config; +import com.jayfeng.androiddigest.listener.Searchable; +import com.jayfeng.androiddigest.service.HttpClientSpiceService; +import com.jayfeng.androiddigest.webservices.JsonRequest; +import com.jayfeng.androiddigest.webservices.json.DigestJson; +import com.jayfeng.androiddigest.webservices.json.DigestListJson; +import com.jayfeng.androiddigest.webservices.json.ToolJson; +import com.jayfeng.androiddigest.webservices.json.ToolListJson; +import com.jayfeng.lesscode.core.AdapterLess; +import com.jayfeng.lesscode.core.DisplayLess; +import com.jayfeng.lesscode.core.ViewLess; +import com.octo.android.robospice.SpiceManager; +import com.octo.android.robospice.persistence.DurationInMillis; +import com.octo.android.robospice.persistence.exception.SpiceException; +import com.octo.android.robospice.request.listener.RequestListener; + +import java.util.List; + +import in.srain.cube.views.ptr.PtrClassicFrameLayout; +import in.srain.cube.views.ptr.PtrDefaultHandler; +import in.srain.cube.views.ptr.PtrFrameLayout; + +public class ToolListFragment extends BaseFragment implements OnScrollListener, Searchable { + + private SpiceManager spiceManager = new SpiceManager(HttpClientSpiceService.class); + + private ListView listView; + private List listData; + private BaseAdapter adapter; + + private PtrClassicFrameLayout ptrFrame; + private View errorView; + private View footerView; + private int visibleLastIndex; + private boolean noMoreData = false; + + private int page = Config.PAGE_START; + private int size = Config.PAGE_SIZE; + + private boolean isSearch = false; + private String searchKey; + + public ToolListFragment() { + // Required empty public constructor + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getArguments() != null + &&SearchActivity.TYPE_SEARCH.equals(getArguments().getString(SearchActivity.KEY_TYPE))) { + isSearch = true; + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View contentView = inflater.inflate(R.layout.fragment_digest, container, false); + listView = ViewLess.$(contentView, R.id.listview); + errorView = ViewLess.$(contentView, R.id.error); + footerView = LayoutInflater.from(getActivity()) + .inflate(R.layout.common_list_footer, null, false); + listView.addFooterView(footerView); + + listView.setOnScrollListener(this); + + ptrFrame = ViewLess.$(contentView, R.id.fragment_rotate_header_with_listview_frame); + ptrFrame.setLastUpdateTimeRelateObject(this); + ptrFrame.setPtrHandler(new PtrDefaultHandler() { + @Override + public void onRefreshBegin(PtrFrameLayout frame) { + resetPage(); + requestNetworkData(); + errorView.setVisibility(View.GONE); + } + + @Override + public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { + return PtrDefaultHandler.checkContentCanBePulledDown(frame, listView, header) ; + } + }); + + return contentView; + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + if (!isSearch) { + showCacheData(); + ptrFrame.postDelayed(new Runnable() { + @Override + public void run() { + ptrFrame.autoRefresh(); + } + }, 100); + } + + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (position < 0 || position>= listData.size()) { + return; + } + + ToolJson toolJson = listData.get(position); + String url = toolJson.getHomepage(); + if (TextUtils.isEmpty(url)) { + url = toolJson.getUrl(); + } + Intent intent = new Intent(getActivity(), WebViewActivity.class); + intent.putExtra(WebViewActivity.KEY_URL, url); + startActivity(intent); + } + }); + } + + /* + * ============================================================= + * first page data + * ============================================================= + */ + + private void requestNetworkData() { + JsonRequest request = new JsonRequest(ToolListJson.class); + request.setUrl(getUrl(page, size)); + spiceManager.getFromCacheAndLoadFromNetworkIfExpired(request, + getCacheKey(page, size), + DurationInMillis.NEVER, new RequestListener() { + @Override + public void onRequestFailure(SpiceException spiceException) { + ptrFrame.refreshComplete(); + ptrFrame.postDelayed(new Runnable() { + @Override + public void run() { + if (listView.getAdapter() == null || listView.getAdapter().getCount() == 0) { + errorView.setVisibility(View.VISIBLE); + } + } + }, 400); + + } + + @Override + public void onRequestSuccess(ToolListJson toolListJson) { + fillAdapterToListView(toolListJson); + errorView.setVisibility(View.GONE); + ptrFrame.refreshComplete(); + } + }); + } + + private void showCacheData() { + spiceManager.getFromCache(ToolListJson.class, + getCacheKey(page, size), + DurationInMillis.ALWAYS_RETURNED, new RequestListener() { + @Override + public void onRequestFailure(SpiceException spiceException) { + } + + @Override + public void onRequestSuccess(ToolListJson toolListJson) { + fillAdapterToListView(toolListJson); + resetPage(); + } + }); + } + + private void fillAdapterToListView(ToolListJson toolListJson) { + if (toolListJson == null) { + return; + } + listData = toolListJson; + adapter = AdapterLess.$base(getActivity(), + listData, + R.layout.activity_tool_list_item, + new AdapterLess.CallBack() { + @Override + public View getView(int i, View view, AdapterLess.ViewHolder viewHolder, ToolJson toolJson) { + TextView titleView = viewHolder.$view(view, R.id.title); + TextView descriptionView = viewHolder.$view(view, R.id.description); + final SimpleDraweeView draweeView = viewHolder.$view(view,R.id.thumbnail); + + titleView.setText(toolJson.getTitle()); + descriptionView.setText(toolJson.getDescription()); + if (!TextUtils.isEmpty(toolJson.getThumbnail())) { + Uri uri = Uri.parse(toolJson.getThumbnail()); + ControllerListener controllerListener = new BaseControllerListener() { + @Override + public void onFinalImageSet( + String id, + @Nullable ImageInfo imageInfo, + @Nullable Animatable anim) { + if (imageInfo.getWidth()> imageInfo.getHeight()) { + draweeView.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT; + } else { + draweeView.getLayoutParams().width = DisplayLess.$dp2px(200); + } + draweeView.setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight()); + } + + @Override + public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) { + if (imageInfo.getWidth()> imageInfo.getHeight()) { + draweeView.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT; + } else { + draweeView.getLayoutParams().width = DisplayLess.$dp2px(200); + } + draweeView.setAspectRatio((float) imageInfo.getWidth() / imageInfo.getHeight()); + } + + @Override + public void onFailure(String id, Throwable throwable) { + } + }; + DraweeController controller = Fresco.newDraweeControllerBuilder() + .setControllerListener(controllerListener) + .setUri(uri) + .setAutoPlayAnimations(true) + .build(); + draweeView.setController(controller); + draweeView.setVisibility(View.VISIBLE); + } else { + draweeView.setVisibility(View.GONE); + } + return view; + } + }); + listView.setAdapter(adapter); + if (listData.size() < Config.PAGE_SIZE) { + if (listView.getFooterViewsCount()> 0) { + listView.removeFooterView(footerView); + } + noMoreData = true; + } + } + + /* + * ============================================================= + * more page data + * ============================================================= + */ + + private void moreNetworkData() { + int nextPage = page + 1; + JsonRequest request = new JsonRequest(ToolListJson.class); + request.setUrl(getUrl(nextPage, size)); + spiceManager.execute(request, new RequestListener() { + @Override + public void onRequestFailure(SpiceException spiceException) { + } + + @Override + public void onRequestSuccess(ToolListJson toolListJson) { + moreAdapterToListView(toolListJson); + } + }); + } + + private void moreAdapterToListView(ToolListJson toolListJson) { + if (toolListJson == null) { + return; + } + listData.addAll(toolListJson); + if (toolListJson.size() < Config.PAGE_SIZE) { + if (listView.getFooterViewsCount()> 0) { + listView.removeFooterView(footerView); + } + noMoreData = true; + } + adapter.notifyDataSetChanged(); + page++; + } + + private void resetPage() { + page = Config.PAGE_START; + size = Config.PAGE_SIZE; + noMoreData = false; + if (listView.getFooterViewsCount() == 0) { + listView.addFooterView(footerView); + } + } + + /* + * ============================================================= + * search page data + * ============================================================= + */ + + @Override + public void search(String key) { + searchKey = key; + + ptrFrame.postDelayed(new Runnable() { + @Override + public void run() { + ptrFrame.autoRefresh(); + } + }, 100); + } + + private String getUrl(int page, int size) { + return Config.getSearchToolListUrl(searchKey, page, size); + } + + private String getCacheKey(int page, int size) { + return (isSearch ? "search_" + searchKey + "_" : "") + "tool_list_page_" + page + "_size_" + size; + } + + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + int lastIndex = adapter.getCount(); + if (scrollState == OnScrollListener.SCROLL_STATE_IDLE + && visibleLastIndex == lastIndex + && !noMoreData) { + moreNetworkData(); + } + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + visibleLastIndex = firstVisibleItem + visibleItemCount - 1; + } + + @Override + public void onStart() { + spiceManager.start(getActivity()); + super.onStart(); + } + + @Override + public void onStop() { + if (spiceManager.isStarted()) { + spiceManager.shouldStop(); + } + super.onStop(); + } + +} diff --git a/app/src/main/java/com/jayfeng/androiddigest/listener/Searchable.java b/app/src/main/java/com/jayfeng/androiddigest/listener/Searchable.java new file mode 100644 index 0000000..3df2cca --- /dev/null +++ b/app/src/main/java/com/jayfeng/androiddigest/listener/Searchable.java @@ -0,0 +1,5 @@ +package com.jayfeng.androiddigest.listener; + +public interface Searchable { + void search(String key); +} diff --git a/app/src/main/java/com/jayfeng/androiddigest/webservices/BaseGoogleHttpClientSpiceRequest.java b/app/src/main/java/com/jayfeng/androiddigest/webservices/BaseGoogleHttpClientSpiceRequest.java deleted file mode 100644 index aa5347a..0000000 --- a/app/src/main/java/com/jayfeng/androiddigest/webservices/BaseGoogleHttpClientSpiceRequest.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.jayfeng.androiddigest.webservices; - -import com.google.api.client.http.GenericUrl; -import com.google.api.client.http.HttpContent; -import com.google.api.client.http.HttpRequest; -import com.octo.android.robospice.request.googlehttpclient.GoogleHttpClientSpiceRequest; - -import java.io.IOException; -import java.util.HashMap; - -public abstract class BaseGoogleHttpClientSpiceRequest extends GoogleHttpClientSpiceRequest { - - String url = null; - HashMap postParameters; - - protected BaseGoogleHttpClientSpiceRequest(Class clazz) { - super(clazz); - } - - public HttpRequest buildGetRequest(GenericUrl url) throws IOException { - System.setProperty("http.keepAlive", "false"); - HttpRequest request = getHttpRequestFactory().buildGetRequest(url); - request.getHeaders().setAcceptEncoding("gzip"); - request.getHeaders().set("Connection", "close"); - request.getHeaders().setAccept("text/html,application/xhtml+xml,application/xml,application/json"); - return request; - } - - public HttpRequest buildPostRequest(GenericUrl url, HttpContent content) throws IOException { - System.setProperty("http.keepAlive", "false"); - HttpRequest request = getHttpRequestFactory().buildPostRequest(url, content); - request.getHeaders().setAcceptEncoding("gzip"); - request.getHeaders().set("Connection", "close"); - request.getHeaders().setAccept("text/html,application/xhtml+xml,application/xml,application/json"); - return request; - } - - public void setUrl(String url) { - this.url = url; - } - - public void setPostParameters(HashMap postParameters) { - this.postParameters = postParameters; - } -} diff --git a/app/src/main/java/com/jayfeng/androiddigest/webservices/DigestListRequest.java b/app/src/main/java/com/jayfeng/androiddigest/webservices/DigestListRequest.java deleted file mode 100644 index 92dc5f8..0000000 --- a/app/src/main/java/com/jayfeng/androiddigest/webservices/DigestListRequest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.jayfeng.androiddigest.webservices; - -import com.google.api.client.http.GenericUrl; -import com.google.api.client.http.HttpContent; -import com.google.api.client.http.HttpRequest; -import com.google.api.client.http.UrlEncodedContent; -import com.google.api.client.json.jackson2.JacksonFactory; -import com.jayfeng.androiddigest.webservices.json.DigestListJson; - -public class DigestListRequest extends BaseGoogleHttpClientSpiceRequest { - - public DigestListRequest() { - super(DigestListJson.class); - } - - @Override - public DigestListJson loadDataFromNetwork() throws Exception { - - HttpRequest request = null; - GenericUrl genericUrl = new GenericUrl(url); - - if (postParameters == null) { - request = getHttpRequestFactory().buildGetRequest(genericUrl); - } else { - HttpContent content = new UrlEncodedContent(postParameters); - request = buildPostRequest(genericUrl, content); - } - request.setParser(new JacksonFactory().createJsonObjectParser()); - - return request.execute().parseAs(getResultType()); - } -} diff --git a/app/src/main/java/com/jayfeng/androiddigest/webservices/DigestRequest.java b/app/src/main/java/com/jayfeng/androiddigest/webservices/DigestRequest.java deleted file mode 100644 index 3ec205e..0000000 --- a/app/src/main/java/com/jayfeng/androiddigest/webservices/DigestRequest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.jayfeng.androiddigest.webservices; - -import com.google.api.client.http.GenericUrl; -import com.google.api.client.http.HttpContent; -import com.google.api.client.http.HttpRequest; -import com.google.api.client.http.UrlEncodedContent; -import com.google.api.client.json.jackson2.JacksonFactory; -import com.jayfeng.androiddigest.webservices.json.DigestJson; - -public class DigestRequest extends BaseGoogleHttpClientSpiceRequest { - - public DigestRequest() { - super(DigestJson.class); - } - - @Override - public DigestJson loadDataFromNetwork() throws Exception { - - HttpRequest request = null; - GenericUrl genericUrl = new GenericUrl(url); - - if (postParameters == null) { - request = getHttpRequestFactory().buildGetRequest(genericUrl); - } else { - HttpContent content = new UrlEncodedContent(postParameters); - request = buildPostRequest(genericUrl, content); - } - request.setParser(new JacksonFactory().createJsonObjectParser()); - - return request.execute().parseAs(getResultType()); - } - -} diff --git a/app/src/main/java/com/jayfeng/androiddigest/webservices/JsonRequest.java b/app/src/main/java/com/jayfeng/androiddigest/webservices/JsonRequest.java new file mode 100644 index 0000000..2f1222c --- /dev/null +++ b/app/src/main/java/com/jayfeng/androiddigest/webservices/JsonRequest.java @@ -0,0 +1,70 @@ +package com.jayfeng.androiddigest.webservices; + +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpContent; +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.UrlEncodedContent; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.jayfeng.lesscode.core.FileLess; +import com.jayfeng.lesscode.core.LogLess; +import com.octo.android.robospice.request.googlehttpclient.GoogleHttpClientSpiceRequest; + +import java.io.IOException; +import java.util.HashMap; + +public class JsonRequest extends GoogleHttpClientSpiceRequest { + + String url = null; + HashMap postParameters; + + public JsonRequest(Class clazz) { + super(clazz); + } + + @Override + public T loadDataFromNetwork() throws Exception { + + HttpRequest request = null; + GenericUrl genericUrl = new GenericUrl(url); + + if (postParameters == null) { + request = buildGetRequest(genericUrl); + } else { + HttpContent content = new UrlEncodedContent(postParameters); + request = buildPostRequest(genericUrl, content); + } + request.setParser(new JacksonFactory().createJsonObjectParser()); + + LogLess.$d("json:" + FileLess.$read(request.execute().getContent())); + + return request.execute().parseAs(getResultType()); + } + + private HttpRequest buildGetRequest(GenericUrl url) throws IOException { + System.setProperty("http.keepAlive", "false"); + HttpRequest request = getHttpRequestFactory().buildGetRequest(url); + customHttpHeader(request); + return request; + } + + private HttpRequest buildPostRequest(GenericUrl url, HttpContent content) throws IOException { + System.setProperty("http.keepAlive", "false"); + HttpRequest request = getHttpRequestFactory().buildPostRequest(url, content); + customHttpHeader(request); + return request; + } + + private void customHttpHeader(HttpRequest request) { + request.getHeaders().setAcceptEncoding("gzip"); + request.getHeaders().set("Connection", "close"); + request.getHeaders().setAccept("text/html,application/xhtml+xml,application/xml,application/json"); + } + + public void setUrl(String url) { + this.url = url; + } + + public void setPostParameters(HashMap postParameters) { + this.postParameters = postParameters; + } +} diff --git a/app/src/main/java/com/jayfeng/androiddigest/webservices/OfflineListRequest.java b/app/src/main/java/com/jayfeng/androiddigest/webservices/OfflineListRequest.java deleted file mode 100644 index cdd5461..0000000 --- a/app/src/main/java/com/jayfeng/androiddigest/webservices/OfflineListRequest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.jayfeng.androiddigest.webservices; - -import com.google.api.client.http.GenericUrl; -import com.google.api.client.http.HttpContent; -import com.google.api.client.http.HttpRequest; -import com.google.api.client.http.UrlEncodedContent; -import com.google.api.client.json.jackson2.JacksonFactory; -import com.jayfeng.androiddigest.webservices.json.OfflineListJson; - -public class OfflineListRequest extends BaseGoogleHttpClientSpiceRequest { - - public OfflineListRequest() { - super(OfflineListJson.class); - } - - @Override - public OfflineListJson loadDataFromNetwork() throws Exception { - - HttpRequest request = null; - GenericUrl genericUrl = new GenericUrl(url); - - if (postParameters == null) { - request = getHttpRequestFactory().buildGetRequest(genericUrl); - } else { - HttpContent content = new UrlEncodedContent(postParameters); - request = buildPostRequest(genericUrl, content); - } - request.setParser(new JacksonFactory().createJsonObjectParser()); - - return request.execute().parseAs(getResultType()); - } -} diff --git a/app/src/main/java/com/jayfeng/androiddigest/webservices/ReviewDigestListRequest.java b/app/src/main/java/com/jayfeng/androiddigest/webservices/ReviewDigestListRequest.java deleted file mode 100644 index 607a185..0000000 --- a/app/src/main/java/com/jayfeng/androiddigest/webservices/ReviewDigestListRequest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.jayfeng.androiddigest.webservices; - -import com.google.api.client.http.GenericUrl; -import com.google.api.client.http.HttpContent; -import com.google.api.client.http.HttpRequest; -import com.google.api.client.http.UrlEncodedContent; -import com.google.api.client.json.jackson2.JacksonFactory; -import com.jayfeng.androiddigest.webservices.json.DigestListJson; -import com.jayfeng.androiddigest.webservices.json.ReviewDigestListJson; - -public class ReviewDigestListRequest extends BaseGoogleHttpClientSpiceRequest { - - public ReviewDigestListRequest() { - super(ReviewDigestListJson.class); - } - - @Override - public ReviewDigestListJson loadDataFromNetwork() throws Exception { - - HttpRequest request = null; - GenericUrl genericUrl = new GenericUrl(url); - - if (postParameters == null) { - request = getHttpRequestFactory().buildGetRequest(genericUrl); - } else { - HttpContent content = new UrlEncodedContent(postParameters); - request = buildPostRequest(genericUrl, content); - } - request.setParser(new JacksonFactory().createJsonObjectParser()); - - return request.execute().parseAs(getResultType()); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/jayfeng/androiddigest/webservices/ToolListRequest.java b/app/src/main/java/com/jayfeng/androiddigest/webservices/ToolListRequest.java deleted file mode 100644 index 7941eba..0000000 --- a/app/src/main/java/com/jayfeng/androiddigest/webservices/ToolListRequest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.jayfeng.androiddigest.webservices; - -import com.google.api.client.http.GenericUrl; -import com.google.api.client.http.HttpContent; -import com.google.api.client.http.HttpRequest; -import com.google.api.client.http.UrlEncodedContent; -import com.google.api.client.json.jackson2.JacksonFactory; -import com.jayfeng.androiddigest.webservices.json.ToolListJson; - -public class ToolListRequest extends BaseGoogleHttpClientSpiceRequest { - - public ToolListRequest() { - super(ToolListJson.class); - } - - @Override - public ToolListJson loadDataFromNetwork() throws Exception { - - HttpRequest request = null; - GenericUrl genericUrl = new GenericUrl(url); - - if (postParameters == null) { - request = getHttpRequestFactory().buildGetRequest(genericUrl); - } else { - HttpContent content = new UrlEncodedContent(postParameters); - request = buildPostRequest(genericUrl, content); - } - request.setParser(new JacksonFactory().createJsonObjectParser()); - - return request.execute().parseAs(getResultType()); - } -} diff --git a/app/src/main/java/com/jayfeng/androiddigest/webservices/UpdateRequest.java b/app/src/main/java/com/jayfeng/androiddigest/webservices/UpdateRequest.java deleted file mode 100644 index 2a93626..0000000 --- a/app/src/main/java/com/jayfeng/androiddigest/webservices/UpdateRequest.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.jayfeng.androiddigest.webservices; - -import com.google.api.client.http.GenericUrl; -import com.google.api.client.http.HttpContent; -import com.google.api.client.http.HttpRequest; -import com.google.api.client.http.UrlEncodedContent; -import com.google.api.client.json.jackson2.JacksonFactory; -import com.jayfeng.androiddigest.webservices.json.DigestJson; -import com.jayfeng.androiddigest.webservices.json.UpdateJson; - -public class UpdateRequest extends BaseGoogleHttpClientSpiceRequest { - - public UpdateRequest() { - super(UpdateJson.class); - } - - @Override - public UpdateJson loadDataFromNetwork() throws Exception { - - HttpRequest request = null; - GenericUrl genericUrl = new GenericUrl(url); - - if (postParameters == null) { - request = getHttpRequestFactory().buildGetRequest(genericUrl); - } else { - HttpContent content = new UrlEncodedContent(postParameters); - request = buildPostRequest(genericUrl, content); - } - request.setParser(new JacksonFactory().createJsonObjectParser()); - - return request.execute().parseAs(getResultType()); - } - -} diff --git a/app/src/main/java/com/jayfeng/androiddigest/webservices/json/DigestJson.java b/app/src/main/java/com/jayfeng/androiddigest/webservices/json/DigestJson.java index ffdc67d..8850683 100644 --- a/app/src/main/java/com/jayfeng/androiddigest/webservices/json/DigestJson.java +++ b/app/src/main/java/com/jayfeng/androiddigest/webservices/json/DigestJson.java @@ -2,7 +2,7 @@ import com.google.api.client.util.Key; -public class DigestJson { +public class DigestJson extends StatusJson { @Key private int id; diff --git a/app/src/main/java/com/jayfeng/androiddigest/webservices/json/ReviewDigestJson.java b/app/src/main/java/com/jayfeng/androiddigest/webservices/json/ReviewDigestJson.java index 8053c86..d36240c 100644 --- a/app/src/main/java/com/jayfeng/androiddigest/webservices/json/ReviewDigestJson.java +++ b/app/src/main/java/com/jayfeng/androiddigest/webservices/json/ReviewDigestJson.java @@ -2,7 +2,7 @@ import com.google.api.client.util.Key; -public class ReviewDigestJson { +public class ReviewDigestJson extends StatusJson { @Key private int id; diff --git a/app/src/main/java/com/jayfeng/androiddigest/webservices/json/StatusJson.java b/app/src/main/java/com/jayfeng/androiddigest/webservices/json/StatusJson.java new file mode 100644 index 0000000..2dfd279 --- /dev/null +++ b/app/src/main/java/com/jayfeng/androiddigest/webservices/json/StatusJson.java @@ -0,0 +1,36 @@ +package com.jayfeng.androiddigest.webservices.json; + +import com.google.api.client.util.Key; + +public class StatusJson { + @Key + private String s_status; + @Key + private String s_message; + @Key + private int s_code; + + public String getS_status() { + return s_status; + } + + public void setS_status(String s_status) { + this.s_status = s_status; + } + + public String getS_message() { + return s_message; + } + + public void setS_message(String s_message) { + this.s_message = s_message; + } + + public int getS_code() { + return s_code; + } + + public void setS_code(int s_code) { + this.s_code = s_code; + } +} diff --git a/app/src/main/res/drawable/selectable_background.xml b/app/src/main/res/drawable/selectable_background.xml new file mode 100644 index 0000000..2ee3a14 --- /dev/null +++ b/app/src/main/res/drawable/selectable_background.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/app/src/main/res/drawable/tab_indicator_bottom.xml b/app/src/main/res/drawable/tab_indicator_bottom.xml new file mode 100644 index 0000000..f880a61 --- /dev/null +++ b/app/src/main/res/drawable/tab_indicator_bottom.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_about_us.xml b/app/src/main/res/layout/activity_about_us.xml new file mode 100644 index 0000000..5f118ac --- /dev/null +++ b/app/src/main/res/layout/activity_about_us.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_developer.xml b/app/src/main/res/layout/activity_developer.xml new file mode 100644 index 0000000..dc81d9c --- /dev/null +++ b/app/src/main/res/layout/activity_developer.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_joke_detail.xml b/app/src/main/res/layout/activity_joke_detail.xml index 54c0f61..37207e9 100644 --- a/app/src/main/res/layout/activity_joke_detail.xml +++ b/app/src/main/res/layout/activity_joke_detail.xml @@ -8,8 +8,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="left" - android:background="#008AC4" - android:minHeight="?attr/actionBarSize" /> + android:background="@color/primary" + android:minHeight="@dimen/abc_action_bar_default_height_material" /> + android:background="@color/primary" + android:title="@string/app_description" + android:minHeight="@dimen/abc_action_bar_default_height_material" /> + android:background="@color/primary" + android:minHeight="@dimen/abc_action_bar_default_height_material" /> + + + + + + + + + + + + + + + + + + +