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 */
17package com.android.browser.homepages;
18
19import android.content.Context;
20import android.content.UriMatcher;
21import android.content.res.Resources;
22import android.database.Cursor;
23import android.net.Uri;
24import android.provider.Browser;
25import android.text.TextUtils;
26import android.util.Base64;
27import android.util.Log;
28
29import com.android.browser.R;
30
31import java.io.IOException;
32import java.io.InputStream;
33import java.io.OutputStream;
34import java.util.regex.Matcher;
35import java.util.regex.Pattern;
36
37public class RequestHandler extends Thread {
38
39    private static final String TAG = "RequestHandler";
40    private static final int INDEX = 1;
41    private static final int RESOURCE = 2;
42    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
43
44    Uri mUri;
45    Context mContext;
46    OutputStream mOutput;
47
48    static {
49        sUriMatcher.addURI(HomeProvider.AUTHORITY, "/", INDEX);
50        sUriMatcher.addURI(HomeProvider.AUTHORITY, "res/*/*", RESOURCE);
51    }
52
53    public RequestHandler(Context context, Uri uri, OutputStream out) {
54        mUri = uri;
55        mContext = context.getApplicationContext();
56        mOutput = out;
57    }
58
59    @Override
60    public void run() {
61        super.run();
62        try {
63            doHandleRequest();
64        } catch (Exception e) {
65            Log.e(TAG, "Failed to handle request: " + mUri, e);
66        } finally {
67            cleanup();
68        }
69    }
70
71    void doHandleRequest() throws IOException {
72        int match = sUriMatcher.match(mUri);
73        switch (match) {
74        case INDEX:
75            writeTemplatedIndex();
76            break;
77        case RESOURCE:
78            writeResource(getUriResourcePath());
79            break;
80        }
81    }
82
83    byte[] htmlEncode(String s) {
84        return TextUtils.htmlEncode(s).getBytes();
85    }
86
87    void writeTemplatedIndex() throws IOException {
88        Template t = Template.getCachedTemplate(mContext, R.raw.most_visited);
89        Cursor cursor = mContext.getContentResolver().query(Browser.BOOKMARKS_URI,
90                new String[] { "DISTINCT url", "title", "thumbnail" },
91                "(visits > 0 OR bookmark = 1) AND url NOT LIKE 'content:%' AND thumbnail IS NOT NULL", null, "visits DESC LIMIT 12");
92
93        t.assignLoop("most_visited", new Template.CursorListEntityWrapper(cursor) {
94            @Override
95            public void writeValue(OutputStream stream, String key) throws IOException {
96                Cursor cursor = getCursor();
97                if (key.equals("url")) {
98                    stream.write(htmlEncode(cursor.getString(0)));
99                } else if (key.equals("title")) {
100                    stream.write(htmlEncode(cursor.getString(1)));
101                } else if (key.equals("thumbnail")) {
102                    stream.write("data:image/png;base64,".getBytes());
103                    byte[] thumb = cursor.getBlob(2);
104                    stream.write(Base64.encode(thumb, Base64.DEFAULT));
105                }
106            }
107        });
108        t.write(mOutput);
109    }
110
111    String getUriResourcePath() {
112        final Pattern pattern = Pattern.compile("/?res/([\\w/]+)");
113        Matcher m = pattern.matcher(mUri.getPath());
114        if (m.matches()) {
115            return m.group(1);
116        } else {
117            return mUri.getPath();
118        }
119    }
120
121    void writeResource(String fileName) throws IOException {
122        Resources res = mContext.getResources();
123        String packageName = R.class.getPackage().getName();
124        int id = res.getIdentifier(fileName, null, packageName);
125        if (id != 0) {
126            InputStream in = res.openRawResource(id);
127            byte[] buf = new byte[4096];
128            int read;
129            while ((read = in.read(buf)) > 0) {
130                mOutput.write(buf, 0, read);
131            }
132        }
133    }
134
135    void writeString(String str) throws IOException {
136        mOutput.write(str.getBytes());
137    }
138
139    void writeString(String str, int offset, int count) throws IOException {
140        mOutput.write(str.getBytes(), offset, count);
141    }
142
143    void cleanup() {
144        try {
145            mOutput.close();
146        } catch (Exception e) {
147            Log.e(TAG, "Failed to close pipe!", e);
148        }
149    }
150
151}
152