package com.dianping.loader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import android.content.Intent; import android.content.pm.ResolveInfo; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.style.ForegroundColorSpan; import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; import com.dianping.app.MyActivity; import com.dianping.app.MyApplication; import com.dianping.loader.model.FileSpec; import com.dianping.loader.model.FragmentSpec; import com.dianping.loader.model.SiteSpec; /** * 负责下载Fragment运行所需的资源 * <p> * Intent参数:<br> * _site:SiteSpec,指定的site地图<br> * * @author Yimin * */ public class LoaderActivity extends MyActivity { private SiteSpec site; private boolean loaded; private Loader loader; private FrameLayout rootView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); rootView = new FrameLayout(this); rootView.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); rootView.setId(android.R.id.primary); setContentView(rootView); site = getIntent().getParcelableExtra("_site"); if (!(getApplication() instanceof MyApplication)) { setFail(101, null, false, true); // #101 return; } startLoader(); } @Override protected void onDestroy() { if (loader != null) { loader.stop(); } super.onDestroy(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 1) { setResult(resultCode, data); finish(); } super.onActivityResult(requestCode, resultCode, data); } private void startLoader() { if (loader != null) { loader.stop(); } loader = new Loader(); loader.start(); } private void setFail(int errorCode, Exception ex, boolean retryBtn, boolean upload) { rootView.removeAllViews(); TextView text = new TextView(this); text.setText("无法载入页面 #" + (errorCode > 0 ? errorCode : 100)); // #100 if (ex != null) { text.append("\n"); text.append(ex.toString()); } if (retryBtn) { text.setLayoutParams(new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL)); Button btn = new Button(this); btn.setText("重试"); btn.setLayoutParams(new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL)); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startLoader(); } }); LinearLayout ll = new LinearLayout(this); ll.setOrientation(LinearLayout.VERTICAL); ll.setLayoutParams(new FrameLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER)); ll.addView(text); ll.addView(btn); rootView.addView(ll); } else { text.setLayoutParams(new FrameLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER)); rootView.addView(text); } } private class Loader implements RepositoryManager.StatusChangeListener { RepositoryManager repoManager; FragmentSpec fragment; FileSpec[] depsList; boolean isDownloading; Handler handler; public Loader() { repoManager = ((MyApplication) getApplication()) .repositoryManager(); } public void start() { handler = new Handler(Looper.getMainLooper()); setLoading(); if (site == null) { setFail(102, null, false, true); // #102 } else { repoManager.addFiles(site.files()); doRepo(); } } private void doRepo() { int error = 0; do { Uri uri = getIntent().getData(); if (uri == null) { error = 105; // #105 break; } String host = uri.getHost(); if (host == null) { error = 106; // #106 break; } fragment = site.getFragment(host); if (fragment == null) { error = 107; // #107 break; } if (TextUtils.isEmpty(fragment.code())) { setDone(); return; } if (repoManager.getStatus(fragment.code()) == null) { // maybe repoManager is empty repoManager.addFiles(site.files()); } ArrayList<FileSpec> deps = new ArrayList<FileSpec>(); if (!repoManager.appendDepsList(deps, fragment.code())) { error = 108; // #108 break; } depsList = deps.toArray(new FileSpec[deps.size()]); } while (false); if (error > 0) { setFail(error, null, false, true); return; } boolean missing = false; for (FileSpec file : depsList) { if (repoManager.getStatus(file.id()) != RepositoryManager.STATUS_DONE) { missing = true; break; } } if (missing) { isDownloading = true; repoManager.addListener(this); repoManager.require(depsList); } else { setDone(); } } public void stop() { if (isDownloading && depsList != null) { repoManager.dismiss(depsList); depsList = null; } isDownloading = false; if (LoaderActivity.this.loader == this) { LoaderActivity.this.loader = null; } } private void setLoading() { if (LoaderActivity.this.loader != this) return; rootView.removeAllViews(); ProgressBar pb = new ProgressBar(LoaderActivity.this); pb.setIndeterminate(true); pb.setLayoutParams(new FrameLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER)); rootView.addView(pb); // show file list and status TextView tv = new TextView(LoaderActivity.this); tv.setTag("FileListAndStatus"); tv.setLayoutParams(new FrameLayout.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.TOP)); rootView.addView(tv); updateFileListAndStatus(); } private void setFail(int errorCode, Exception ex, boolean retryBtn, boolean upload) { if (LoaderActivity.this.loader != this) return; LoaderActivity.this.setFail(errorCode, ex, retryBtn, upload); stop(); } private void setDone() { if (LoaderActivity.this.loader != this) return; rootView.removeAllViews(); FileSpec fs = site.getFile(fragment.code()); MyClassLoader classLoader = MyClassLoader.getClassLoader(site, fs); loaded = classLoader != null; if (loaded) { Intent intent = new Intent(getIntent().getAction(), getIntent() .getData()); if (getIntent().getExtras() != null) { intent.putExtras(getIntent().getExtras()); } intent.putExtra("_site", site); intent = urlMap(intent); try { // check if it open myself to avoid infinite loop List<ResolveInfo> l = getPackageManager() .queryIntentActivities(intent, 0); if (l.size() == 1) { ResolveInfo ri = l.get(0); if (getPackageName() .equals(ri.activityInfo.packageName)) { if (LoaderActivity.this.getClass().getName() .equals(ri.activityInfo.name)) { setFail(121, null, false, true); // #121 return; } } } else if (l.size() > 1) { // should not happen, do we allow this? } startActivityForResult(intent, 1); // do a switch without transition animation overridePendingTransition(0, 0); } catch (Exception e) { setFail(122, null, false, true); // #122 Log.e("loader", "fail to start activity", e); return; } } else { setFail(120, null, false, true); // #120 return; } stop(); } void updateFileListAndStatus() { TextView tv = (TextView) rootView .findViewWithTag("FileListAndStatus"); if (tv == null) return; if (depsList == null || depsList.length == 0) { tv.setText(null); return; } ForegroundColorSpan ready = new ForegroundColorSpan(Color.BLACK); ForegroundColorSpan idle = new ForegroundColorSpan(Color.GRAY); ForegroundColorSpan running = new ForegroundColorSpan(Color.BLUE); SpannableStringBuilder sb = new SpannableStringBuilder(); for (FileSpec f : depsList) { int start = sb.length(); sb.append(f.url()); int end = sb.length(); Object status = repoManager.getStatus(f.id()); if (status == RepositoryManager.STATUS_DONE) { sb.setSpan(ready, start, end, 0); } else if (status == RepositoryManager.STATUS_IDLE) { sb.setSpan(idle, start, end, 0); } else if (status == RepositoryManager.STATUS_RUNNING) { sb.setSpan(running, start, end, 0); } sb.append('\n'); } tv.setText(sb); } @Override public void onStatusChanged(FileSpec file, String newStatus) { // in background thread runOnUiThread(new Runnable() { @Override public void run() { updateFileListAndStatus(); } }); if (newStatus == RepositoryManager.STATUS_RUNNING) return; FileSpec[] depsList = this.depsList; if (isDownloading && depsList != null && Arrays.asList(depsList).contains(file)) { if (newStatus == RepositoryManager.STATUS_IDLE) { handler.post(new Runnable() { @Override public void run() { setFail(110, null, true, false); // #110 } }); } else { int doneCount = 0, runningCount = 0; for (FileSpec f : depsList) { String status = repoManager.getStatus(f.id()); if (status == RepositoryManager.STATUS_DONE) { doneCount++; } else if (status == RepositoryManager.STATUS_RUNNING) { runningCount++; } } if (doneCount == depsList.length) { handler.post(new Runnable() { @Override public void run() { setDone(); } }); } else if (newStatus != RepositoryManager.STATUS_DONE && runningCount == 0) { handler.post(new Runnable() { @Override public void run() { setFail(111, null, true, false); // #111 } }); } else { // not finished, do nothing } } } } } }