Commits

Tobias Duehr committed 0a090bd

Added new Activity: RepositoryFollowersActivity. Started implementing caching for images in memory and on sd card

Comments (0)

Files changed (14)

     <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="14"/>
     <uses-permission android:name="android.permission.INTERNET"></uses-permission>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 
     <application android:label="@string/app_name" android:theme="@style/Theme.Sherlock.Light.DarkActionBar" android:icon="@drawable/icon" android:name="Bitbeaker">
         <activity android:label="@string/app_name" android:name="LoginActivity">
         <activity android:name="UserProfileActivity"></activity>
         <activity android:name="SettingsActivity"></activity>
     <uses-library android:name="android.test.runner"/>
+    <activity android:name="RepositoryFollowersActivity"></activity>
     </application>
     
     <instrumentation android:label="All tests" 
 Contribution is very welcome! If you want to add a feature or fix a bug yourself,
 please fork the repository at <https://bitbucket.org/saibotd/bitbeaker>, do
 your changes and send a pull request.
+
+
+# IMPORTANT LINKS
+
+<https://play.google.com/store/apps/details?id=com.saibotd.bitbeaker>
+<https://www.ohloh.net/p/bitbeaker>

res/drawable-hdpi/ab_icon_follower.png

Added
New image

res/layout/profile.xml

                 android:layout_marginRight="42dip"
                 android:autoLink="all"
                 android:linksClickable="true"
-                android:text="@string/login_error_message" >
+                android:text="@string/nothing" >
             </TextView>
 
             <ImageView

res/menu/menu_repository.xml

         android:showAsAction="always|withText"
         android:icon="@drawable/ab_icon_wiki">
     </item>
+    <item android:id="@+id/menu_followers" android:title="@string/followers" android:icon="@drawable/ab_icon_follower" android:showAsAction="ifRoom|withText"></item>
 
 </menu>

res/values/strings.xml

     <string name="prefs_locale_title">Locale</string>
     <string name="prefs_locale_summary">Change language. Takes effect after Activity\'s next restart.</string>
     <string name="prefs_locale_dialog_title">Select locale</string>
+    <string name="followers">Followers</string>
 </resources>

src/com/saibotd/bitbeaker/AsyncImageLoader.java

 package com.saibotd.bitbeaker;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.net.URL;
 
 import javax.net.ssl.HttpsURLConnection;
 import android.content.DialogInterface.OnCancelListener;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.view.View;
 import android.widget.ImageView;
 
 public class AsyncImageLoader extends AbstractAsyncLoader<String, Integer, Bitmap> implements OnCancelListener {
 	
 	private final ImageView mImageView;
+	private final MemoryCache mMemoryCache;
+	private final FileCache mFileCache;
 	
-	public AsyncImageLoader(MyActivity parentActivity, String user, String pass, ImageView imageView) {
+	public AsyncImageLoader(MyActivity parentActivity, MemoryCache memoryCache, FileCache fileCache, String user, String pass, ImageView imageView) {
 		super(parentActivity, user, pass);
 		mImageView = imageView;
+		mMemoryCache = memoryCache;
+		mFileCache = fileCache;
 	}
 
 	@Override
 	protected Bitmap doInBackground(String... parameters) {
-		Bitmap b = null;
+		Bitmap b = mMemoryCache.get(parameters[0]);
+		if(b!=null)
+			return b;
+		File f = mFileCache.getFile(parameters[0]);
+        b = Helper.decodeFile(f);
+        if(b!=null)
+            return b;
 		try {
 			BitmapFactory.Options options=new BitmapFactory.Options();
 			options.inSampleSize = 8;
 			if(url.getHost().equals("bitbucket.org"))
 				authorize(connection);
 
-			b = BitmapFactory.decodeStream(connection.getInputStream());
+			InputStream is = connection.getInputStream();
+            OutputStream os = new FileOutputStream(f);
+            Helper.CopyStream(is, os);
+            os.close();
+            b = Helper.decodeFile(f);
+            mMemoryCache.put(parameters[0], b);
+            return b;
 		} catch (Exception e) {
 			e.printStackTrace();
 			this.cancel(true);
 
 	@Override
 	protected void onPostExecute(Bitmap b) {
+		mImageView.setVisibility(View.VISIBLE);
 		if (mImageView.isShown()) {
 			mImageView.setImageBitmap(b);
 		}

src/com/saibotd/bitbeaker/Bitbeaker.java

 	private Map<String,String> kv = new LinkedHashMap<String,String>(); 
 
 	private static final int MAX_CACHE_ITEMS = 100;
+	public MemoryCache memoryCache = new MemoryCache();
+	public FileCache fileCache;
 	
 	@Override
 	public void onCreate(){
 		Helper.setContext(getApplicationContext());
 		settings = PreferenceManager.getDefaultSharedPreferences(this);
 		kv = new LinkedHashMap<String,String>();
+		fileCache = new FileCache(this);
 	}
 	
 	public void setKV(String key, String value){

src/com/saibotd/bitbeaker/FileCache.java

+package com.saibotd.bitbeaker;
+
+import java.io.File;
+import android.content.Context;
+
+public class FileCache {
+    
+    private File cacheDir;
+    
+    public FileCache(Context context){
+        if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
+            cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"bitbeaker");
+        else
+            cacheDir=context.getCacheDir();
+        if(!cacheDir.exists())
+            cacheDir.mkdirs();
+    }
+    
+    public File getFile(String url){
+        String filename=String.valueOf(url.hashCode());
+        File f = new File(cacheDir, filename);
+        return f;
+        
+    }
+    
+    public void clear(){
+        File[] files=cacheDir.listFiles();
+        if(files==null)
+            return;
+        for(File f:files)
+            f.delete();
+    }
+
+}

src/com/saibotd/bitbeaker/Helper.java

 package com.saibotd.bitbeaker;
 
 import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 import java.text.DateFormat;
 import org.json.JSONObject;
 
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.util.Log;
 
 public class Helper {
 		return "";
 	}
 	
+	public static Bitmap decodeFile(File f){
+	    try {
+	        return BitmapFactory.decodeStream(new FileInputStream(f));
+	    } catch (FileNotFoundException e) {}
+	    return null;
+	}
+	 
+    public static void CopyStream(InputStream is, OutputStream os){
+        final int buffer_size=1024;
+        try
+        {
+            byte[] bytes=new byte[buffer_size];
+            for(;;)
+            {
+              int count=is.read(bytes, 0, buffer_size);
+              if(count==-1)
+                  break;
+              os.write(bytes, 0, count);
+            }
+        }
+        catch(Exception ex){}
+    }
+	 
 	/**
 	 * Checks whether the given string is null, empty or
 	 * consists only of whitespace.

src/com/saibotd/bitbeaker/MemoryCache.java

+package com.saibotd.bitbeaker;
+
+import java.lang.ref.SoftReference;
+import java.util.HashMap;
+import android.graphics.Bitmap;
+
+public class MemoryCache {
+    private HashMap<String, SoftReference<Bitmap>> cache=new HashMap<String, SoftReference<Bitmap>>();
+    
+    public Bitmap get(String id){
+        if(!cache.containsKey(id))
+            return null;
+        SoftReference<Bitmap> ref=cache.get(id);
+        return ref.get();
+    }
+    
+    public void put(String id, Bitmap bitmap){
+        cache.put(id, new SoftReference<Bitmap>(bitmap));
+    }
+
+    public void clear() {
+        cache.clear();
+    }
+}

src/com/saibotd/bitbeaker/MyActivity.java

 	}
 
 	protected AsyncTask<String, Integer, Bitmap> executeAsyncImageLoader(ImageView image, String... params) {
-		return new AsyncImageLoader(this, bitbeaker.getUsername(), bitbeaker.getPassword(), image).execute(params);
+		return new AsyncImageLoader(this, bitbeaker.memoryCache, bitbeaker.fileCache, bitbeaker.getUsername(), bitbeaker.getPassword(), image).execute(params);
 	}
 }

src/com/saibotd/bitbeaker/RepositoryActivity.java

     private static final int MENU_INDEX_BRANCHES = 0;
     private static final int MENU_INDEX_ISSUES = 1;
     private static final int MENU_INDEX_WIKI = 2;
+    private static final int MENU_INDEX_FOLLOWERS = 3;
     
     @Override
     public boolean onPrepareOptionsMenu (Menu menu) {
         else menu.getItem(MENU_INDEX_ISSUES).setVisible(false);
         if (hasWiki) menu.getItem(MENU_INDEX_WIKI).setVisible(true);
         else menu.getItem(MENU_INDEX_WIKI).setVisible(false);
+        menu.getItem(MENU_INDEX_FOLLOWERS).setVisible(true);
         return true;
     }
     
 		    	wikiIntent.putExtras(b);
 				startActivity(wikiIntent);
 				return true;
+		    case R.id.menu_followers:
+		    	Intent repositoryFollowersIntent = new Intent(this, RepositoryFollowersActivity.class);
+		    	repositoryFollowersIntent.putExtras(b);
+				startActivity(repositoryFollowersIntent);
+				return true;
 		    default:
 		        return super.onOptionsItemSelected(item);
 	    }

src/com/saibotd/bitbeaker/RepositoryFollowersActivity.java

+package com.saibotd.bitbeaker;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.actionbarsherlock.app.ActionBar;
+
+public class RepositoryFollowersActivity extends MyActivity {
+
+	private String slug;
+	private String owner;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Bundle b = getIntent().getExtras();
+        slug = b.getString("slug");
+        owner = b.getString("owner");
+        setContentView(R.layout.repositories);
+        ActionBar actionBar = getSupportActionBar();
+        setTitle(slug);
+        actionBar.setSubtitle(getString(R.string.followers));
+        executeAsyncLoader(API_BASE_URL + "/repositories/" + owner + "/" + slug + "/followers");
+    }
+
+	@Override
+	protected void asyncLoaderDone(String result) {
+		super.asyncLoaderDone(result);
+		JSONArray followers = null;
+		ListView listView = (ListView) findViewById(R.id.repositories_list);
+		try {
+			JSONObject jsonObject = new JSONObject(result);
+			followers = jsonObject.getJSONArray("followers");
+		} catch (JSONException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		listView.setAdapter(new RepositoryFollowersAdapter(this, followers));
+	}
+    
+    protected class RepositoryFollowersAdapter extends MyAdapter {
+    	
+    	public RepositoryFollowersAdapter(Context context, JSONArray jsonArray) {
+    		super(context, jsonArray);
+    	}
+    	
+    	private OnClickListener clickListener = new OnClickListener() {
+    		@Override
+    		public void onClick(View v) {
+    			try {
+    				String user = getItem(v.getId()).getString("username");
+    				Bundle b = new Bundle();
+					b.putString("user", user);  					
+    				Intent intent = new Intent(v.getContext(), UserProfileActivity.class);
+					intent.putExtras(b);
+					v.getContext().startActivity(intent);
+    			} catch (JSONException e) {
+    				// TODO Auto-generated catch block
+    				e.printStackTrace();
+    			}
+    		}
+    	};
+    	
+    	
+    	@Override
+    	public View getView(int position, View convertView, ViewGroup parent) {
+    		View view;
+    		view = mFactory.inflate(R.layout.listitem_two_rows_icon, null);
+    		view.setId(position);
+    		TextView title = (TextView) view.findViewById(R.id.title);
+    		TextView subtitle = (TextView) view.findViewById(R.id.subtitle);
+    		ImageView icon = (ImageView) view.findViewById(R.id.icon);
+    		icon.setVisibility(View.GONE);
+    		try {
+				title.setText(getItem(position).getString("username"));
+				subtitle.setText(getItem(position).getString("first_name") + " " + getItem(position).getString("last_name"));
+				String avatar = getItem(position).getString("avatar");
+	    		if (!Helper.isJsonEmpty(avatar)) {
+					executeAsyncImageLoader(icon, avatar);
+				}
+    		} catch (JSONException e) {
+    			// TODO Auto-generated catch block
+    			e.printStackTrace();
+    		}
+    		view.setOnClickListener(clickListener);
+    		return view;
+    	}
+    }
+}