From 4ec26ce945a078bd365c82e3dbc1c00a4539cfd4 Mon Sep 17 00:00:00 2001 From: gumartinm Date: Sun, 24 Jun 2012 00:03:31 +0200 Subject: [PATCH] AndroidMobiads BroadCastReceiver working smoothly Application looking nicer than ever!!! --- Android/MobiAdsReloaded/AndroidManifest.xml | 9 +- .../MobiAdsReloaded/gen/de/android/mobiads/R.java | 22 +- .../res/layout/ads_entry_list_item.xml | 4 +- Android/MobiAdsReloaded/res/layout/latest_ads.xml | 9 - .../src/de/android/mobiads/MobiAdsService.java | 114 +++++--- .../src/de/android/mobiads/MobiAdsSettings.java | 136 ++++++++- .../src/de/android/mobiads/batch/MobiAdsBatch.java | 13 +- .../src/de/android/mobiads/list/AdsEntry.java | 23 +- .../de/android/mobiads/list/AdsEntryAdapter.java | 61 ++-- .../de/android/mobiads/list/MobiAdsLatestList.java | 240 ++++++++++++---- .../src/de/android/mobiads/list/MobiAdsList.java | 308 ++++----------------- .../android/mobiads/provider/IndexerProvider.java | 15 + 12 files changed, 553 insertions(+), 401 deletions(-) delete mode 100644 Android/MobiAdsReloaded/res/layout/latest_ads.xml diff --git a/Android/MobiAdsReloaded/AndroidManifest.xml b/Android/MobiAdsReloaded/AndroidManifest.xml index de63b82..c444fa6 100644 --- a/Android/MobiAdsReloaded/AndroidManifest.xml +++ b/Android/MobiAdsReloaded/AndroidManifest.xml @@ -75,20 +75,19 @@ android:theme="@android:style/Theme.Dialog" android:screenOrientation="portrait" android:configChanges="touchscreen|keyboard" - android:noHistory="true" android:excludeFromRecents="true" - android:taskAffinity="de.android.mobiads.list" + android:taskAffinity="" android:launchMode="singleTask"> - + - + android:process=":mobiadsservice" + android:exported="false" /> + android:orientation="vertical" + android:background="?android:attr/activatedBackgroundIndicator"> diff --git a/Android/MobiAdsReloaded/res/layout/latest_ads.xml b/Android/MobiAdsReloaded/res/layout/latest_ads.xml deleted file mode 100644 index 6286be1..0000000 --- a/Android/MobiAdsReloaded/res/layout/latest_ads.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - \ No newline at end of file diff --git a/Android/MobiAdsReloaded/src/de/android/mobiads/MobiAdsService.java b/Android/MobiAdsReloaded/src/de/android/mobiads/MobiAdsService.java index deb96d8..29cb231 100644 --- a/Android/MobiAdsReloaded/src/de/android/mobiads/MobiAdsService.java +++ b/Android/MobiAdsReloaded/src/de/android/mobiads/MobiAdsService.java @@ -1,5 +1,6 @@ package de.android.mobiads; +import java.util.ArrayList; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -14,7 +15,11 @@ import android.location.LocationListener; import android.location.LocationManager; import android.os.Binder; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; import de.android.mobiads.batch.MobiAdsBatch; import de.android.mobiads.list.MobiAdsLatestList; @@ -46,6 +51,11 @@ public class MobiAdsService extends Service { private LocationManager locationManager; private LocationListener locationListener; + private final ArrayList mClients = new ArrayList(); + /** Holds last value set by a client. */ + int mValue = 0; + + /** * Class for clients to access. Because we know this service always * runs in the same process as its clients, we don't need to deal with @@ -58,25 +68,19 @@ public class MobiAdsService extends Service { } private final BroadcastReceiver receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - //This will be run in the main thread of this service. It might be interesting to use a Hanlder - //for this receiver implemeting its own thread. :/ - //TODO: If I do not want to have any trouble, to use a synchronize to access this code here and when - //receiving new ads. Besides you are using the same code xD. No time right now. I must improve my code - //but I am in a hurry. - int noReadCount = 0; - CharSequence contentText; - if ((noReadCount = mobiAdsBatch.noReadAdsCount()) == 0) { - contentText = getText(R.string.remote_service_content_empty_notification); - showNotification(0, noReadCount, contentText, null); - } - else { - contentText = getText(R.string.remote_service_content_notification); - showNotification(0, noReadCount, contentText, MobiAdsLatestList.class); - } - } + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + //This will be run in the main thread of this service. It might be interesting to use a Hanlder + //for this receiver implemeting its own thread. :/ + //TODO: If I do not want to have any trouble, to use a synchronize to access this code here and when + //receiving new ads. Besides you are using the same code xD. No time right now. I must improve my code + //but I am in a hurry. + if(action.equals("de.android.mobiads.MOBIADSRECEIVER")){ + updateNotification(); + } + } }; @Override @@ -138,17 +142,9 @@ public class MobiAdsService extends Service { locationManager.requestLocationUpdates(0, 10, criteria, locationListener, null); notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); - // Display a notification about us starting. - int noReadCount = 0; - CharSequence contentText; - if ((noReadCount = this.mobiAdsBatch.noReadAdsCount()) == 0) { - contentText = getText(R.string.remote_service_content_empty_notification); - showNotification(0, noReadCount, contentText, MobiAdsLatestList.class); - } - else { - contentText = getText(R.string.remote_service_content_notification); - showNotification(0, noReadCount, contentText, MobiAdsLatestList.class); - } + + + updateNotification (); IntentFilter filter = new IntentFilter(); @@ -156,13 +152,12 @@ public class MobiAdsService extends Service { registerReceiver(receiver, filter); - return super.onStartCommand(intent, flags, startId); + return Service.START_REDELIVER_INTENT; } @Override public IBinder onBind(Intent intent) { - // TODO Auto-generated method stub - return null; + return mMessenger.getBinder(); } @@ -183,10 +178,24 @@ public class MobiAdsService extends Service { } + public void updateNotification () { + + int noReadCount = 0; + CharSequence contentText; + if ((noReadCount = this.mobiAdsBatch.noReadAdsCount()) == 0) { + contentText = getText(R.string.remote_service_content_empty_notification); + showNotification(0, noReadCount, contentText, null); + } + else { + contentText = getText(R.string.remote_service_content_notification); + showNotification(0, noReadCount, contentText, MobiAdsLatestList.class); + } + } + /** * Show a notification while this service is running. */ - public void showNotification(final int level, final int noReadAds, CharSequence contentText, Class cls) { + private void showNotification(final int level, final int noReadAds, CharSequence contentText, Class cls) { PendingIntent contentIntent = null; if (cls != null) { @@ -213,4 +222,41 @@ public class MobiAdsService extends Service { // We use a string id because it is a unique number. We use it later to cancel. notificationManager.notify(R.string.remote_service_title_notification, notification); } + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_REGISTER_CLIENT: + mClients.add(msg.replyTo); + break; + case MSG_UNREGISTER_CLIENT: + mClients.remove(msg.replyTo); + break; + case MSG_SET_VALUE: + mValue = msg.arg1; + for (int i=mClients.size()-1; i>=0; i--) { + try { + mClients.get(i).send(Message.obtain(null, + MSG_SET_VALUE, mValue, 0)); + } catch (RemoteException e) { + // The client is dead. Remove it from the list; + // we are going through the list from back to front + // so this is safe to do inside the loop. + mClients.remove(i); + } + } + break; + default: + super.handleMessage(msg); + } + } + } + + final Messenger mMessenger = new Messenger(new IncomingHandler()); + + } diff --git a/Android/MobiAdsReloaded/src/de/android/mobiads/MobiAdsSettings.java b/Android/MobiAdsReloaded/src/de/android/mobiads/MobiAdsSettings.java index ba27ae8..c038c82 100644 --- a/Android/MobiAdsReloaded/src/de/android/mobiads/MobiAdsSettings.java +++ b/Android/MobiAdsReloaded/src/de/android/mobiads/MobiAdsSettings.java @@ -1,21 +1,26 @@ package de.android.mobiads; -import de.android.mobiads.MobiAdsTabsActivity.AlertDialogFragment; -import de.android.mobiads.list.MobiAdsList; import android.app.Activity; import android.app.ActivityManager; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; import android.app.ActivityManager.RunningServiceInfo; +import android.app.DialogFragment; import android.app.FragmentManager; +import android.content.ComponentName; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; +import android.content.ServiceConnection; import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.PreferenceFragment; +import android.widget.TextView; +import android.widget.Toast; +import de.android.mobiads.list.MobiAdsList; public class MobiAdsSettings extends Activity { @@ -59,6 +64,7 @@ public class MobiAdsSettings extends Activity { if (Cookie.getCookie() != null) { Intent intent = new Intent(getActivity(), MobiAdsService.class); intent.putExtra("cookie", Cookie.getCookie()); + //((MobiAdsSettings)getActivity()).doBindService(); getActivity().startService(intent); } else { @@ -68,6 +74,7 @@ public class MobiAdsSettings extends Activity { } } else { + //((MobiAdsSettings)getActivity()).doUnbindService(); getActivity().stopService(new Intent(getActivity(), MobiAdsService.class)); } return false; @@ -86,5 +93,122 @@ public class MobiAdsSettings extends Activity { } } + + + + + + + /** Messenger for communicating with service. */ + Messenger mService = null; + /** Flag indicating whether we have called bind on the service. */ + boolean mIsBound; + /** Some text view we are using to show state information. */ + TextView mCallbackText; + + /** + * Handler of incoming messages from service. + */ + class IncomingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MobiAdsService.MSG_SET_VALUE: + mCallbackText.setText("Received from service: " + msg.arg1); + break; + default: + super.handleMessage(msg); + } + } + } + + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + final Messenger mMessenger = new Messenger(new IncomingHandler()); + + /** + * Class for interacting with the main interface of the service. + */ + private ServiceConnection mConnection = new ServiceConnection() { + + public void onServiceConnected(ComponentName className, + IBinder service) { + // This is called when the connection with the service has been + // established, giving us the service object we can use to + // interact with the service. We are communicating with our + // service through an IDL interface, so get a client-side + // representation of that from the raw service object. + mService = new Messenger(service); + mCallbackText.setText("Attached."); + + // We want to monitor the service for as long as we are + // connected to it. + try { + Message msg = Message.obtain(null, + MobiAdsService.MSG_REGISTER_CLIENT); + msg.replyTo = mMessenger; + mService.send(msg); + + // Give it some value as an example. + msg = Message.obtain(null, + MobiAdsService.MSG_SET_VALUE, this.hashCode(), 0); + mService.send(msg); + } catch (RemoteException e) { + // In this case the service has crashed before we could even + // do anything with it; we can count on soon being + // disconnected (and then reconnected if it can be restarted) + // so there is no need to do anything here. + } + + // As part of the sample, tell the user what happened. + Toast.makeText(MobiAdsSettings.this, R.string.button_messagebind, + Toast.LENGTH_SHORT).show(); + } + + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + mService = null; + mCallbackText.setText("Disconnected."); + + // As part of the sample, tell the user what happened. + Toast.makeText(MobiAdsSettings.this, R.string.button_messageunbind, + Toast.LENGTH_SHORT).show(); + } + }; + + private void doBindService() { + // Establish a connection with the service. We use an explicit + // class name because there is no reason to be able to let other + // applications replace our component. + bindService(new Intent(this, + MobiAdsService.class), mConnection, Context.BIND_AUTO_CREATE); + mIsBound = true; + mCallbackText.setText("Binding."); + } + + private void doUnbindService() { + if (mIsBound) { + // If we have received the service, and hence registered with + // it, then now is the time to unregister. + if (mService != null) { + try { + Message msg = Message.obtain(null, + MobiAdsService.MSG_UNREGISTER_CLIENT); + msg.replyTo = mMessenger; + mService.send(msg); + } catch (RemoteException e) { + // There is nothing special we need to do if the service + // has crashed. + } + } + + // Detach our existing connection. + unbindService(mConnection); + mIsBound = false; + mCallbackText.setText("Unbinding."); + } + } } diff --git a/Android/MobiAdsReloaded/src/de/android/mobiads/batch/MobiAdsBatch.java b/Android/MobiAdsReloaded/src/de/android/mobiads/batch/MobiAdsBatch.java index abb09fd..31ca84d 100644 --- a/Android/MobiAdsReloaded/src/de/android/mobiads/batch/MobiAdsBatch.java +++ b/Android/MobiAdsReloaded/src/de/android/mobiads/batch/MobiAdsBatch.java @@ -119,18 +119,7 @@ public class MobiAdsBatch { if ((uriInsert = updatedIndexer(objects)) != null) { try { downloadAds((String)objects.get("image"), (String) objects.get("id")); - int noReadCount = 0; - CharSequence contentText; - if ((noReadCount = MobiAdsBatch.this.noReadAdsCount()) == 0) { - contentText = ((MobiAdsService)MobiAdsBatch.this.context). - getText(R.string.remote_service_content_empty_notification); - } - else { - contentText = ((MobiAdsService)MobiAdsBatch.this.context). - getText(R.string.remote_service_content_notification); - } - ((MobiAdsService)MobiAdsBatch.this.context). - showNotification(0, noReadCount, contentText, MobiAdsLatestList.class); + ((MobiAdsService)MobiAdsBatch.this.context).updateNotification(); } catch (Throwable e1) { //In case of any error, remove the index database and the file diff --git a/Android/MobiAdsReloaded/src/de/android/mobiads/list/AdsEntry.java b/Android/MobiAdsReloaded/src/de/android/mobiads/list/AdsEntry.java index f5492e2..cb4a0fe 100644 --- a/Android/MobiAdsReloaded/src/de/android/mobiads/list/AdsEntry.java +++ b/Android/MobiAdsReloaded/src/de/android/mobiads/list/AdsEntry.java @@ -13,13 +13,17 @@ public final class AdsEntry { private final Bitmap icon; private final int idAd; private final String URL; + private boolean readStatus; - public AdsEntry(final String title, final String text, final Bitmap icon, final int idAd, final String URL) { + public AdsEntry(final String title, final String text, + final Bitmap icon, final int idAd, + final String URL, final boolean alreadyRead) { this.title = title; this.text = text; this.icon = icon; this.idAd = idAd; this.URL = URL; + this.readStatus = alreadyRead; } /** @@ -49,11 +53,26 @@ public final class AdsEntry { public int getIdAd() { return idAd; } - + /** * @return URL matching this ad. */ public String getURL() { return URL; } + + /** + * @return boolean true is already read false otherwise. + */ + public boolean isRead() { + return readStatus; + } + + /** + * + * @param readStatus true is already read false otherwise + */ + public void setIsRead(boolean readStatus) { + this.readStatus = readStatus; + } } diff --git a/Android/MobiAdsReloaded/src/de/android/mobiads/list/AdsEntryAdapter.java b/Android/MobiAdsReloaded/src/de/android/mobiads/list/AdsEntryAdapter.java index 0d4dffb..97ca24b 100644 --- a/Android/MobiAdsReloaded/src/de/android/mobiads/list/AdsEntryAdapter.java +++ b/Android/MobiAdsReloaded/src/de/android/mobiads/list/AdsEntryAdapter.java @@ -1,13 +1,15 @@ package de.android.mobiads.list; -import android.widget.ArrayAdapter; -import de.android.mobiads.R; +import java.util.List; import android.content.Context; +import android.graphics.Color; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; +import de.android.mobiads.R; /** * Adapts AdsEntry objects onto views for lists @@ -21,23 +23,50 @@ public final class AdsEntryAdapter extends ArrayAdapter { this.adsItemLayoutResource = adsItemLayoutResource; } + public void setData(List data) { + clear(); + if (data != null) { + addAll(data); + } + } + + /** + * Populate new items in the list. + */ @Override public View getView(final int position, final View convertView, final ViewGroup parent) { - + // We need to get the best view (re-used if possible) and then // retrieve its corresponding ViewHolder, which optimizes lookup efficiency final View view = getWorkingView(convertView); final ViewHolder viewHolder = getViewHolder(view); final AdsEntry entry = getItem(position); + + int color; + int colorText; + if (!entry.isRead()) { + color = Color.GRAY; + colorText = Color.BLACK; + } + else { + color = Color.BLACK; + colorText = Color.GRAY; + } + view.setBackgroundColor(color); // Setting the text view viewHolder.titleView.setText(entry.getTitle()); + viewHolder.titleView.setTextColor(colorText); + viewHolder.textView.setText(entry.getText()); - + viewHolder.textView.setTextColor(colorText); + // Setting image view viewHolder.imageView.setImageBitmap(entry.getIcon()); + viewHolder.imageView.setBackgroundColor(Color.BLACK); + return view; } @@ -45,43 +74,43 @@ public final class AdsEntryAdapter extends ArrayAdapter { // The workingView is basically just the convertView re-used if possible // or inflated new if not possible View workingView = null; - + if(null == convertView) { final Context context = getContext(); final LayoutInflater inflater = (LayoutInflater)context.getSystemService - (Context.LAYOUT_INFLATER_SERVICE); - + (Context.LAYOUT_INFLATER_SERVICE); + workingView = inflater.inflate(adsItemLayoutResource, null); } else { workingView = convertView; } - + return workingView; } - + private ViewHolder getViewHolder(final View workingView) { // The viewHolder allows us to avoid re-looking up view references // Since views are recycled, these references will never change final Object tag = workingView.getTag(); ViewHolder viewHolder = null; - - + + if(null == tag || !(tag instanceof ViewHolder)) { viewHolder = new ViewHolder(); - + viewHolder.titleView = (TextView) workingView.findViewById(R.id.ads_entry_title); viewHolder.textView = (TextView) workingView.findViewById(R.id.ads_entry_text); viewHolder.imageView = (ImageView) workingView.findViewById(R.id.ads_entry_icon); - + workingView.setTag(viewHolder); - + } else { viewHolder = (ViewHolder) tag; } - + return viewHolder; } - + /** * ViewHolder allows us to avoid re-looking up view references * Since views are recycled, these references will never change diff --git a/Android/MobiAdsReloaded/src/de/android/mobiads/list/MobiAdsLatestList.java b/Android/MobiAdsReloaded/src/de/android/mobiads/list/MobiAdsLatestList.java index 7ab8079..8033c29 100644 --- a/Android/MobiAdsReloaded/src/de/android/mobiads/list/MobiAdsLatestList.java +++ b/Android/MobiAdsReloaded/src/de/android/mobiads/list/MobiAdsLatestList.java @@ -1,31 +1,34 @@ package de.android.mobiads.list; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; import java.util.List; -import de.android.mobiads.MobiAdsService; -import de.android.mobiads.R; -import de.android.mobiads.list.MobiAdsList.AdsEntry; -import de.android.mobiads.list.MobiAdsList.AdsEntryAdapter; -import de.android.mobiads.list.MobiAdsList.AdsListLoader; -import android.app.ActivityManager; import android.app.ListActivity; import android.app.LoaderManager; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.ActivityManager.RunningServiceInfo; +import android.content.AsyncTaskLoader; +import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.Loader; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; +import android.util.Log; import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.view.ContextMenu.ContextMenuInfo; import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; +import de.android.mobiads.R; +import de.android.mobiads.provider.Indexer; public class MobiAdsLatestList extends ListActivity implements LoaderManager.LoaderCallbacks> { @@ -47,8 +50,22 @@ public class MobiAdsLatestList extends ListActivity implements LoaderManager.Loa @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - Uri uri = Uri.parse(mAdapter.getItem(position).getTitle()); + final AdsEntry entry = mAdapter.getItem(position); + + if (!entry.isRead()) { + setIsReadEntry(entry); + } + + //Change notification (if there is one) + Intent updateDatabase = new Intent("de.android.mobiads.MOBIADSRECEIVER"); + + sendBroadcast(updateDatabase); + + Uri uri = Uri.parse(mAdapter.getItem(position).getURL()); startActivity(new Intent(Intent.ACTION_VIEW, uri)); + + //Hopefully this will update our view showing a nice black background for this item in our list :/ + mAdapter.notifyDataSetChanged(); } }); @@ -68,14 +85,8 @@ public class MobiAdsLatestList extends ListActivity implements LoaderManager.Loa @Override public void onLoadFinished(Loader> loader, List data) { + mAdapter.setData(data); - - - //TODO: this should be done with a broadcast to our service. The service should be the only one - //showing notifications... :/ - if (isMyServiceRunning()) { - showNotification(0, 0, getText(R.string.remote_service_content_empty_notification)); - } } @@ -88,13 +99,14 @@ public class MobiAdsLatestList extends ListActivity implements LoaderManager.Loa public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.menuads, menu); + inflater.inflate(R.menu.selectedmenuads, menu); } @Override - public boolean onContextItemSelected(MenuItem item) { - AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); + public boolean onContextItemSelected(final MenuItem item) { + final AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); + switch (item.getItemId()) { case R.id.selectedmenu_remove: removeAd(info.position); @@ -105,41 +117,173 @@ public class MobiAdsLatestList extends ListActivity implements LoaderManager.Loa } public void removeAd(int position){ - AdsEntry entry = mAdapter.getItem(position); + final AdsEntry entry = mAdapter.getItem(position); int idAd = entry.getIdAd(); - Uri uriDelete = Uri.parse("content://" + "de.android.mobiads.provider" + "/" + "indexer" + "/idad/" + idAd); + final Uri uriDelete = Uri.parse("content://" + "de.android.mobiads.provider" + "/" + "indexer" + + "/idad/" + idAd); getContentResolver().delete(uriDelete, null, null); mAdapter.remove(entry); + + //Change notification (if there is one) + Intent updateDatabase = new Intent("de.android.mobiads.MOBIADSRECEIVER"); + sendBroadcast(updateDatabase); + mAdapter.notifyDataSetChanged(); } - private boolean isMyServiceRunning() { - ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); - for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { - if (MobiAdsService.class.getName().equals(service.service.getClassName())) { - return true; - } - } - return false; + + private void setIsReadEntry(final AdsEntry entry) { + final Uri uriUpdate = Uri.parse("content://" + "de.android.mobiads.provider" + + "/" + "indexer/idad/" + entry.getIdAd()); + final ContentValues values = new ContentValues(); + + values.put(Indexer.Index.COLUMN_NAME_IS_READ, new Integer(1)); + + getContentResolver().update(uriUpdate, values, null, null); + + entry.setIsRead(true); } - public void showNotification(final int level, final int noReadAds, CharSequence contentText) { - NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); - - // Set the icon, scrolling text and timestamp - Notification.Builder notificationBuilder = new Notification.Builder(getApplicationContext()). - setSmallIcon(R.drawable.wheelnotification, level). - setTicker(getText(R.string.remote_service_started_notification)). - setWhen(System.currentTimeMillis()). - setContentText(contentText). - setContentTitle(getText(R.string.remote_service_title_notification)). - setNumber(noReadAds); - Notification notification = notificationBuilder.getNotification(); - notification.flags |= Notification.FLAG_NO_CLEAR; - - // Send the notification. - // We use a string id because it is a unique number. We use it later to cancel. - notificationManager.notify(R.string.remote_service_title_notification, notification); + + /** + * A custom Loader that loads all of the installed applications. + */ + public static class AdsListLoader extends AsyncTaskLoader> { + private static final String TAG = "AdsListLoader"; + List mApps; + + public AdsListLoader(Context context) { + super(context); + } + + /** + * This is where the bulk of our work is done. This function is + * called in a background thread and should generate a new set of + * data to be published by the loader. + */ + @Override + public List loadInBackground() { + // Create corresponding array of entries and load their labels. + List entries = getAdsEntries(); + + return entries; + } + + private List getAdsEntries() { + final List entries = new ArrayList(); + final Uri uri = Uri.parse("content://" + "de.android.mobiads.provider" + "/" + "indexer" + "/isRead/"); + final ContentValues values = new ContentValues(); + + Cursor cursor = getContext().getContentResolver().query(uri, null, null, null, null); + try { + if (cursor.moveToFirst()) { + do { + values.clear(); + Bitmap bitMap = null; + FileInputStream file = null; + try { + file = getContext().openFileInput(cursor.getString(cursor.getColumnIndexOrThrow(Indexer.Index.COLUMN_NAME_PATH))); + bitMap = BitmapFactory.decodeStream(file); + } catch (FileNotFoundException e) { + //Giving more chances to other ads + continue; + } catch (IllegalArgumentException e) { + //Giving more chances to other ads + continue; + } + finally { + if (file != null) { + try { + file.close(); + } catch (IOException e) { + Log.w(TAG, "Error while closing image file."); + } + } + } + + boolean readStatus; + if ( cursor.getInt(cursor.getColumnIndexOrThrow(Indexer.Index.COLUMN_NAME_IS_READ)) == 0) { + readStatus = false; + } + else { + readStatus = true; + } + + entries.add(new AdsEntry(cursor.getString(cursor.getColumnIndexOrThrow(Indexer.Index.COLUMN_NAME_AD_NAME)), + cursor.getString(cursor.getColumnIndexOrThrow(Indexer.Index.COLUMN_NAME_TEXT)), bitMap, + cursor.getInt(cursor.getColumnIndexOrThrow(Indexer.Index.COLUMN_NAME_ID_AD)), + cursor.getString(cursor.getColumnIndexOrThrow(Indexer.Index.COLUMN_NAME_URL)), + readStatus)); + }while (cursor.moveToNext()); + } + }finally { + cursor.close(); + } + + return entries; + } + + /** + * Called when there is new data to deliver to the client. The + * super class will take care of delivering it; the implementation + * here just adds a little more logic. + */ + @Override + public void deliverResult(List apps) { + mApps = apps; + + if (isStarted()) { + // If the Loader is currently started, we can immediately + // deliver its results. + super.deliverResult(apps); + } + } + + /** + * Handles a request to start the Loader. + */ + @Override + protected void onStartLoading() { + if (mApps != null) { + // If we currently have a result available, deliver it + // immediately. + deliverResult(mApps); + } + + if (takeContentChanged() || mApps == null) { + // If the data has changed since the last time it was loaded + // or is not currently available, start a load. + forceLoad(); + } + } + + /** + * Handles a request to cancel a load. + */ + @Override + public void onCanceled(List apps) { + super.onCanceled(apps); + + // At this point we can release the resources associated with 'apps' + // if needed. + } + + /** + * Handles a request to completely reset the Loader. + */ + @Override + protected void onReset() { + super.onReset(); + + // Ensure the loader is stopped + onStopLoading(); + + // At this point we can release the resources associated with 'apps' + // if needed. + if (mApps != null) { + mApps = null; + } + } } } diff --git a/Android/MobiAdsReloaded/src/de/android/mobiads/list/MobiAdsList.java b/Android/MobiAdsReloaded/src/de/android/mobiads/list/MobiAdsList.java index 9161605..979e78b 100644 --- a/Android/MobiAdsReloaded/src/de/android/mobiads/list/MobiAdsList.java +++ b/Android/MobiAdsReloaded/src/de/android/mobiads/list/MobiAdsList.java @@ -8,16 +8,12 @@ import java.util.Collection; import java.util.List; import android.app.ActionBar; import android.app.Activity; -import android.app.ActivityManager; -import android.app.ActivityManager.RunningServiceInfo; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.app.FragmentManager; import android.app.ListFragment; import android.app.LoaderManager; -import android.app.Notification; -import android.app.NotificationManager; import android.content.AsyncTaskLoader; import android.content.ComponentName; import android.content.ContentValues; @@ -29,27 +25,20 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.util.SparseBooleanArray; import android.view.ActionMode; -import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; import android.widget.AbsListView.MultiChoiceModeListener; -import android.widget.ArrayAdapter; -import android.widget.ImageView; import android.widget.ListView; import android.widget.SearchView; import android.widget.SearchView.OnQueryTextListener; -import android.widget.TextView; import de.android.mobiads.Cookie; -import de.android.mobiads.MobiAdsService; import de.android.mobiads.R; import de.android.mobiads.provider.Indexer; @@ -178,19 +167,20 @@ public class MobiAdsList extends Activity { } } } + + boolean readStatus; + if ( cursor.getInt(cursor.getColumnIndexOrThrow(Indexer.Index.COLUMN_NAME_IS_READ)) == 0) { + readStatus = false; + } + else { + readStatus = true; + } + entries.add(new AdsEntry(cursor.getString(cursor.getColumnIndexOrThrow(Indexer.Index.COLUMN_NAME_AD_NAME)), cursor.getString(cursor.getColumnIndexOrThrow(Indexer.Index.COLUMN_NAME_TEXT)), bitMap, cursor.getInt(cursor.getColumnIndexOrThrow(Indexer.Index.COLUMN_NAME_ID_AD)), - cursor.getString(cursor.getColumnIndexOrThrow(Indexer.Index.COLUMN_NAME_URL)))); - //Esto debe ser hecho como en LoaderThrottle en una AsyncTask cuando el usuario lea el anuncio. - //Incialmente (si no leido) el anuncio tiene background gris. Cuando se ha leido tiene el background negro. - if (cursor.getInt(cursor.getColumnIndexOrThrow(Indexer.Index.COLUMN_NAME_IS_READ)) == 0) - { - values.put(Indexer.Index.COLUMN_NAME_IS_READ, new Integer(1)); - Uri uriUpdate = Uri.parse("content://" + "de.android.mobiads.provider" + "/" + "indexer/" + - cursor.getString(cursor.getColumnIndexOrThrow(Indexer.Index._ID))); - getContext().getContentResolver().update(uriUpdate, values, null, null); - } + cursor.getString(cursor.getColumnIndexOrThrow(Indexer.Index.COLUMN_NAME_URL)), + readStatus)); }while (cursor.moveToNext()); } }finally { @@ -264,12 +254,10 @@ public class MobiAdsList extends Activity { } public static class MobiAdsListFragment extends ListFragment implements OnQueryTextListener, - LoaderManager.LoaderCallbacks> { - private static final String TAG = "MobiAdsListFragment"; + LoaderManager.LoaderCallbacks> { AdsEntryAdapter mAdapter; // If non-null, this is the current filter the user has provided. String mCurFilter; - AsyncTask mOnItemClick; @Override @@ -347,7 +335,7 @@ public class MobiAdsList extends Activity { @Override public void onDestroyActionMode(ActionMode mode) { - //TODO: Save state (checked items) in orde to keep them when coming back from + //TODO: Save state (checked items) in order to keep them when coming back from //the home screen. } @@ -371,37 +359,6 @@ public class MobiAdsList extends Activity { // Start out with a progress indicator. setListShown(false); - - mOnItemClick = new AsyncTask() { - - @Override - protected Void doInBackground(Void... params) { - final Uri uri = Uri.parse("content://" + "de.android.mobiads.provider" + "/" + "indexer"); - final ContentValues values = new ContentValues(); - Cursor cursor = null; - try { - cursor = getActivity().getContentResolver().query(uri, null, null, null, null); - values.put(Indexer.Index.COLUMN_NAME_IS_READ, new Integer(1)); - Uri uriUpdate = Uri.parse("content://" + "de.android.mobiads.provider" + "/" + "indexer/" + - cursor.getString(cursor.getColumnIndexOrThrow(Indexer.Index._ID))); - - getActivity().getContentResolver().update(uriUpdate, values, null, null); - } - catch(Throwable e) { - Log.e(TAG, "AsyncTask error"); - } - finally { - if (cursor != null) { - cursor.close(); - } - } - - //Send BroadCast - - return null; - } - - }; } //TODO: Broadcast receiver from service, and stop using onResume... :/ @@ -418,20 +375,40 @@ public class MobiAdsList extends Activity { @Override public void onListItemClick(ListView l, View v, int position, long id) { - //Mirar LoaderThrottle. Aqui tengo que lanzar una aynctask y es aquí y no antes cuando tengo que - //indicar que el anuncio ha sido leido. Es decir actualizar la base datos. - //Ademas en AdsEntry necesito un nuevo campo que este asociado al campo de leido/no leido en la base de datos - //y si no está leido cambiar el background del anuncio. Si no se ha leido ponerlo en gris y si sí se ha leído - //ponerlo en negro (lo de por defecto) - //Supone: 1 cambiar el getAdsEntries() para que no actualice ahí la base datos si no aquí, 2 añadir al AdsEntry - //un nuevo campo indicando si se ha leido ya o no. :/ - Uri uri = Uri.parse(mAdapter.getItem(position).getURL()); - startActivity(new Intent(Intent.ACTION_VIEW, uri)); + final AdsEntry entry = mAdapter.getItem(position); + + if (!entry.isRead()) { + setIsReadEntry(entry); + } + + //Change notification (if there is one) + Intent updateDatabase = new Intent("de.android.mobiads.MOBIADSRECEIVER"); + + getActivity().sendBroadcast(updateDatabase); + + //Going to open the web navigator whatever it is... + Uri uri = Uri.parse(entry.getURL()); + startActivity(new Intent(Intent.ACTION_VIEW, uri)); + + //Hopefully this will update our view showing a nice black background for this item in our list :/ + mAdapter.notifyDataSetChanged(); } - private void removeAd(AdsEntry entry){ + private void setIsReadEntry(final AdsEntry entry) { + final Uri uriUpdate = Uri.parse("content://" + "de.android.mobiads.provider" + + "/" + "indexer/idad/" + entry.getIdAd()); + final ContentValues values = new ContentValues(); + + values.put(Indexer.Index.COLUMN_NAME_IS_READ, new Integer(1)); + + getActivity().getContentResolver().update(uriUpdate, values, null, null); + + entry.setIsRead(true); + } + + private void removeAd(final AdsEntry entry) { int idAd = entry.getIdAd(); - Uri uriDelete = Uri.parse("content://" + "de.android.mobiads.provider" + "/" + "indexer" + "/idad/" + idAd); + final Uri uriDelete = Uri.parse("content://" + "de.android.mobiads.provider" + "/" + "indexer" + "/idad/" + idAd); getActivity().getContentResolver().delete(uriDelete, null, null); mAdapter.remove(entry); @@ -445,7 +422,8 @@ public class MobiAdsList extends Activity { @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { // Place an action bar item for searching. - MenuItem item = menu.add("Search"); + final MenuItem item = menu.add("Search"); + item.setIcon(android.R.drawable.ic_menu_search); item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); SearchView sv = new SearchView(getActivity()); @@ -470,25 +448,21 @@ public class MobiAdsList extends Activity { } else { setListShownNoAnimation(true); } - - if (isMyServiceRunning()) { - showNotification(0, 0, getText(R.string.remote_service_content_empty_notification)); - } } @Override - public void onLoaderReset(Loader> loader) { + public void onLoaderReset(final Loader> loader) { mAdapter.setData(null); } @Override - public boolean onQueryTextSubmit(String query) { + public boolean onQueryTextSubmit(final String query) { // Don't care about this. return true; } @Override - public boolean onQueryTextChange(String newText) { + public boolean onQueryTextChange(final String newText) { // Called when the action bar search text has changed. Update // the search filter, and restart the loader to do a new query // with this filter. @@ -496,188 +470,11 @@ public class MobiAdsList extends Activity { getLoaderManager().restartLoader(0, null, this); return true; } - - private boolean isMyServiceRunning() { - ActivityManager manager = (ActivityManager) getActivity().getSystemService(Context.ACTIVITY_SERVICE); - for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { - if (MobiAdsService.class.getName().equals(service.service.getClassName())) { - return true; - } - } - return false; - } - - public void showNotification(final int level, final int noReadAds, CharSequence contentText) { - NotificationManager notificationManager = (NotificationManager)getActivity().getSystemService(Context.NOTIFICATION_SERVICE); - - // Set the icon, scrolling text and timestamp - Notification.Builder notificationBuilder = new Notification.Builder(getActivity().getApplicationContext()). - setSmallIcon(R.drawable.wheelnotification, level). - setTicker(getText(R.string.remote_service_started_notification)). - setWhen(System.currentTimeMillis()). - setContentText(contentText). - setContentTitle(getText(R.string.remote_service_title_notification)). - setNumber(noReadAds); - Notification notification = notificationBuilder.getNotification(); - notification.flags |= Notification.FLAG_NO_CLEAR; - - // Send the notification. - // We use a string id because it is a unique number. We use it later to cancel. - notificationManager.notify(R.string.remote_service_title_notification, notification); - } - } - - - - - public static class AdsEntryAdapter extends ArrayAdapter { - private final int adsItemLayoutResource; - - public AdsEntryAdapter(final Context context, final int adsItemLayoutResource) { - super(context, 0); - this.adsItemLayoutResource = adsItemLayoutResource; - } - - public void setData(List data) { - clear(); - if (data != null) { - addAll(data); - } - } - - /** - * Populate new items in the list. - */ - @Override - public View getView(final int position, final View convertView, final ViewGroup parent) { - - // We need to get the best view (re-used if possible) and then - // retrieve its corresponding ViewHolder, which optimizes lookup efficiency - final View view = getWorkingView(convertView); - final ViewHolder viewHolder = getViewHolder(view); - final AdsEntry entry = getItem(position); - - // Setting the text view - viewHolder.titleView.setText(entry.getTitle()); - - viewHolder.textView.setText(entry.getText()); - - // Setting image view - viewHolder.imageView.setImageBitmap(entry.getIcon()); - - - return view; - } - - private View getWorkingView(final View convertView) { - // The workingView is basically just the convertView re-used if possible - // or inflated new if not possible - View workingView = null; - - if(null == convertView) { - final Context context = getContext(); - final LayoutInflater inflater = (LayoutInflater)context.getSystemService - (Context.LAYOUT_INFLATER_SERVICE); - - workingView = inflater.inflate(adsItemLayoutResource, null); - } else { - workingView = convertView; - } - - return workingView; - } - - private ViewHolder getViewHolder(final View workingView) { - // The viewHolder allows us to avoid re-looking up view references - // Since views are recycled, these references will never change - final Object tag = workingView.getTag(); - ViewHolder viewHolder = null; - - - if(null == tag || !(tag instanceof ViewHolder)) { - viewHolder = new ViewHolder(); - - viewHolder.titleView = (TextView) workingView.findViewById(R.id.ads_entry_title); - viewHolder.textView = (TextView) workingView.findViewById(R.id.ads_entry_text); - viewHolder.imageView = (ImageView) workingView.findViewById(R.id.ads_entry_icon); - - workingView.setTag(viewHolder); - - } else { - viewHolder = (ViewHolder) tag; - } - - return viewHolder; - } - - /** - * ViewHolder allows us to avoid re-looking up view references - * Since views are recycled, these references will never change - */ - private static class ViewHolder { - public TextView titleView; - public TextView textView; - public ImageView imageView; - } - } - - /** - * Encapsulates information about an ads entry - */ - public static class AdsEntry { - - private final String title; - private final String text; - private final Bitmap icon; - private final int idAd; - private final String URL; - - public AdsEntry(final String title, final String text, final Bitmap icon, final int idAd, final String URL) { - this.title = title; - this.text = text; - this.icon = icon; - this.idAd = idAd; - this.URL = URL; - } - - /** - * @return Title of ads entry - */ - public String getTitle() { - return title; - } - - /** - * @return Text of ads entry - */ - public String getText() { - return text; - } - - /** - * @return Icon of this ads entry - */ - public Bitmap getIcon() { - return icon; - } - - /** - * @return Ad unique identifier of this ads entry - */ - public int getIdAd() { - return idAd; - } - - /** - * @return URL matching this ad. - */ - public String getURL() { - return URL; - } } private void createAlertDialog(int title) { - DialogFragment newFragment = AlertDialogFragment.newInstance(title); + final DialogFragment newFragment = AlertDialogFragment.newInstance(title); + newFragment.show(getFragmentManager(), "alertDialog"); } @@ -685,7 +482,8 @@ public class MobiAdsList extends Activity { public static class AlertDialogFragment extends DialogFragment { public static AlertDialogFragment newInstance(int title) { - AlertDialogFragment frag = new AlertDialogFragment(); + final AlertDialogFragment frag = new AlertDialogFragment(); + Bundle args = new Bundle(); args.putInt("title", title); diff --git a/Android/MobiAdsReloaded/src/de/android/mobiads/provider/IndexerProvider.java b/Android/MobiAdsReloaded/src/de/android/mobiads/provider/IndexerProvider.java index e43d45c..c6a961c 100644 --- a/Android/MobiAdsReloaded/src/de/android/mobiads/provider/IndexerProvider.java +++ b/Android/MobiAdsReloaded/src/de/android/mobiads/provider/IndexerProvider.java @@ -457,6 +457,21 @@ public class IndexerProvider extends ContentProvider { // null if the values are in the where argument. ); break; + + case INDEXER_IDAD: + finalWhere = Indexer.Index.COLUMN_NAME_ID_AD + " = " + uri.getPathSegments().get(2); + if (where !=null) { + finalWhere = finalWhere + " AND " + where; + } + count = db.update( + Indexer.Index.TABLE_NAME, // The database table name. + values, // A map of column names and new values to use. + finalWhere, // The final WHERE clause to use + // placeholders for whereArgs + whereArgs // The where clause column values to select on, or + // null if the values are in the where argument. + ); + break; default: throw new IllegalArgumentException("Unknown URI " + uri); } -- 2.1.4