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">
<intent-filter>
- <action android:name="android.intent.action.MOBIADSNEWADS" />
+ <action android:name="android.intent.action.MOBIADSLATESTADS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<service
android:name=".MobiAdsService"
- android:process=":mobiadsservice" >
- </service>
+ android:process=":mobiadsservice"
+ android:exported="false" />
<provider
android:authorities="de.android.mobiads.provider"
public static final int ads_entry_icon=0x7f070000;
public static final int ads_entry_text=0x7f070002;
public static final int ads_entry_title=0x7f070001;
- public static final int frameLayout1=0x7f070004;
- public static final int frameLayout2=0x7f070007;
- public static final int list_frag=0x7f070003;
- public static final int login_button=0x7f070008;
- public static final int menuads_login=0x7f070009;
- public static final int menuads_settings=0x7f07000a;
- public static final int password=0x7f070006;
- public static final int selectedmenu_remove=0x7f07000b;
- public static final int username=0x7f070005;
+ public static final int frameLayout1=0x7f070003;
+ public static final int frameLayout2=0x7f070006;
+ public static final int login_button=0x7f070007;
+ public static final int menuads_login=0x7f070008;
+ public static final int menuads_settings=0x7f070009;
+ public static final int password=0x7f070005;
+ public static final int selectedmenu_remove=0x7f07000a;
+ public static final int username=0x7f070004;
}
public static final class layout {
public static final int ads_entry_list_item=0x7f030000;
- public static final int latest_ads=0x7f030001;
- public static final int login=0x7f030002;
- public static final int main=0x7f030003;
+ public static final int login=0x7f030001;
+ public static final int main=0x7f030002;
}
public static final class menu {
public static final int menuads=0x7f060000;
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:background="?android:attr/activatedBackgroundIndicator">
<!-- Icon shown next to the title/text -->
<ImageView
android:layout_alignParentLeft="true"
android:padding="3dp"
android:orientation="vertical"
- android:background="?android:attr/activatedBackgroundIndicator"
android:contentDescription="@string/desc"/>
<!-- Title of the ads entry -->
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <fragment class="de.android.mobiads.list.MobiAdsLatest$MobiAdsListFragment"
- android:id="@+id/list_frag"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-</FrameLayout>
\ No newline at end of file
package de.android.mobiads;
+import java.util.ArrayList;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
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;
private LocationManager locationManager;
private LocationListener locationListener;
+ private final ArrayList<Messenger> mClients = new ArrayList<Messenger>();
+ /** 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
}
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
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();
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();
}
}
+ 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) {
// 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());
+
+
}
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 {
if (Cookie.getCookie() != null) {
Intent intent = new Intent(getActivity(), MobiAdsService.class);
intent.putExtra("cookie", Cookie.getCookie());
+ //((MobiAdsSettings)getActivity()).doBindService();
getActivity().startService(intent);
}
else {
}
}
else {
+ //((MobiAdsSettings)getActivity()).doUnbindService();
getActivity().stopService(new Intent(getActivity(), MobiAdsService.class));
}
return false;
}
}
+
+
+
+
+
+
+ /** 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.");
+ }
+ }
}
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
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;
}
/**
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;
+ }
}
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
this.adsItemLayoutResource = adsItemLayoutResource;
}
+ public void setData(List<AdsEntry> 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;
}
// 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
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<List<AdsEntry>>
{
@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();
}
});
@Override
public void onLoadFinished(Loader<List<AdsEntry>> loader,
List<AdsEntry> 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));
- }
}
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);
}
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<List<AdsEntry>> {
+ private static final String TAG = "AdsListLoader";
+ List<AdsEntry> 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<AdsEntry> loadInBackground() {
+ // Create corresponding array of entries and load their labels.
+ List<AdsEntry> entries = getAdsEntries();
+
+ return entries;
+ }
+
+ private List<AdsEntry> getAdsEntries() {
+ final List<AdsEntry> entries = new ArrayList<AdsEntry>();
+ 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<AdsEntry> 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<AdsEntry> 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;
+ }
+ }
}
}
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;
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;
}
}
}
+
+ 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 {
}
public static class MobiAdsListFragment extends ListFragment implements OnQueryTextListener,
- LoaderManager.LoaderCallbacks<List<AdsEntry>> {
- private static final String TAG = "MobiAdsListFragment";
+ LoaderManager.LoaderCallbacks<List<AdsEntry>> {
AdsEntryAdapter mAdapter;
// If non-null, this is the current filter the user has provided.
String mCurFilter;
- AsyncTask<Void, Void, Void> mOnItemClick;
@Override
@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.
}
// Start out with a progress indicator.
setListShown(false);
-
- mOnItemClick = new AsyncTask<Void, Void, Void>() {
-
- @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... :/
@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);
@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());
} else {
setListShownNoAnimation(true);
}
-
- if (isMyServiceRunning()) {
- showNotification(0, 0, getText(R.string.remote_service_content_empty_notification));
- }
}
@Override
- public void onLoaderReset(Loader<List<AdsEntry>> loader) {
+ public void onLoaderReset(final Loader<List<AdsEntry>> 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.
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<AdsEntry> {
- private final int adsItemLayoutResource;
-
- public AdsEntryAdapter(final Context context, final int adsItemLayoutResource) {
- super(context, 0);
- this.adsItemLayoutResource = adsItemLayoutResource;
- }
-
- public void setData(List<AdsEntry> 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");
}
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);
// 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);
}