2 * Copyright (C) 2009 Felix Bechstein
4 * This file is part of AdBlock.
6 * This program is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU General Public License as published by the Free Software
8 * Foundation; either version 3 of the License, or (at your option) any later
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 * You should have received a copy of the GNU General Public License along with
17 * this program; If not, see <http://www.gnu.org/licenses/>.
19 package de
.ub0r
.android
.adBlock
;
21 import java
.io
.BufferedReader
;
22 import java
.io
.IOException
;
23 import java
.io
.InputStreamReader
;
24 import java
.net
.HttpURLConnection
;
25 import java
.net
.MalformedURLException
;
27 import java
.util
.ArrayList
;
29 import android
.app
.Activity
;
30 import android
.app
.AlertDialog
;
31 import android
.app
.Dialog
;
32 import android
.content
.ActivityNotFoundException
;
33 import android
.content
.DialogInterface
;
34 import android
.content
.Intent
;
35 import android
.content
.SharedPreferences
;
36 import android
.net
.Uri
;
37 import android
.os
.AsyncTask
;
38 import android
.os
.Bundle
;
39 import android
.preference
.PreferenceManager
;
40 import android
.util
.Log
;
41 import android
.view
.Menu
;
42 import android
.view
.MenuInflater
;
43 import android
.view
.MenuItem
;
44 import android
.view
.View
;
45 import android
.view
.View
.OnClickListener
;
46 import android
.widget
.AdapterView
;
47 import android
.widget
.ArrayAdapter
;
48 import android
.widget
.Button
;
49 import android
.widget
.EditText
;
50 import android
.widget
.LinearLayout
;
51 import android
.widget
.ListView
;
52 import android
.widget
.TextView
;
53 import android
.widget
.Toast
;
54 import android
.widget
.AdapterView
.OnItemClickListener
;
57 * Main Activity to control ad blocking Proxy.
59 * @author Felix Bechstein
61 public class AdBlock
extends Activity
implements OnClickListener
,
64 /** Tag for output. */
65 private static final String TAG
= "AdBlock";
67 /** Prefs: name for last version run. */
68 private static final String PREFS_LAST_RUN
= "lastrun";
69 /** Preferences: import url. */
70 private static final String PREFS_IMPORT_URL
= "importurl";
72 /** Filename for export of filter. */
73 // private static final String FILENAME_EXPORT = "/sdcard/filter.txt";
74 /** ItemDialog: edit. */
75 private static final short ITEM_DIALOG_EDIT
= 0;
76 /** ItemDialog: delete. */
77 private static final short ITEM_DIALOG_DELETE
= 1;
80 private static final int DIALOG_ABOUT
= 0;
81 /** Dialog: import. */
82 private static final int DIALOG_IMPORT
= 1;
83 /** Dialog: update. */
84 private static final int DIALOG_UPDATE
= 2;
87 private SharedPreferences preferences
;
88 /** Prefs. import URL. */
89 private String importUrl
= null;
92 private ArrayList
<String
> filter
= new ArrayList
<String
>();
93 /** The ArrayAdapter. */
94 private ArrayAdapter
<String
> adapter
= null;
97 private int itemToEdit
= -1;
100 * Import filter from URL on background.
102 * @author Felix Bechstein
104 class Importer
extends AsyncTask
<String
, Boolean
, Boolean
> {
105 /** Error message. */
106 private String message
= "";
113 * @return successful?
116 protected final Boolean
doInBackground(final String
... dummy
) {
118 HttpURLConnection c
= (HttpURLConnection
) (new URL(
119 AdBlock
.this.importUrl
)).openConnection();
120 int resp
= c
.getResponseCode();
124 BufferedReader reader
= new BufferedReader(
125 new InputStreamReader(c
.getInputStream()));
126 AdBlock
.this.filter
.clear();
128 String s
= reader
.readLine();
131 } else if (s
.length() > 0) {
132 AdBlock
.this.filter
.add(s
);
137 } catch (MalformedURLException e
) {
138 Log
.e(AdBlock
.TAG
, null, e
);
139 this.message
= e
.toString();
141 } catch (IOException e
) {
142 this.message
= e
.toString();
143 Log
.e(AdBlock
.TAG
, null, e
);
149 * Merge imported filter to the real one.
155 protected final void onPostExecute(final Boolean result
) {
156 if (result
.booleanValue()) {
157 Toast
.makeText(AdBlock
.this, "imported", Toast
.LENGTH_LONG
)
159 AdBlock
.this.adapter
.notifyDataSetChanged();
161 Toast
.makeText(AdBlock
.this, "failed: " + this.message
,
162 Toast
.LENGTH_LONG
).show();
171 public final void onCreate(final Bundle savedInstanceState
) {
172 super.onCreate(savedInstanceState
);
173 this.setContentView(R
.layout
.main
);
175 this.preferences
= PreferenceManager
.getDefaultSharedPreferences(this);
176 // display changelog?
177 String v0
= this.preferences
.getString(PREFS_LAST_RUN
, "");
178 String v1
= this.getString(R
.string
.app_version
);
179 if (!v0
.equals(v1
)) {
180 SharedPreferences
.Editor editor
= this.preferences
.edit();
181 editor
.putString(PREFS_LAST_RUN
, v1
);
183 this.showDialog(DIALOG_UPDATE
);
186 ((EditText
) this.findViewById(R
.id
.port
)).setText(this.preferences
187 .getString(Proxy
.PREFS_PORT
, "8080"));
188 String f
= this.preferences
.getString(Proxy
.PREFS_FILTER
, this
189 .getString(R
.string
.default_filter
));
190 for (String s
: f
.split("\n")) {
191 if (s
.length() > 0) {
195 this.importUrl
= this.preferences
.getString(PREFS_IMPORT_URL
, "");
197 ((Button
) this.findViewById(R
.id
.start_service
))
198 .setOnClickListener(this);
199 ((Button
) this.findViewById(R
.id
.stop_service
))
200 .setOnClickListener(this);
201 ((Button
) this.findViewById(R
.id
.filter_add_
)).setOnClickListener(this);
202 ListView lv
= (ListView
) this.findViewById(R
.id
.filter
);
203 this.adapter
= new ArrayAdapter
<String
>(this,
204 R
.layout
.simple_list_item_1
, this.filter
);
205 lv
.setAdapter(this.adapter
);
206 lv
.setTextFilterEnabled(true);
207 lv
.setOnItemClickListener(this);
209 this.startService(new Intent(this, Proxy
.class));
212 /** Save Preferences. */
213 private void savePreferences() {
214 SharedPreferences
.Editor editor
= this.preferences
.edit();
215 editor
.putString(Proxy
.PREFS_PORT
, ((EditText
) this
216 .findViewById(R
.id
.port
)).getText().toString());
217 StringBuilder sb
= new StringBuilder();
218 for (String s
: this.filter
) {
219 if (s
.indexOf("admob") < 0 // won't block admob
220 && s
.indexOf("google") < 0) { // won't block google
224 editor
.putString(Proxy
.PREFS_FILTER
, sb
.toString());
225 editor
.putString(PREFS_IMPORT_URL
, this.importUrl
);
233 public final void onPause() {
235 this.savePreferences();
242 public final void onClick(final View v
) {
244 case R
.id
.start_service
:
245 this.savePreferences();
246 this.startService(new Intent(this, Proxy
.class));
248 case R
.id
.stop_service
:
249 this.stopService(new Intent(this, Proxy
.class));
250 case R
.id
.filter_add_
:
251 EditText et
= (EditText
) this.findViewById(R
.id
.filter_add
);
252 String f
= et
.getText().toString();
253 if (f
.length() > 0) {
254 if (this.itemToEdit
>= 0) {
255 this.filter
.remove(this.itemToEdit
);
256 this.itemToEdit
= -1;
260 this.adapter
.notifyDataSetChanged();
264 this.dismissDialog(DIALOG_IMPORT
);
267 this.dismissDialog(DIALOG_IMPORT
);
268 this.importUrl
= ((EditText
) v
.getRootView().findViewById(
269 R
.id
.import_url
)).getText().toString();
270 new Importer().execute((String
[]) null);
281 public final boolean onCreateOptionsMenu(final Menu menu
) {
282 MenuInflater inflater
= this.getMenuInflater();
283 inflater
.inflate(R
.menu
.menu
, menu
);
290 public final boolean onOptionsItemSelected(final MenuItem item
) {
291 switch (item
.getItemId()) {
292 case R
.id
.item_about
: // start about dialog
293 this.showDialog(DIALOG_ABOUT
);
295 case R
.id
.item_import
:
296 this.showDialog(DIALOG_IMPORT
);
298 case R
.id
.item_donate
:
300 this.startActivity(new Intent(Intent
.ACTION_VIEW
, Uri
301 .parse(this.getString(R
.string
.donate_url
))));
302 } catch (ActivityNotFoundException e
) {
303 Log
.e(TAG
, "no browser", e
);
308 this.startActivity(new Intent(Intent
.ACTION_VIEW
, Uri
309 .parse("market://search?q=pub:\"Felix Bechstein\"")));
310 } catch (ActivityNotFoundException e
) {
311 Log
.e(TAG
, "no market", e
);
314 // case R.id.item_export:
316 // // OutputStream os = this.openFileOutput(FILENAME_EXPORT,
317 // // MODE_WORLD_READABLE);
318 // File file = new File(FILENAME_EXPORT);
319 // if (!file.createNewFile()) {
321 // file.createNewFile();
323 // OutputStream os = new FileOutputStream(file);
324 // BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
326 // for (String s : this.filter) {
327 // if (s.indexOf("admob") < 0) { // won't block admob
328 // bw.append(s + "\n");
333 // Toast.makeText(this, "exported to " + FILENAME_EXPORT,
334 // Toast.LENGTH_LONG).show();
335 // } catch (IOException e) {
336 // Log.e(this.TAG, null, e);
337 // Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show();
349 protected final Dialog
onCreateDialog(final int id
) {
353 d
= new Dialog(this);
354 d
.setContentView(R
.layout
.about
);
355 d
.setTitle(this.getString(R
.string
.about_
) + " v"
356 + this.getString(R
.string
.app_version
));
359 d
= new Dialog(this);
360 d
.setContentView(R
.layout
.import_url
);
361 d
.setTitle(this.getString(R
.string
.import_url_
));
362 ((Button
) d
.findViewById(R
.id
.ok
)).setOnClickListener(this);
363 ((Button
) d
.findViewById(R
.id
.cancel
)).setOnClickListener(this);
366 final AlertDialog
.Builder builder
= new AlertDialog
.Builder(this);
367 builder
.setTitle(R
.string
.changelog_
);
368 final String
[] changes
= this.getResources().getStringArray(
370 final StringBuilder buf
= new StringBuilder(changes
[0]);
371 for (int i
= 1; i
< changes
.length
; i
++) {
373 buf
.append(changes
[i
]);
375 builder
.setIcon(android
.R
.drawable
.ic_menu_info_details
);
376 builder
.setMessage(buf
.toString());
377 builder
.setCancelable(true);
378 builder
.setPositiveButton(android
.R
.string
.ok
,
379 new DialogInterface
.OnClickListener() {
380 public void onClick(final DialogInterface dialog
,
385 return builder
.create();
395 protected final void onPrepareDialog(final int id
, final Dialog dialog
) {
396 super.onPrepareDialog(id
, dialog
);
399 ((EditText
) dialog
.findViewById(R
.id
.import_url
))
400 .setText(this.importUrl
);
411 public final void onItemClick(final AdapterView
<?
> parent
, final View v
,
412 final int position
, final long id
) {
414 AlertDialog
.Builder builder
= new AlertDialog
.Builder(this);
416 this.getResources().getStringArray(R
.array
.itemDialog
),
417 new DialogInterface
.OnClickListener() {
418 public void onClick(final DialogInterface dialog
,
421 case ITEM_DIALOG_EDIT
:
422 AdBlock
.this.itemToEdit
= position
;
423 ((EditText
) AdBlock
.this
424 .findViewById(R
.id
.filter_add
))
425 .setText(AdBlock
.this.adapter
428 case ITEM_DIALOG_DELETE
:
429 AdBlock
.this.filter
.remove(position
);
430 AdBlock
.this.adapter
.notifyDataSetChanged();
437 AlertDialog alert
= builder
.create();