Home | History | Annotate | Download | only in homepages
      1 
      2 /*
      3  * Copyright (C) 2011 The Android Open Source Project
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 package com.android.browser.homepages;
     18 
     19 import android.content.Context;
     20 import android.content.UriMatcher;
     21 import android.content.res.Resources;
     22 import android.database.Cursor;
     23 import android.database.MergeCursor;
     24 import android.net.Uri;
     25 import android.provider.BrowserContract.Bookmarks;
     26 import android.provider.BrowserContract.History;
     27 import android.text.TextUtils;
     28 import android.util.Base64;
     29 import android.util.Log;
     30 
     31 import com.android.browser.R;
     32 import com.android.browser.homepages.Template.ListEntityIterator;
     33 
     34 import java.io.File;
     35 import java.io.IOException;
     36 import java.io.InputStream;
     37 import java.io.OutputStream;
     38 import java.text.DateFormat;
     39 import java.text.DecimalFormat;
     40 import java.util.Arrays;
     41 import java.util.Comparator;
     42 import java.util.regex.Matcher;
     43 import java.util.regex.Pattern;
     44 
     45 public class RequestHandler extends Thread {
     46 
     47     private static final String TAG = "RequestHandler";
     48     private static final int INDEX = 1;
     49     private static final int RESOURCE = 2;
     50     private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
     51 
     52     Uri mUri;
     53     Context mContext;
     54     OutputStream mOutput;
     55 
     56     static {
     57         sUriMatcher.addURI(HomeProvider.AUTHORITY, "/", INDEX);
     58         sUriMatcher.addURI(HomeProvider.AUTHORITY, "res/*/*", RESOURCE);
     59     }
     60 
     61     public RequestHandler(Context context, Uri uri, OutputStream out) {
     62         mUri = uri;
     63         mContext = context.getApplicationContext();
     64         mOutput = out;
     65     }
     66 
     67     @Override
     68     public void run() {
     69         super.run();
     70         try {
     71             doHandleRequest();
     72         } catch (Exception e) {
     73             Log.e(TAG, "Failed to handle request: " + mUri, e);
     74         } finally {
     75             cleanup();
     76         }
     77     }
     78 
     79     void doHandleRequest() throws IOException {
     80         if ("file".equals(mUri.getScheme())) {
     81             writeFolderIndex();
     82             return;
     83         }
     84         int match = sUriMatcher.match(mUri);
     85         switch (match) {
     86         case INDEX:
     87             writeTemplatedIndex();
     88             break;
     89         case RESOURCE:
     90             writeResource(getUriResourcePath());
     91             break;
     92         }
     93     }
     94 
     95     byte[] htmlEncode(String s) {
     96         return TextUtils.htmlEncode(s).getBytes();
     97     }
     98 
     99     // We can reuse this for both History and Bookmarks queries because the
    100     // columns defined actually belong to the CommonColumn and ImageColumn
    101     // interfaces that both History and Bookmarks implement
    102     private static final String[] PROJECTION = new String[] {
    103         History.URL,
    104         History.TITLE,
    105         History.THUMBNAIL
    106     };
    107     private static final String SELECTION = History.URL
    108             + " NOT LIKE 'content:%' AND " + History.THUMBNAIL + " IS NOT NULL";
    109     void writeTemplatedIndex() throws IOException {
    110         Template t = Template.getCachedTemplate(mContext, R.raw.most_visited);
    111         Cursor historyResults = mContext.getContentResolver().query(
    112                 History.CONTENT_URI, PROJECTION, SELECTION,
    113                 null, History.VISITS + " DESC LIMIT 12");
    114         Cursor cursor = historyResults;
    115         try {
    116             if (cursor.getCount() < 12) {
    117                 Cursor bookmarkResults = mContext.getContentResolver().query(
    118                         Bookmarks.CONTENT_URI, PROJECTION, SELECTION,
    119                         null, Bookmarks.DATE_CREATED + " DESC LIMIT 12");
    120                 cursor = new MergeCursor(new Cursor[] { historyResults, bookmarkResults }) {
    121                     @Override
    122                     public int getCount() {
    123                         return Math.min(12, super.getCount());
    124                     }
    125                 };
    126             }
    127             t.assignLoop("most_visited", new Template.CursorListEntityWrapper(cursor) {
    128                 @Override
    129                 public void writeValue(OutputStream stream, String key) throws IOException {
    130                     Cursor cursor = getCursor();
    131                     if (key.equals("url")) {
    132                         stream.write(htmlEncode(cursor.getString(0)));
    133                     } else if (key.equals("title")) {
    134                         stream.write(htmlEncode(cursor.getString(1)));
    135                     } else if (key.equals("thumbnail")) {
    136                         stream.write("data:image/png;base64,".getBytes());
    137                         byte[] thumb = cursor.getBlob(2);
    138                         stream.write(Base64.encode(thumb, Base64.DEFAULT));
    139                     }
    140                 }
    141             });
    142             t.write(mOutput);
    143         } finally {
    144             cursor.close();
    145         }
    146     }
    147 
    148     private static final Comparator<File> sFileComparator = new Comparator<File>() {
    149         @Override
    150         public int compare(File lhs, File rhs) {
    151             if (lhs.isDirectory() != rhs.isDirectory()) {
    152                 return lhs.isDirectory() ? -1 : 1;
    153             }
    154             return lhs.getName().compareTo(rhs.getName());
    155         }
    156     };
    157 
    158     void writeFolderIndex() throws IOException {
    159         File f = new File(mUri.getPath());
    160         final File[] files = f.listFiles();
    161         Arrays.sort(files, sFileComparator);
    162         Template t = Template.getCachedTemplate(mContext, R.raw.folder_view);
    163         t.assign("path", mUri.getPath());
    164         t.assign("parent_url", f.getParent() != null ? f.getParent() : f.getPath());
    165         t.assignLoop("files", new ListEntityIterator() {
    166             int index = -1;
    167 
    168             @Override
    169             public void writeValue(OutputStream stream, String key) throws IOException {
    170                 File f = files[index];
    171                 if ("name".equals(key)) {
    172                     stream.write(f.getName().getBytes());
    173                 }
    174                 if ("url".equals(key)) {
    175                     stream.write(("file://" + f.getAbsolutePath()).getBytes());
    176                 }
    177                 if ("type".equals(key)) {
    178                     stream.write((f.isDirectory() ? "dir" : "file").getBytes());
    179                 }
    180                 if ("size".equals(key)) {
    181                     if (f.isFile()) {
    182                         stream.write(readableFileSize(f.length()).getBytes());
    183                     }
    184                 }
    185                 if ("last_modified".equals(key)) {
    186                     String date = DateFormat.getDateTimeInstance(
    187                             DateFormat.SHORT, DateFormat.SHORT)
    188                             .format(f.lastModified());
    189                     stream.write(date.getBytes());
    190                 }
    191                 if ("alt".equals(key)) {
    192                     if (index % 2 == 0) {
    193                         stream.write("alt".getBytes());
    194                     }
    195                 }
    196             }
    197 
    198             @Override
    199             public ListEntityIterator getListIterator(String key) {
    200                 return null;
    201             }
    202 
    203             @Override
    204             public void reset() {
    205                 index = -1;
    206             }
    207 
    208             @Override
    209             public boolean moveToNext() {
    210                 return (++index) < files.length;
    211             }
    212         });
    213         t.write(mOutput);
    214     }
    215 
    216     static String readableFileSize(long size) {
    217         if(size <= 0) return "0";
    218         final String[] units = new String[] { "B", "KB", "MB", "GB", "TB" };
    219         int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
    220         return new DecimalFormat("#,##0.#").format(
    221                 size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
    222     }
    223 
    224     String getUriResourcePath() {
    225         final Pattern pattern = Pattern.compile("/?res/([\\w/]+)");
    226         Matcher m = pattern.matcher(mUri.getPath());
    227         if (m.matches()) {
    228             return m.group(1);
    229         } else {
    230             return mUri.getPath();
    231         }
    232     }
    233 
    234     void writeResource(String fileName) throws IOException {
    235         Resources res = mContext.getResources();
    236         String packageName = R.class.getPackage().getName();
    237         int id = res.getIdentifier(fileName, null, packageName);
    238         if (id != 0) {
    239             InputStream in = res.openRawResource(id);
    240             byte[] buf = new byte[4096];
    241             int read;
    242             while ((read = in.read(buf)) > 0) {
    243                 mOutput.write(buf, 0, read);
    244             }
    245         }
    246     }
    247 
    248     void writeString(String str) throws IOException {
    249         mOutput.write(str.getBytes());
    250     }
    251 
    252     void writeString(String str, int offset, int count) throws IOException {
    253         mOutput.write(str.getBytes(), offset, count);
    254     }
    255 
    256     void cleanup() {
    257         try {
    258             mOutput.close();
    259         } catch (Exception e) {
    260             Log.e(TAG, "Failed to close pipe!", e);
    261         }
    262     }
    263 
    264 }
    265