tag:blogger.com,1999:blog-6464150568700614142024-03-19T10:48:23.171+02:00Android SherificationA blog that uses an advanced AI to generate content about Android. Sherif elKhatibSherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.comBlogger28125tag:blogger.com,1999:blog-646415056870061414.post-31307569801941093022013-09-19T15:34:00.001+03:002013-09-19T15:40:31.723+03:00Android Custom View: PlayStation Knob - Part 1: Getting StartedThis is part 1 of the series "Android Custom View: PlayStation Knob", in which I will be creating an Android Custom View that acts just like the PlayStation Analogue Knob:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibXV9UzlPwLGQGOsAlklg-apbXbboA5eMbSytJcm4_G2dev75xS4xxX-k3-Bpxph3pwgmlL8nm-zqo1AQfO61SHH-lG_LEfrhzsEW32GaSxVMt-u0iTjshxh_8ShpycGrrctMPEkV-nt2Z/s1600/controller.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="231" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibXV9UzlPwLGQGOsAlklg-apbXbboA5eMbSytJcm4_G2dev75xS4xxX-k3-Bpxph3pwgmlL8nm-zqo1AQfO61SHH-lG_LEfrhzsEW32GaSxVMt-u0iTjshxh_8ShpycGrrctMPEkV-nt2Z/s320/controller.png" width="320" /></a></div>
In this part, you will be able to:<br />
<br />
<ol>
<li>Create the Custom View</li>
<li>Create Custom Attributes</li>
<li>Use your View in an Activity</li>
<li>Capture Touch Events to move the knob</li>
</ol>
<br />
<a name='more'></a>Note: The code demonstrated here can be directly fetched from the git repository <a href="https://github.com/sherifelkhatib/WidgyWidgets" target="_blank">WidgyWidgets</a>. You need to clone the repository and checkout to tags/knobview-part1 (Instruction are at the end of this post)<br />
<br />
To start with this, you want to decide on whether to extend the base <a href="http://developer.android.com/reference/android/view/View.html" target="_blank">View</a> class or to extend one of the widgets available in android.<br />
In our case, I want to create a view that somehow shows in its center a circle that can be dragged by the user to any of the edges. One way to go would be to extend one of the available <a href="http://developer.android.com/reference/android/view/ViewGroup.html" target="_blank">ViewGroup</a>s, add the knob in its center, and implement this dragging functionality. Another way would be to start the view from scratch by extending View.<br />
To make things more interesting, I will, in this post, extend View. By the way, I will call it <i>KnobView</i>.<br />
<br />
As a general practice I usually create a class that extends View and directly create a function called init and override the 3 constructors of View calling init() in all of them:<br />
<br />
<pre class="java">public class KnobView extends View {
public KnobView(Context context) {
super(context);
init(context, null);
}
public KnobView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public KnobView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
// TODO Auto-generated method stub
}
}
</pre>
If you do not know, the AttributeSet parameter in the constructor contains the attributes specified in the xml. To demonstrate, I will create an attribute called <b>knob</b> that will reference a drawable used for the knob in the center. To do so, we need to declare this attribute in the res folder. I will create a file called attr_knobview.xml in the res/values folder: this file will include the attributes of our KnobView. At this stage, we will only declare the attribute called <b>knob</b> of type integer since it is a reference to a drawable:<br />
<pre class="xml">
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="KnobView">
<attr name="knob" format="integer" />
</declare-styleable>
</resources>
</pre>
<div>
<br /></div>
Now in my init function I will get this drawable. (If you are working step by step, build your project after each change - e.g. NOW). To get the drawable:<br />
<br />
<pre class="java">private void init(Context context, AttributeSet attrs) {
// TODO Auto-generated method stub
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KnobView);
int drawable = a.getResourceId(R.styleable.KnobView_knob, 0);
if(drawable != 0) {
mKnobDrawable = a.getDrawable(R.styleable.KnobView_knob);
}
}
</pre>
<br />
In this code, I try to get the integer which would be specified by the attribute <b>knob</b> (<i>KnobView_knob</i>). If this integer is not 0, I get the drawable and assign it to the field mKnobDrawable. You can add the field mKnobDrawable at the top of the class using this line: <i>private Drawable mKnobDrawable;</i><br />
<i><br /></i>
What if there is no knob attribute specified in xml? I will simply create my own round black circle. This can be done in the else statement by creating a ShapeDrawable and assigning it to mKnobDrawable. Now my init function will assign a drawable to mKnobDrawable whether knob was specified or not.<br />
<br />
<pre class="java">private void init(Context context, AttributeSet attrs) {
// TODO Auto-generated method stub
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KnobView);
int drawable = a.getResourceId(R.styleable.KnobView_knob, 0);
if(drawable != 0) {
mKnobDrawable = a.getDrawable(R.styleable.KnobView_knob);
}
else {
mKnobDrawable = new ShapeDrawable(new OvalShape());
ShapeDrawable s = (ShapeDrawable) mKnobDrawable;
s.getPaint().setColor(Color.BLACK);
}
}
</pre>
<br />
Now we want to take care of the size of this knob. For now, I will center it in the view and let it take half the width and half the height. I will update the Bounds of this mKnobDrawable each time the size of our KnobView changes. This can be captured in <i>onSizeChanged</i>:<br />
<br />
<pre class="java">@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mKnobDrawable.setBounds(w/2-w/4, h/2-h/4, w/2+w/4, h/2+h/4);
}
</pre>
<br />
Notice that I am always updating the bounds of the knob drawable when the size changed. These bounds determine the actual rectangle in which my drawable will be drawn. I set the bounds to be the rectangle that is exactly half the size of the view and exactly located in the center of our view. It is pretty straight-forward.<br />
<br />
Finally, I want to draw this Knob. This is simply calling the Drawable's draw function on our canvas in the onDraw function of the KnobView:<br />
<br />
<pre class="java">@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mKnobDrawable.draw(canvas);
}</pre>
<i><br /></i>
For testing, I created an activity with the following xml layout:<br />
<br />
<pre class="xml"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCDIfR6Uv3X8L2q_Md-i9XBH7Mr_R-yYpKnb3JuwyajsAq-68IqL48_OBhmVExqP2m4M3nfRH-YN8rsjteBP03G1jIT05n4sISp5NKXjGxAUEezIxqZVsn1wEnwrdSnz0EaPbFhYRyoCHb/s1600/screen1.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCDIfR6Uv3X8L2q_Md-i9XBH7Mr_R-yYpKnb3JuwyajsAq-68IqL48_OBhmVExqP2m4M3nfRH-YN8rsjteBP03G1jIT05n4sISp5NKXjGxAUEezIxqZVsn1wEnwrdSnz0EaPbFhYRyoCHb/s320/screen1.png" width="180" /></a><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/mobi.sherif.widgywidgetstest"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<mobi.sherif.widgywidgets.KnobView
android:id="@+id/knob1"
android:background="#f00"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1" />
<mobi.sherif.widgywidgets.KnobView
android:id="@+id/knob2"
android:background="#0f0"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
app:knob="@drawable/ic_launcher" />
</LinearLayout>
</pre>
<br />
Notice:<br />
<br />
<ol>
<li>If you are doing your own project, you probably want to specify your package name instead of <b>mobi.sherif.widgywidgetstest</b> (second line)</li>
<li>Due to <b>layout_height="0dp"</b> and <b>layout_weight="1"</b>, each KnobView will take half the screen.</li>
<li>The first KnobView does not specify the <b>knob</b> attribute while the second does.</li>
<li>Each of the KnobView has a differnt background (red and blue).</li>
</ol>
<div>
Anyway, if you run this activity, you will get the output that is shown in the previous image.</div>
<div>
<br /></div>
<div>
With some modifications, int the drawables used for the background and the knob, I was able to get the following KnobView:<br />
<br />
I modified the first KnobView in the layout (notice the background and the knob values)<br />
<br />
<pre class="xml"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkNOQAvBvnF3nOhvaWqA_gCmZCwlte6cCnN6btt0D2LLsY6oImil50dj8GYeWst1pyKEvt9xWMJIGbmCZAIuVIVDLb7tM2_hbJhzJfetOtVqLNNL8izXM4TFwkBz1cfjqSvbkpYjXhv35R/s1600/screen2.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkNOQAvBvnF3nOhvaWqA_gCmZCwlte6cCnN6btt0D2LLsY6oImil50dj8GYeWst1pyKEvt9xWMJIGbmCZAIuVIVDLb7tM2_hbJhzJfetOtVqLNNL8izXM4TFwkBz1cfjqSvbkpYjXhv35R/s320/screen2.png" width="180" /></a> <mobi.sherif.widgywidgets.KnobView
android:id="@+id/knob1"
android:background="@drawable/bg_knobview"
app:knob="@drawable/bg_knob"
android:layout_width="100dip"
android:layout_height="100dip"
android:layout_marginTop="100dip"
android:layout_marginBottom="100dip" /></pre>
</div>
<div>
<br />
<pre class="xml"><span style="font-family: 'Times New Roman'; white-space: normal;">I also created </span><b style="font-family: 'Times New Roman'; white-space: normal;"><i>res/bg_knobview</i></b></pre>
<pre class="xml"><?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
<solid android:color="#aaaaaa" />
</shape></pre>
<pre class="xml"></pre>
and<b><i>res/bg_knob</i></b></div>
<div>
<pre class="xml"><?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
<solid android:color="#333333" />
</shape></pre>
<br />
Now it is time to move our knob. It is a very simple thing to: We will capture the touches on our view using the function <i>onTouchEvent</i> and when an <i>ACTION_DOWN </i>or <i>ACTION_MOVE </i>is detected, we move the knob to the location of the event. How do we do so? It's pretty easy: we use the setBounds function that we used in the <i>onSizeChanged</i>.<br />
<br /></div>
<pre class="java">@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = MotionEventCompat.getActionMasked(event);
if(action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
int w = getWidth();
int h = getHeight();
int x = (int) event.getX();
int y = (int) event.getY();
mKnobDrawable.setBounds(x-w/4, y-h/4, x+w/4, y+h/4);
invalidate();
}
return true;
}
</pre>
Notice that we only did two things if the action is <i>ACTION_DOWN</i> or <i>ACTION_MOVE</i>:<br />
<br />
<ol>
<li>Set the bounds of our knob drawable based on the location of the event: We kept its width = w/2 and its height h/2 but we translated it to (x,y), the location of the event</li>
<li>Invalidated the view using <i>invalidate()</i> to force our view to redraw -i.e. to move the knob.</li>
</ol>
<div>
The last natural thing to do is move the knob back when the user stops his touches. That is almost the same thing but with x and y set to the midpoint of the view -i.e. (w/2, h/2). Therefore our final <i>onTouchEvent</i> will look something like:</div>
<div>
<br /></div>
<pre class="java">@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = MotionEventCompat.getActionMasked(event);
if(action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
int w = getWidth();
int h = getHeight();
int x = (int) event.getX();
int y = (int) event.getY();
mKnobDrawable.setBounds(x-w/4, y-h/4, x+w/4, y+h/4);
invalidate();
}
else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
int w = getWidth();
int h = getHeight();
int x = w/2;
int y = h/2;
mKnobDrawable.setBounds(x-w/4, y-h/4, x+w/4, y+h/4);
invalidate();
}
return true;
}
</pre>
<div>
<br /></div>
<div>
Notice that the only difference is that x and y are not set to w/2 and h/2 respectively.<br />
<br />
At the end of this part, your knob should be able to move when touched and return to its original place when released. <a href="https://github.com/sherifelkhatib/WidgyWidgets" target="_blank">Clone the WidgyWidget repository to try it yourself</a>.<br />
<br />
Have fun (:<br />
<br />
Note: If you want to get the code of this part only, clone and checkout tags/knobview-part1 using the following commands (If you ommit <span style="background-color: #eeeeee; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px;">folder_name</span>, it will automatically be cloned into folder WidgyWidgets) :<br />
<br />
<span style="background-color: #eeeeee; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px;">git clone https://github.com/sherifelkhatib/WidgyWidgets.git folder_name</span><br />
<span style="background-color: #eeeeee; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px;">cd folder_name</span><br />
<span style="background-color: #eeeeee; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px;">git checkout tags/knobview-part1</span><br />
<span style="background-color: #eeeeee; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px;">... Try it and when you are done ...</span><br />
<span style="background-color: #eeeeee; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; line-height: 18px;">git checkout master</span></div>
Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com58tag:blogger.com,1999:blog-646415056870061414.post-92086923588311517972013-09-06T17:21:00.000+03:002013-09-06T17:21:46.713+03:00Universal Image Loader Wrapper for AndroidI am not really sure if this is a good idea or not. Yet, I did it and created a github project that is a wrapper for <a href="https://github.com/nostra13/Android-Universal-Image-Loader" target="_blank">Sergey Tarasevich's Android-Universal-Image-Loader</a>.<br />
<br />
<a name='more'></a>In the first version, I just included one of the classes I usually use in my projects, namely ImageLoadingView.<br />
I have, of course, removed many of the functions in the class to make it a minimal wrapper for now.<br />
<br />
It could be used in the following way:<br />
<br />
<pre class="xml"> <mobi.sherif.util.ui.ImageLoadingView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:bitmapConfig="RGB_565"
app:cacheInMemory="true"
app:cacheOnDisc="false"
app:delayBeforeLoading="1000"
app:imageForEmptyUri="@drawable/ic_empty"
app:imageOnFail="@drawable/ic_error"
app:imageOnLoading="@drawable/ic_stub"
app:resetViewBeforeLoading="true" /></pre>
<br />
Obviously, not all the configurations of UIL's DisplayImageOptions are available in xml but hopefully this will change in future releases.<br />
<br />
Another important addition is specifying the url of the image to be loaded in XML using:<br />
<br />
<pre class="xml"> app:imageUrl="http://www.android.com/images/logo.png"</pre>
<br />
Some times, some java code should be used to set the url: This still possible using the various <b>setImage</b> provided in the <b>ImageLoadingView</b> class.<br />
<br />
Basically, that is what this wrapper is for now. It is <a href="https://github.com/sherifelkhatib/Android-Universal-Image-Loader-Wrapper" target="_blank">available on github</a>.Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com226tag:blogger.com,1999:blog-646415056870061414.post-76191902335537744962013-04-03T12:09:00.002+03:002013-04-03T12:09:58.939+03:00Share on User's Wall using Facebook SDKApps are really getting into the "sharing" thingy. I am unsure why many developers hate to use the ACTION_SEND. Instead, they like to go into the hassle of integrating facebook into their app using the always-changing sdk facebook's offering.<br />
<br />
I know many of you want to set the "picture" / "name" and other parameters of the facebook share, and, therefore, the normal ACTION_SEND is not adequate. If it were to me, I would force you all to use the ACTION_SEND.<br />
Since it is not:<br />
<br />
<a name='more'></a>Here is what I consider the right way of using the Facebook SDK for the purpose of sharing. Here, I present the user with a sharing dialog so that he can add his <b>own message</b> to the share. I think that's the whole point of social media: user engagement. If you want auto-share, that is pretty simple and I do not feel like posting about it.<br />
<div>
<br /></div>
<div>
Ok. There are dozens of tutorials out there that will help you in implementing Facebook login. You will end up with a Session object that will return <i><b>true</b></i> if you call the <i style="font-weight: bold;">isOpened</i> function upon it.</div>
<div>
<br /></div>
<div>
In the following code, I used mContext and mySession: mContext is the context which is usually the activity where you want to share - mySession is the Facebook Session object which you would probably get by calling <b>Session.getActiveSession()</b>; or it could be handed to you in the <b>StatusCallback</b> when you login<br />
<br />
Here is how to use it to share: </div>
<div>
<br /></div>
<pre class="java" name="code">Bundle bundle = new Bundle();
bundle.putString("caption", "Harlem Shake Launcher for Android");
bundle.putString("description", "Your android can do the Harlem Shake. Download it from google play");
bundle.putString("link", "https://play.google.com/store/apps/details?id=mobi.shush.harlemlauncher");
bundle.putString("name", "Harlem Shake Launcher");
bundle.putString("picture", "http://shush.mobi/bla.png");
new WebDialog.FeedDialogBuilder(mContext, mySession, bundle).build().show();
</pre>
Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com166tag:blogger.com,1999:blog-646415056870061414.post-50113967677962685232013-03-21T16:29:00.001+02:002013-03-21T16:29:28.804+02:00How to make ListView super fastThis might be the most asked question. How a listview can be made faster/smoother is really all about optimizations of the code.<br />
<br />
<a name='more'></a><br /><br />
You should probably always use a Holder class not to <b>findViewById</b> in every <i>getView</i> call. According to a google IO thingy, this would <b><i>slightly</i></b> increase the performance of your ListView.<br />
<br />
Of course, do not forget to use the logic: <b>if(convertView == null)</b> then <b>(convertView = new InflatedView)</b>. Then intialise everything in convertView... bla bla bla.<br />
<br />
<pre class="default prettyprint prettyprinted" style="background-color: #eeeeee; border: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 13.600000381469727px; line-height: 17.600000381469727px; margin-bottom: 10px; max-height: 600px; overflow: auto; padding: 5px; vertical-align: baseline; width: auto;"><code style="border: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"><div style="font-family: 'Times New Roman'; font-size: medium; line-height: normal; white-space: normal;">
However, today I got a +1 on <a href="http://stackoverflow.com/a/12451829/833622" target="_blank">one of my answers on stackoverflow</a>. I gave away a small hack.</div>
<div style="font-family: 'Times New Roman'; font-size: medium; line-height: normal; white-space: normal;">
</div>
<pre class="default prettyprint prettyprinted" style="border: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 13.600000381469727px; line-height: 17.600000381469727px; margin-bottom: 10px; max-height: 600px; overflow: auto; padding: 5px; vertical-align: baseline; width: auto;"><code style="border: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"><pre class="default prettyprint prettyprinted" style="border: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 13.600000381469727px; line-height: 17.600000381469727px; margin-bottom: 10px; max-height: 600px; overflow: auto; padding: 5px; vertical-align: baseline; width: auto;"><code style="border: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"><span class="lit" style="background-color: transparent; border: 0px; color: maroon; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">@Override</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="kwd" style="background-color: transparent; border: 0px; color: darkblue; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">public</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="kwd" style="background-color: transparent; border: 0px; color: darkblue; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">long</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> getItemViewType</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">(</span><span class="kwd" style="background-color: transparent; border: 0px; color: darkblue; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">int</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> position</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">)</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">{</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="kwd" style="background-color: transparent; border: 0px; color: darkblue; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">return</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> position</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">;</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">}</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="lit" style="background-color: transparent; border: 0px; color: maroon; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">@Override</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="kwd" style="background-color: transparent; border: 0px; color: darkblue; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">public</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="kwd" style="background-color: transparent; border: 0px; color: darkblue; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">long</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> getViewTypeCount</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">(</span><span class="kwd" style="background-color: transparent; border: 0px; color: darkblue; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">int</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> position</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">)</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">{</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="kwd" style="background-color: transparent; border: 0px; color: darkblue; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">return</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> getCount</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">();</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">}</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="lit" style="background-color: transparent; border: 0px; color: maroon; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">@Override</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="kwd" style="background-color: transparent; border: 0px; color: darkblue; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">public</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="typ" style="background-color: transparent; border: 0px; color: #2b91af; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">View</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> getView</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">(</span><span class="kwd" style="background-color: transparent; border: 0px; color: darkblue; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">int</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> position</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">,</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="typ" style="background-color: transparent; border: 0px; color: #2b91af; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">View</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> convertView</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">,</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="typ" style="background-color: transparent; border: 0px; color: #2b91af; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">ViewGroup</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> arg2</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">)</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">{</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="kwd" style="background-color: transparent; border: 0px; color: darkblue; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">if</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">(</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">convertView </span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">==</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="kwd" style="background-color: transparent; border: 0px; color: darkblue; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">null</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">)</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> </span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">{</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="com" style="background-color: transparent; border: 0px; color: grey; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">//inflate your convertView and set everything</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">}</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="com" style="background-color: transparent; border: 0px; color: grey; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">//do not do anything just return the convertView</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="kwd" style="background-color: transparent; border: 0px; color: darkblue; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">return</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;"> convertView</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">;</span><span class="pln" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">
</span><span class="pun" style="background-color: transparent; border: 0px; font-size: 13.600000381469727px; margin: 0px; padding: 0px; vertical-align: baseline;">}</span></code></pre>
</span></code></pre>
<br />
<div style="font-family: 'Times New Roman'; font-size: medium; line-height: normal; white-space: normal;">
I found out that I had a small typo: I fixed that. Anyway, I would like to give this away and it would really be nice to tell me if this does not work with you.</div>
<div style="font-family: 'Times New Roman'; font-size: medium; line-height: normal; white-space: normal;">
</div>
<div style="font-family: 'Times New Roman'; font-size: medium; line-height: normal; white-space: normal;">
The hack is simple: I give each view its own view type, and, therefore, I force the ListView to do the whole caching on its own.</div>
</span></code></pre>
Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com50tag:blogger.com,1999:blog-646415056870061414.post-33225753386250029902013-03-12T00:03:00.001+02:002013-03-12T00:03:16.692+02:00Harlem Shake Launcher Released<br />
Just like the title says, your app icons can now do the harlem shake!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/NXErP2VuJZA?feature=player_embedded' frameborder='0'></iframe></div>
<br />
Available on <a href="https://play.google.com/store/apps/details?id=mobi.shush.harlemlauncher" target="_blank">Google Play :D</a> "Harlem Shake Launcher"<br />
<br />
<a name='more'></a><br />
<br />
DISCLAIMER:<br />
<br />
Harlem Shake Launcher uses the open-source launcher: android-launcher-plus. See PRIVACY POLICY<br />
Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com28tag:blogger.com,1999:blog-646415056870061414.post-76965459822078586242013-01-24T17:52:00.000+02:002013-01-24T17:57:00.607+02:00How to get width/height of a ViewAs the idea of the Android evolved, Android has received wide attention and deployed on a very wide range of devices. Android UI had to move and make developers' life easier: AbsoluteLayout got deprecated. It is very logical because your app will be installed on very small devices and very large devices and all the devices in between.<br />
<br />
Now, it is all WRAP_CONTENT, FILL_PARENT/MATCH_PARENT. Yet, a developer sometimes needs to know the dimensions of his view to do some extra tweaks to perfect his ui.<br />
<br />
So, what is the best way to do so?<br />
<a name='more'></a>Well, there are several ways of getting the dimensions of a view. Most of them boil down to waiting the layout of the view hierarchy. In case you still have not gotten into the problem of getWidth() and getHeight() returning 0, well that is very normal in Android as the width and height of a view are zero until they are visible, measured, and part of the layout.<br />
<br />
In the following I will assume that your View is called <i><b>view</b></i>.<br />
<b><br /></b>
<br />
<ol>
<li><b><b>Using the famous <i>OnGlobalLayoutListener</i>:</b></b></li>
<br />
This is one of the most used mechanisms to get the view dimensions. You attach a <a href="http://developer.android.com/reference/android/view/ViewTreeObserver.OnGlobalLayoutListener.html" target="_blank">Global Layout Listener</a> to the view hierarchy. It helps you actually get the width of all the views in your view heirarchy: <br /><br /><pre class="java" name="code">view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
//now we can retrieve the width and height
int width = view.getWidth();
int height = view.getHeight();
//...
//do whatever you want with them
//...
//this is an important step not to keep receiving callbacks:
//we should remove this listener</pre>
<pre class="java" name="code"><pre class="java" name="code"> //I use the function to remove it based on the api level!</pre>
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
else
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
</pre>
<br /><br />
<li><b>By extending a View:</b></li>
<br />Personally, I use this approach when the whole Activity or Layout is really dependant on one view only, most probably, a GridView or a ListView. I have already presented this solution on one SO question: <a href="http://stackoverflow.com/questions/13681024/how-to-manage-gridview/13835812#13835812" target="_blank">How to manage GridView</a>. The whole idea is to instantiate your view in your code instead of inserting it in the layout xml, and by doing this, you are able to override the onLayout function.<br /><br />
<pre class="java" name="code">mGrid = new GridView(this) {
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
//here you have the size of the view and you can do stuff
}
};</pre>
<br /><br />
<li><b>By forcing a measurement of the View:</b></li>
<br />This way is probably rarely used but I personally like it. I am not 100% sure but this probably is the most lightweight of them all, although it might not be as precise. If you force the view to measure itself, you can get the measured dimensions. I use this when you need to move some views (using their margins) based on other views when you can not simply use some of the layout properties because they have different parents or something. Here is the way to do it:<br /><br /><pre class="java" name="code">view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
int widht = view.getMeasuredWidth();
int height = view.getMeasuredHeight();</pre>
</ol>
Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com153tag:blogger.com,1999:blog-646415056870061414.post-50807181178190909872013-01-16T02:53:00.002+02:002013-04-16T15:51:46.854+03:00Android: the sword of Damocles hanging over "Facebook's Graph Search"The past years have definitely pushed Microsoft away, got Facebook on the big screens, and furthermore emphasized Google's dominance. Now it is more of Is it Facebook or Google?<br />
<a name='more'></a><br />
<br />
Well, Facebook is by far the best social network and its strength is not in its so-called <i>internal</i> graph, but the integration it supports with different applications and its own application platform. On the other side, Google is the user's number one search engine and losing this edge can prove costly. Yet, another edge is Android, something that Facebook has left to the future: I would not be surprised to see a Facebook OS: That is cool!<br />
<br />
Apparently, it is clear that Facebook wants to have a bite of Google's share in the search engine market. Will that succeed? To a certain extent, every service Facebook launches will be colorful and wow! You have the social side that is missing Google. What makes it even better is the fact that Facebook has a brilliant team; Well, so does Google.<br />
<br />
Facebook's Graph Search will most probably do what is advertised! You will be able to search "what is the best book" and "where should I eat". It it awesome; With this Search, many will probably opt out Google for people tend to prefer search results related to THEM/ME and not just any search results. Yes, true but this will prove to be a very hard task and launching the graph search is just the first step in a million steps journey.<br />
<br />
First of all, <b>Facebook's search has a stinky reputation.</b> Have you ever tried to search for someone on Facebook! You should either spell the exact name or spend hours checking the results. You probably have more luck seeing him on "People you may know" section! Facebook will have to spend more than a twitter Trend and some Facebook shares to advertise this new search.<br />
<br />
Secondly, Google chrome is one barrier Facebook must overcome. Not all average users will change their search engine. With almost a 50% share in the browser usage, <b>Chrome stands in the face of Facebook</b>. Google still have some cards to play and I believe they will do it sooner not later.<br />
<br />
Thirdly, we should be aware that <b>Google's search is not entirely anonymous</b>. It is actually integrated with Google Plus; Gmail; and, more generally, any Google account. I am not sure if that is stated somewhere but you should try to notice how search results evolve on Google (for the same search/query).<br />
<br />
Finally, the whole world is moving to mobile and Facebook's main concern here is advertisements' revenues cut-off! They may manage to put advertisements in their mobile apps but it is not a pc whatsoever. With Google's admobs, it is really hard to match their pace. Yet, <b>Android is the sword of Damocles hanging over Facebook's head</b>! Android is a Google product: Google search will persist there until a Facebook Phone emerges from somewhere.<br />
<br />
A good conclusion would state that Facebook's Graph Search is very important step right now. Definitely, it will be totally awesome! But, least to say, old habits die hard.Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com53tag:blogger.com,1999:blog-646415056870061414.post-73749785844627429692012-12-19T13:11:00.001+02:002012-12-19T13:11:43.511+02:00Applying style to views dynamically in java codeIn my answer to <a href="http://stackoverflow.com/a/11195473/833622" target="_blank">Applying style to views dynamically in java code</a> on <span class="relativetime" style="background-color: white; border: 0px; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 13px; line-height: 12px; margin: 0px; padding: 0px; vertical-align: baseline;" title="2012-06-25 18:51:50Z">Jun 25 at 18:51</span>, I thought setting a style to a view dynamically would be as simple as passing the style to the constructor of your view. A couple of minutes ago, I had a notification and a comment (Thank you for it) saying that this does not work. I had a freaking -1 ):<br />
<br />
<a name='more'></a>My first solution was to override the constructors and pass the style as a third parameter to the <a href="http://developer.android.com/reference/android/view/View.html#View(android.content.Context, android.util.AttributeSet, int)" target="_blank">View(android.content.Context, android.util.AttributeSet, int)</a> constructor in this manner:<br />
<pre class="java" name="code">public StyledButton(Context context) {
super(context, null, R.style.Button_PrayerTimeButton);
//... whatever
}
public StyledButton(Context context, AttributeSet attrs) {
super(context, attrs, R.style.Button_PrayerTimeButton);
//... whatever
}
public StyledButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, R.style.Button_PrayerTimeButton);
//... whatever
}
</pre>
<br />
So, obviously this did not work although according to the documentation of the third parameter of the constructor:
<br />
<blockquote class="tr_bq">
<span style="background-color: #f9f9f9; color: #222222; font-family: Roboto, sans-serif; font-size: 14px; line-height: 19px;">The default style to apply to this view. If 0, no style will be applied (beyond what is included in the theme). This may either be an attribute resource, whose value will be retrieved from the current theme, or <b>an explicit style resource</b>.</span></blockquote>
Anyway, now that <span style="color: #222222; font-family: Roboto, sans-serif; font-size: 14px; line-height: 19px;"><i>an explicit style resource</i></span> is another dead end, I had to find a work around!<br />
And here it is: <b>Instead of supplying a style to the constructor</b> (which again doesn't work for some unknown <i>native</i> reason), <b>you have to supply an attribute and define that attribute in your theme</b> as follows:<br />
<br />
<i><b>styles.xml</b>:</i><br />
<br />
<br />
<pre class="xml" name="code"><resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="AppBaseTheme" parent="android:Theme.Light">
<item name="@attr/gangnamStyle">@style/myDefaultStyle</item>
</style>
<attr name="gangnamStyle" format="reference" />
<style name="myDefaultStyle" parent="@android:style/Widget.Button">
<item name="android:background">#f00</item>
</style>
</resources>
</pre>
<br />
<i><b>StyledButton.java</b>:</i><br />
<br />
<br />
<pre class="java" name="code">public class StyledButton extends Button {
public StyledButton(Context context) {
super(context, null, R.attr.gangnamStyle);
}
public StyledButton(Context context, AttributeSet attrs) {
super(context, attrs, R.attr.gangnamStyle);
}
public StyledButton(Context context, AttributeSet attrs, int defStyle) {
super(context, null, R.attr.gangnamStyle);
}
}
</pre>
<br />
Finally, you just have to set the theme of the Activity or Application to your Theme (in this case AppBaseTheme) :
<br />
<pre class="xml" name="code"> <application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
<b>android:theme="@style/AppBaseTheme"</b> >
</pre>
<br />
And that's it! Enjoy<br />
<br />
<br />
<span style="font-size: x-large;"><a href="http://www.mediafire.com/?3nbbdc7yfab392u">Click here for Sample Project + Source Code</a> (download)</span><br />
<br />Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com63tag:blogger.com,1999:blog-646415056870061414.post-54923670465017177402012-11-01T12:02:00.000+02:002012-11-01T12:02:00.368+02:00Application which always requires LoginIn this tutorial is an attempt to disallow the user to resume an application without signing in again. Whatever happens that causes my app to go off-screen should force the user to sign in again. In this context, signing in is rather case-specific: Say, every time the app is hidden and then shown again, the app will go to one specific activity.<br />
<br />
<a name='more'></a>To start with, we will need an activity that will be extended by all our activities. I will call it here RequireLoginActivity which is a base class and all our work will be in it.<br />
<br />
This activity has 2 requirements:<br />
<br />
<u>Requirement 1</u>: Once resumed, it should go to the Sign in screen if it was resumed from the background.<br />
<u>Requirement 2</u>: Once we decide to force the user to sign in, any instance of this activity should finish.<br />
<br />
<div>
<u><b>Requirement 2</b></u>:<br />
<br />
To start, I will accomplish the somehow easier requirement 2 by using <a href="http://stackoverflow.com/a/3008684/833622">Francesco Laurita's Solution</a>. Simply stated, each RequireLoginActivity should register a receiver that will listen for a CLOSE broadcast. Once we decide to go to the Sign in screen, we broadcast this message and all RequireLoginActivities alive will just finish.</div>
<div>
<br /></div>
<div>
First of all, let's name this BroadCast intent Action as INTENT_LOGOUT in RequireLoginActivity.</div>
<div>
<br /></div>
<div>
<pre class="java">public class RequireLoginActivity extends Activity {
private static final String INTENT_LOGOUT = "mobi.sherif.INTENT_LOGOUT";
}
</pre>
</div>
<div>
<br />
Now, we need the broadcast receiver: Each activity should have a logout receiver, so just after this INTENT_LOGOUT we initialize our receiver:<br />
<br />
<pre class="java">private final BroadcastReceiver mLogoutReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
finish();
}
};
</pre>
<br />
Notice that this receiver's only function is to finish the current RequireLogin Activity.<br />
<br />
Finally, we need to register the broadcast receiver in our onCreate function and unregister it when our activity is finished in our onDestroy function:<br />
<br />
<pre class="java">@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(INTENT_LOGOUT);
registerReceiver(mLogoutReceiver, intentFilter);
}
@Override
protected void onDestroy() {
unregisterReceiver(mLogoutReceiver);
super.onDestroy();
}
</pre>
<br />
For the sake of completeness and neatness, we shall add a function that will broadcast this intent once we need to go to the SignIn screen. I will call it gotoLogin():<br />
<br />
<pre class="java">private void gotoLogin() {
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(INTENT_LOGOUT);
sendBroadcast(broadcastIntent);
}</pre>
<br />
<br />
<u><b>Requirement 1</b></u>:<br />
<br />
Now, we need a mechanism that will allow us to capture when that activity is hidden and shown. To be specific, we need one that allows us to differentiate between a normal onResume, onPause cycle due to activity navigation and a cycle caused by hiding the activity for any reason such as phone call, pressing the home button, screen lock, navigating to a different activity.<br />
<br />
Three cases should be captured when the onResume function is called:<br />
<br />
<ol>
<li>Jumping from one RequireLoginActivity to another RequireLoginActivity using a flavor of startActivity.</li>
<li>Jumping from one RequireLoginActivity back to a previous RequireLoginActivity by finishing the current activity.</li>
<li>Coming back to a RequireLoginActivity after hiding it (we should here show the login!)</li>
</ol>
<br />
<br />
The basic idea of my solution is to have 2 counters: number of Started activities (<b>startCounter</b>) and number of Paused activities (<b>pauseCounter</b>). Each time an activity starts we will increment <b>startCounter</b>. Similarly, when an activity pauses, <b>pauseCounter</b> should be incremented. In our onResume function, we will decide whether to go to the Sign in by comparing the 2 counters. We will gotoLogin() if the 2 counters are equal!<br />
<br />
Let me explain:<br />
At any time, case 1 can be captured simply because upon starting new activities, our <b>startCounter</b> will always be greater that <b>pauseCounter</b> by 1. This is true because we will always have one extra activity started but not paused.<br />
<br />
Also, case 3 is easily captured, because once you leave our app, say, using the HOME button, we will increment the <b>pauseCounter</b> and the 2 counters will become equal. Once the app is resumed, the onResume will decide to gotoLogin().<br />
<br />
Case 2 is a bit tricky, but simple as well. The trick is by overriding the finish() function and decrementing the <b>startCounter</b> once and the <b>pauseCounter</b> twice in it. Remember that when finishing the activity, onPause is called and our counters are equal. Now by decrementing <b>startCounter </b>once and <b>pauseCounter </b>twice, we ultimately returned to the counters' values of the previous activity, and <b>startCounter </b>will remain greater the <b>pauseCounter </b>by 1 when the previous activity resumes.<br />
<br />
<b>Implementation:</b><br />
I will implement the counters in my Application instance:<br />
<br />
<pre class="java">public class RequireLoginApplication extends Application {
private static int nRequiredStarted = 0; //this is our startCounter
public void incrementStart() {
++nRequiredStarted;
}
private static int nRequiredPaused = 0; //this is our pauseCounter
public void incrementPause() {
++nRequiredPaused;
}
public void notifyFinish() { //this is called when the activity finishes
--nRequiredPaused;
--nRequiredPaused;
--nRequiredStarted;
}
public boolean shouldLogin() { //this decides whether to go to the login screen(true)
return (nRequiredPaused==nRequiredStarted);
}
public void reset() { //reset the counters in the login screen
nRequiredPaused = 0;
nRequiredStarted = 0;
}
}
</pre>
<br />
Now, in our RequireLoginActivity, we simply <i><b>incrementStart</b></i> in <i>onCreate</i>, <b style="font-style: italic;">incrementPause </b> in <i>onPause</i>, and <i style="font-weight: bold;">notifyFinish</i> in <i>finish</i>.<br />
<br />
<pre class="java">@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(INTENT_LOGOUT);
registerReceiver(mLogoutReceiver, intentFilter);
((RequireLoginApplication)getApplication()).incrementStart();
}
@Override
protected void onPause() {
super.onPause();
((RequireLoginApplication)getApplication()).incrementPause();
}
@Override
public void finish() {
((RequireLoginApplication)getApplication()).notifyFinish();
super.finish();
}
</pre>
<br />
The last step is the <i>onResume</i> function: Just decide whether we need to <i style="font-weight: bold;">gotoLogin</i>:<br />
<br />
<pre class="java">@Override
protected void onResume() {
super.onResume();
if(((RequireLoginApplication)getApplication()).shouldLogin()) {
gotoLogin();
}
}
</pre>
<br />
<br /></div>
<span style="font-size: x-large;"><a href="http://www.mediafire.com/?mb6o7mtbg1cucx9">Click here for Sample Project + Source Code</a> (download)</span>Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com41tag:blogger.com,1999:blog-646415056870061414.post-8965083175814089142012-10-23T01:36:00.001+03:002012-11-01T14:47:02.860+02:00PART II: How to implement GCM PhP Push Server for androidIn <a href="http://www.sherif.mobi/2012/07/gcm-php-push-server.html">PART I: How to implement GCM PhP Push Server for android</a>, I have concluded with a function to send a push notification to a single device.<br />
This part only describes the function that may be used to send to multiple devices the same push message.<br />
<br />
<a name='more'></a>Batch push is very useful for many applications that require the delivery of certain messages to multiple uses. With the introduction of GCM, there is no more need to find hacks in order to get a decent delivery speed for your push message. I was previously able to achieve speeds of 6 seconds per 10k devices, which is relatively fast when compared to sending a single request for each of these devices.<br />
<br />
Anyway, with the new GCM, sending to multiple devices is a piece of cake. Just prepare your tokens in an array and use the following function. Keep in mind that you can specify up to 1000 receiver in one request: That is your limit now. For the sake of completeness, pushing to 10k (10 thousand) devices would require now 10 requests which would take on an average 10Mb connection less than a second. Here is the code:<br />
<br />
<pre class="php" name="code">function sendMessageToPhones($deviceTokens, $collapseKey, $messageText, $yourKey)
{
$headers = array('Authorization:key=' . $yourKey,
'Content-Type:application/json');
$data = array('message' => $messageText);
$data = array(
"registration_ids" => $deviceTokens,
"collapse_key" => (string)$collapseKey,
"data" => $data);
$data_string = json_encode($data);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://android.googleapis.com/gcm/send");
if ($headers)
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
$response = curl_exec($ch);
echo "\n";
echo "RESPONSE LENGTH: " . strlen($response) . "\n";
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch))
{
//request failed
return false; //probably you want to return false
}
if ($httpCode != 200)
{
//request failed
return false; //probably you want to return false
}
curl_close($ch);
return $response;
}
</pre>
Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com30tag:blogger.com,1999:blog-646415056870061414.post-14257423748122262052012-10-21T22:59:00.001+03:002012-10-22T18:38:43.744+03:00UI Trick: Sliding contentHere is a small trick that I use sometimes to show a related content in an activity. In the back of my mind, my intention might be to mimic the iphone's <span style="background-color: white; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;">presentModalViewController. </span>This would specifically be useful when you have a list of items which users might repeatedly click items to view an Expanded view of them. I prefer to use this instead of a new Activity.<br />
<a name='more'></a>The basic idea is simple: Use a SlidingDrawer with an invisible handle.<br />
<br />
In your activity add the SlidingDrawer to your content view. I usually set the Handle's height and width to 0dp. Then when you want to show the Expanded View you can just open the drawer.<br />
<br />
To hide or show the drawer is pretty straight forward using animateOpen and animateClose of the SlidingDrawer. It is preferable to wrap these calls in your own functions in order to be able to add your own code upon closing/opening the drawer. I know that we can use the OnDrawerOpenListener and OnDrawerCloseListener: just a matter of taste. Just wrap them in your own calls:<br />
<br />
<br />
<pre class="java" name="code">public void showDrawer() {
mDrawer.animateOpen();
}
public void hideDrawer() {
mDrawer.animateClose();
}
</pre>
<div>
<br /></div>
<br />
An important point to keep in mind is to handle the onBackPressed() of the activity that contains such a drawer<br />
<br />
<pre class="java" name="code">@Override
public void onBackPressed() {
if(mDrawer.isOpened())
hideDrawer();
else
super.onBackPressed();
}</pre>
<br />
<br />
<span style="font-size: x-large;"><a href="http://www.mediafire.com/?1ebsx0hcu6chsms">Click here for Sample Project + Source Code</a> (download)</span>
<br />
<br />
Here is the full code of the activity.<br />
<br />
<pre class="java" name="code">public class MainActivity extends Activity {
SlidingDrawer mDrawer;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDrawer = (SlidingDrawer) findViewById(R.id.drawer);
findViewById(R.id.buttonshow).setOnClickListener(new OnClickListener() {
public void onClick(View v) {
showDrawer();
}
});
findViewById(R.id.buttonhide).setOnClickListener(new OnClickListener() {
public void onClick(View v) {
hideDrawer();
}
});
}
public void showDrawer() {
mDrawer.animateOpen();
}
public void hideDrawer() {
mDrawer.animateClose();
}
@Override
public void onBackPressed() {
if(mDrawer.isOpened())
hideDrawer();
else
super.onBackPressed();
}
}</pre>
Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com28tag:blogger.com,1999:blog-646415056870061414.post-48294448242237716702012-09-17T13:10:00.000+03:002012-09-17T13:11:25.378+03:00How to send status Notification androidHere is a small function you can use to send a notification in android using the new Api which recommends using Notification.Builder. It still works on older 2.2+ devices.<br />
<br />
The main components of the Notification are the <i>ticker</i>, <i>title</i>, <i>content</i>, and i<i>ntent</i>:<br />
<br />
<a name='more'></a><br />
<br />
<i>ticker</i> : this is the text that shows on the top of the screen on the notification bar once the notification is received -i.e. "Someone just pinged you" or "John Smith likes your event"<i><br class="Apple-interchange-newline" />title</i> : this is the title of the notification that appears on the top of the notification<i><br class="Apple-interchange-newline" />content</i> : this is the text of the notification that appears usually below the title
<i><br class="Apple-interchange-newline" />intent</i>: this is the intent that describes the behavior when the user presses this notification, namely the Activity that starts when this notification is presed.<br />
<br />
<pre class="java" name="code">@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
private void sendNotification(String message) {
//we need to prepare the Notification object
Notification notification;
//this is the title of the notification
CharSequence contentTitle = getString(R.string.app_name);
// this is the text that flows by in the status bar when the notification first activates.
CharSequence ticker = message;
//this is the content of the notification
CharSequence contentText = message;
//we need an Identifier for this notification
//in this case I am trying to get a unique one because i dont want new notifications to replace old ones
int id = Integer.valueOf(String.valueOf(System.currentTimeMillis()).substring(5));
//let us prepare the intent
//my intent will start the activity ActivityNotification
Intent notificationIntent = new Intent(this, ActivityNotification.class);
//put any extra needed in my activity
notificationIntent.putExtra(S.extra.notification, message);
//my design requires that only one ActivityNotification to be open so I set the FLAG_ACTIVITY_SINGLE_TOP flag
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
//this is the pending intent (when user presses the notification)
PendingIntent contentIntent = PendingIntent.getActivity(this, id,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//use the new API if our phone's sdk is jelly bean or more
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
Notification.Builder builder =
new Notification.Builder(getApplicationContext())
.setDefaults(Notification.DEFAULT_ALL)
.setAutoCancel(true)
.setOnlyAlertOnce(true)
.setSmallIcon(R.drawable.ic_launcher)
.setTicker(ticker)
.setContentTitle(contentTitle)
.setContentText(contentText)
.setWhen(System.currentTimeMillis())
.setContentIntent(contentIntent);
notification = builder.build();
}
//use the old API otherwise
else {
notification = new Notification(R.drawable.ic_launcher, ticker, System.currentTimeMillis());
notification.defaults = Notification.DEFAULT_ALL;
notification.flags = Notification.FLAG_AUTO_CANCEL;
Context context = getApplicationContext();
notification.setLatestEventInfo(context, contentTitle, contentText,
contentIntent);
}
//get the Notification manager
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//send the Notification
mNotificationManager.notify(id, notification);
}
</pre>Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com10tag:blogger.com,1999:blog-646415056870061414.post-85456378296124574892012-09-03T05:51:00.001+03:002012-10-07T14:42:01.016+03:00How to create a Splash Activity - best practicesIn this small tutorial, I will explain how to add 2 types of splash activities in Android:<br />
<br />
<ol>
<li>Splash that shows a simple layout</li>
<li>Splash that plays a movie</li>
</ol>
<br />
<br class="Apple-interchange-newline" />
While working on different projects, I discovered that splash activities are really simple to create. You just create some xml layout for your activity, create the activity, and set a timer after a couple of seconds with a Runnable that starts another activity.<br />
<br />
<a name='more'></a><br />
<br />
However, I have introduced a small feature that complicates things. I think we all agree that users prefer to have the ability to skip the splash screen. Since we're dealing with customers having eyes and users having taste, we can not add a skip button. Instead, I think it would be best that a splash is skipped when the user touches the screen.<br />
<br />
Before coding, what is the major complication? Well, we do not want the splash to hang there if the user does not touch the screen so we still need the timer to start the second activity. Yet, we want the splash activity to start this second activity also when the screen is touched. The problem might happen when the user touches the screen and the timer is fired also: You will have 2 instances of the second activity on the stack - a really undesirable effect.<br />
<br />
<div style="direction: ltr;">
<b>Solution: </b>When I first did this, I used to complicate my life with some synchronization because it felt like there was always a situation where a user can start the second activity twice, either by tapping the screen multiple times or because of the timer being run even after skipping it. However, it was pretty simple, in all our splash activities, we should add the following piece of code before using any of the flavors of <i>startActivity</i>:<br />
<br /></div>
<pre class="java" name="code">if(isFinishing())
return;
</pre>
<br />
This code will work perfectly knowing that you will finish the Splash when you start the second activity. Anyway, in this tutorial, I will explain how to add this "skip" feature very easily but it is up to you to add it.<br />
<br />
<br />
<div id="splashnormalactivity">
<span style="font-size: large;"><b>Splash Activity that shows a simple layout</b></span></div>
<ol>
<li>Create a new Android project if you've not already done so</li>
<li>Create two Activities: <b>SplashActivity.java</b> and <b>MainActivity.java </b>in your <b>src</b> directory - (keep in mind that sometimes MainActivity is created automatically for you: it will be our second activity)</li>
<li>Fix your manifest to start the SplashActivity first:</li>
<div>
<br /></div>
<pre class="xml" name="code"><manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="mobi.sherif.splash"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".SplashActivity"
android:label="@string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:label="@string/title_activity_main" >
</activity>
</application>
</manifest>
</pre>
<br />
<li>Create your MainActivity as you wish...</li>
<li>In <b>SplashActivity.java</b>, Create the jump function that will jump to the second activity</li>
<div>
<br /></div>
<pre class="java" name="code">private void jump() {
//it is safe to use this code even if you
//do not intend to allow users to skip the splash
if(isFinishing())
return;
startActivity(new Intent(this, MainActivity.class));
finish();
}
</pre>
<div>
<br /></div>
<li>If you do not want to allow skipping, You, yourself, should skip this step and go to step 7.<br />In <b>SplashActivity.java</b>, override the <i>onTouchEvent(MotionEvent)</i> function using the following code:</li>
<div>
<br /></div>
<pre class="java" name="code">@Override
public boolean onTouchEvent(MotionEvent event) {
jump();
return true;
}
</pre>
<br />
However, Please keep in mind that you can allow users to skip the activity by using on click listeners or any other manner, just by calling <b>jump()</b><div>
<br /></div>
<li>If you wish to show a movie, jump to the <a href="http://www.blogger.com/blogger.g?blogID=646415056870061414#splashmovieactivity">Splash Activity that plays a movie</a> instead.<br />Add to your <b>res/layout</b> folder a new xml file and call it <b>activity_splash.xml </b>This will be the layout of the SplashActivity. Here is a sample:</li>
<div>
<br /></div>
<pre class="xml" name="code"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/textsplash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="@dimen/padding_medium"
android:text="This is my Splash Screen" />
</RelativeLayout>
</pre>
<div>
<br /></div>
<li>In <b>SplashActivity.java</b>, override the <i>onCreate(Bundle)</i> method and set the content view to the xml layout that you just created in step 7:</li>
<div>
<br /></div>
<pre class="java" name="code">@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
</pre>
<div>
<br /></div>
<li>Now add three members to your class (under the line <b><i>public class SplashActivity extends Activity</i></b>):</li>
<div>
<br /></div>
<pre class="java" name="code">private static final long SPLASH_TIME = 3000; //3 seconds
Handler mHandler;
Runnable mJumpRunnable;</pre>
<div>
<br /></div>
<i>
SPLASH_TIME: this is how long the splash will appear in milliseconds</i>
<br />
<i>
mHandler: this a handler that we can use to execute some code after some time</i>
<br />
<i>
mJumpRunnable: this a the runnable that will be executed after the time passes (we will probably just call jump() in this runnable)</i>
<br />
<br />
<li>Finally, lets fix the <i>onCreate(Bundle)</i> method to instantiate this handler and post this runnable:</li>
<div>
<br /></div>
<pre class="java" name="code">@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
mJumpRunnable = new Runnable() {
public void run() {
jump();
}
};
mHandler = new Handler();
mHandler.postDelayed(mJumpRunnable, SPLASH_TIME);
}
</pre>
<div>
<br /></div>
<div>
Here, we first instantiated the Runnable and as you may have noticed, the <i>run()</i> function just has one line with a call to the <i>jump()</i> method.<br />
We used the <i>postDelayed(Runnable, long)</i> method of the Handler to ask it to execute this Runnable after SPLASH_TIME passes.<br />
<br />
This concludes it. Once you run this, it will show up a screen that will be dismissed after 3 seconds.<br />
<br />
here is a <a href="http://www.mediafire.com/?8o969377qcnnc5v"><span style="font-size: large;">sample project with a splash that uses a normal layout</span></a></div>
</ol>
<div id="splashmovieactivity">
<span style="font-size: large;"><b>Splash Activity that plays a movie</b></span></div>
<br />
Do the first 6 steps of the <a href="http://www.blogger.com/blogger.g?blogID=646415056870061414#splashnormalactivity">Splash Activity that shows a simple layout</a> and continue here with step 7:<br />
<ol start="7">
<br />
<li>Add to your <b>res/raw </b>folder your movie (preferably an mp4 movie!) If you do not have a raw folder there, just create one. I will consider that it is called <b>splash.mp4 (</b><a href="http://www.mediafire.com/?p05ki89i2dt5x2x">here is a sample mp4 splash</a><b>)</b></li>
<li>In <b>SplashActivity.java</b>, override the <i>onCreate(Bundle)</i> method to play this video in this way:</li>
<div>
<br /></div>
<pre class="java" name="code">@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try{
VideoView videoHolder = new VideoView(this);
setContentView(videoHolder);
Uri video = Uri.parse("android.resource://" + getPackageName() + "/"
+ R.raw.splash);
videoHolder.setVideoURI(video);
videoHolder.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
jump();
}
});
videoHolder.start();
} catch(Exception ex) {
jump();
}
}
</pre>
<div>
As discussed in one of my posts, <a href="http://www.sherif.mobi/2012/06/how-to-play-video-from-resources.html">How to play video from resources</a>, this is a good way of playing a video. We set the content view to a <i>VideoView</i>, which we give the movie that we previously added to the project.<br />
Two things are important: First, we need to use the <i>setOnCompletionListener </i>method of this <i>VideoView</i> and we set the <i>OnCompletionListener</i> to call our <i>jump()</i> function. Last but not least, we need to include all this code in a try...catch block and call <i>jump()</i> if an exception is thrown for some reason (we do not want a user stuck on the splash screen).</div>
<br />
here is a <a href="http://www.mediafire.com/?2fh99pl2gs42njq"><span style="font-size: large;">sample project with a splash that plays a movie</span></a></ol>
Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com15tag:blogger.com,1999:blog-646415056870061414.post-74083004750938854852012-07-05T23:35:00.002+03:002012-10-23T00:01:34.996+03:00PART I: How to implement GCM PhP Push Server for androidIn my previous post, <a href="http://sherifandroid.blogspot.com/2012/07/gcm-replaces-c2dm.html">GCM replaces C2DM</a>, I was just expressing my happiness of what I think is the best of the new Google Cloud Messaging.<br />
<br />
Now this is a small guide for those who would like to add push notifications to their applications:<br />
This part will explain how to prepare your server to send push messages. In another post, preparing the android app will be discussed.<br />
<br />
<a name='more'></a><br />
<h3>
STEP 1: Get an access token to issue push requests to the android GCM server</h3>
<div>
First of all, go to the <a href="https://code.google.com/apis/console">google APIs console page</a> and create a new project by pressing the Create project button. If you already have a project created, just create a new one for our purpose.</div>
<div>
<br /></div>
<div>
You will probably be directed to the services page, just search for "Google Cloud Messaging for Android" and press the button next to it (turn it on). Once you accept the license, you are good to go. Btw, if you were not redirected to the services page, just search for it, its somewhere on the page you are in.</div>
<div>
<br /></div>
<div>
Just before leaving, open your favorite text editor and save the id of your project: This can be found upstairs in the url (address bar), it will probably contain something like "<u>project:<span style="color: lime;">5664646216546</span></u>". Yeah! save the green number.</div>
<div>
<br /></div>
<div>
We are almost done, we need the token: search for API Access and press it. At the time of this writing, it was somewhere in the left menu on the page. Ok, once your there, you will find some sort of button saying "Create new Server key...". press it and you will get small form with bla bla bla and downstairs a "Create" button which you have to press. PS do not press the "Cancel" button.</div>
<div>
<br /></div>
<div>
Refresh the page to get the API key: here is a small illustration just to make sure you got it all right:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWHb2mA5A7A7xeqIoSQTS0LkDayOhjlqyhwclGGzIY4VqpJXlasjmMHeZ-_5AkhOmNw88cFB3GheZqrSD7C4ncdiAVynMHpNeDfhSxaCcb8OAJWdbKYok4FU1lPzSdATqvGrMut58DkGEz/s1600/api.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="Make sure this is there" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWHb2mA5A7A7xeqIoSQTS0LkDayOhjlqyhwclGGzIY4VqpJXlasjmMHeZ-_5AkhOmNw88cFB3GheZqrSD7C4ncdiAVynMHpNeDfhSxaCcb8OAJWdbKYok4FU1lPzSdATqvGrMut58DkGEz/s1600/api.png" title="" /></a></div>
<div>
<br /></div>
<div>
Congratulations, you have your TOKEN :D</div>
<h3>
STEP 2: Use the access token to issue push messages to devices</h3>
<div>
Now we have an access token: Lets send the message (I will edit this code to handle errors asap)<br />
<br />
Just to clarify <i>$yourKey</i> is the token that we have been working to get in STEP 1. <i>$deviceToken</i> is the token of the device which this message will be sent to. <i>$collapseKey</i> is a key used to override any previous messages with the same <i>$collapseKey</i>. I will post on getting this <i>$deviceToken</i> soon.<br />
<br />
<pre class="php" name="code">$yourKey = 'PUT_YOUR_KEY_HERE';
function sendMessageToPhone($deviceToken, $collapseKey, $messageText, $yourKey)
{
$headers = array('Authorization:key=' . $yourKey);
$data = array(
'registration_id' => $deviceToken,
'collapse_key' => $collapseKey,
'data.message' => $messageText);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://android.googleapis.com/gcm/send");
if ($headers)
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
//request failed
return false;//probably you want to return false
}
if ($httpCode != 200) {
//request failed
return false;//probably you want to return false
}
curl_close($ch);
return $response;
}
</pre>
This is tested and working! If you have any suggestions, leave a comment.<br />
<br />
Advice: <strike>For all those who did not watch Ace Ventura Pet Detective, watch it!</strike></div>
Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com15tag:blogger.com,1999:blog-646415056870061414.post-2232969059211777892012-07-03T02:36:00.001+03:002012-07-06T14:47:51.631+03:00GCM replaces C2DMFinally! good news from the android team! The old c2dm is deprecated and the new GCM is here:<br />
Same concept but much better features.<br />
<br />
I need to say that the best news is that we can finally get push messages as fast as an iphone does. Put simply, <b>we can now send to multiple devices the same push message in one request</b>!!!!<br />
<br />
<a name='more'></a><br />
<br />
That seems the best of the new apis, it was terribly annoying when we had to push one news entry to 20k android devices: the process took around 4-5 minutes in the perfect case even after optimization. At the same time, we could send the same message to 30k iphones in 5 seconds.<br />
<br />
Now, the android team introduces what definitely made my day, GCM! even its name sounds cool.<br />
<br />
Anyway, it seems most of the basics are still the same:<br />
<h3>
REGISTRATION:</h3>
<br />
<ul>
<li>It is still based on intents and upon firing a registration intent (<span style="color: #006600; font-family: 'courier new', courier, monospace; font-size: 14px; font-weight: bold; line-height: 21px;">com.google.android.c2dm.intent.REGISTER), your app will receive an <i>asynchronous</i> broadcast (<code style="color: #006600; font-family: 'courier new', courier, monospace; font-size: 14px; font-weight: bold; line-height: 1.5;">com.google.android.c2dm.intent.REGISTRATION</code>) which will hand your app a registration ID, aka token. This token is used by your server to send messages to the device</span></li>
<li>I know this is old news but it is important to understand that it is not good practice to send the registration intent every time the app starts (I know people who do this). Actually, you should work on sending the registration intent once and saving the token that you receive in the broadcast receiver and, if you wish to, send it several time to your own push server for no good reason at all!</li>
<li>Also, remember that your broadcast receiver's might receive multiple registration intents (<span style="color: #006600; font-family: 'courier new', courier, monospace; font-size: 14px; font-weight: bold; line-height: 21px;">onRegistered</span>) and, therefore, your server must have the necessary apis to replace an old token with a new one which of course implies that this function could handle the situation where is it triggered whilst a token already exists (inform the server to replace it)</li>
</ul>
<br />
<h3>
SENDING A MESSAGE:</h3>
<div>
<ul>
<li>Your server should have an api key as usual to send the messages. The data sent is still the same old name-value pair format. Now the cool thing is the ability to send to up to 1000 devices per request (using json formatted request):</li>
</ul>
Example request:<br />
<pre class="prettyprint" style="background-color: #f7f7f7; border: 1px solid rgb(221, 221, 221); color: #006600; font-family: 'courier new', courier, monospace; font-size: 14px; line-height: 1.5; margin-bottom: 1em; margin-top: 1em; overflow: auto; padding: 1em;"><span class="typ" style="color: #660066;">Content</span><span class="pun" style="color: #666600;">-</span><span class="typ" style="color: #660066;">Type</span><span class="pun" style="color: #666600;">:</span><span class="pln" style="color: black;">application</span><span class="pun" style="color: #666600;">/</span><span class="pln" style="color: black;">json
</span><span class="typ" style="color: #660066;">Authorization</span><span class="pun" style="color: #666600;">:</span><span class="pln" style="color: black;">key</span><span class="pun" style="color: #666600;">=</span><span class="typ" style="color: #660066;">AIzaSyB</span><span class="pun" style="color: #666600;">-</span><span class="lit" style="color: #006666;">1uEai2WiUapxCs2Q0GZYzPu7Udno5aA</span>
{ "collapse_key": "score_update",
"time_to_live": 108,
"delay_while_idle": true,
"data": {
"score": "4x8",
"time": "15:16.2342"
},
"registration_ids":["4", "8", "15", "16", "23", "42"]
}</pre>
</div>
<br />
Notice that the tokens are sent as a json array and that the data is still encoded as a name value pair. collapse_key is still there and it is used to replace any old (unsent) message queued in the google servers having the same collapse_key with this message.<br />
<br />
I am just happy and extremely happy that this has been implemented!!! <a href="http://developer.android.com/guide/google/gcm/index.html">Please check the Official Docs to implement fully</a>. Congratulations everybody!<br />
<br />
Related: <a href="http://www.sherif.mobi/2012/07/gcm-php-push-server.html">How to implement GCM PhP Push Server for android</a>Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com4tag:blogger.com,1999:blog-646415056870061414.post-82381616575070097672012-06-19T03:43:00.003+03:002012-06-19T03:43:58.619+03:00How to play a video from resourcesAs am getting lots of good feedback on my answer on SO for the question <b><a href="http://stackoverflow.com/a/8616187/833622">How to play a video from the raw or assets folder</a></b>, I thought of writing this small tutorial on playing a video in your activity.<br />
<br />
<a name='more'></a>So, say you want to create a splash activity that will show an mp4 video that you stored in your raw directory which is usually located in your projects's res. Usually, the directory raw is not there, so just create one there. Add your splash video to the raw folder: In my case, it is splash.mp4<br />
<br />
In your splash activity create the function splashPlayer():<br />
<br />
<pre class="java" name="code">public void splashPlayer() {
VideoView videoHolder = new VideoView(this);
setContentView(videoHolder);
Uri video = Uri.parse("android.resource://" + getPackageName() + "/"
+ R.raw.splash);
videoHolder.setVideoURI(video);
videoHolder.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
jumpMain(); //jump to the next Activity
}
});
videoHolder.start();
}
</pre>
<br />
In the previous code the function jumpMain() will probably jump you to the next activity. If you want to give the user the option to cancel your video/splash, add the following line to the end of splashPlayer():
<br />
<br />
<pre class="java" name="code">videoHolder.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
((VideoView)v).stopPlayback();
jumpMain();
return true;
}
});
</pre>
<br />
Finally, in your onCreate() function call this splashPlayer():
<br />
<br />
<pre class="java" name="code">@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
splashPlayer();
}
</pre>
<br />
An extra step would be to surround the splashPlayer() call in your onCreate() with a try...catch block. My personal experience shows that not all videos are playable on all devices even if they are mp4 videos. If this is the case, an exception is thrown and you can for example show an image instead. In this example I will only jump to the next Activity by calling jumpMain(), so my onCreate looks something like this:
<br />
<br />
<pre class="java" name="code">@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
splashPlayer();
} catch(Exception ex) {
jumpMain();
}
}
</pre>
<br />
Do not forget to call finish() in your jumpMain() <b>and enjoy</b>!<br />
<br />
Here is the full SplashActivity (I am just showing the general idea so feel free to notify me of errors or bugs)<br />
<pre class="java" name="code">public class ActivitySplash extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
splashPlayer();
} catch (Exception ex) {
jumpMain();
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return false;
}
public void splashPlayer() {
VideoView videoHolder = new VideoView(this);
setContentView(videoHolder);
Uri video = Uri.parse("android.resource://" + getPackageName() + "/"
+ R.raw.splash);
videoHolder.setVideoURI(video);
videoHolder.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
jumpMain();
}
});
videoHolder.start();
videoHolder.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
((VideoView) v).stopPlayback();
jumpMain();
return true;
}
});
}
private synchronized void jumpMain() {
Intent intent = new Intent(ActivitySplash.this, ActivityMain.class);
startActivity(intent);
finish();
}
}
</pre>Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com46tag:blogger.com,1999:blog-646415056870061414.post-58429293693029052562012-05-30T13:11:00.001+03:002012-06-10T17:02:50.426+03:00String arrays and Object arrays in SharedPreferencesI see many questions on SO asking about the best mechanism to store some kind of preferences such as statistics, favorites, or shortcuts. Many of these questions are answered with "Use a database".<br />
<br />
The biggest, yet clear, secret is that SharedPreferences are actually implemented using a <br />
<a name='more'></a><strike>database</strike>.
To check it, add some SharedPreferences to your app (Get the Editor and commit some changes). Now, run the app on the emulator, and got to DDMS and check the files of your package: you will see the database there. Okay, I take that back! It is an <u><b>XML</b> </u>file. I saw it once from a long time and it seems I thought back then that it was a database and the idea just stuck in my small brain! However everything still applies, as we all know, we can export a database into an excel sheet so no problem concept-wise.<br />
<br />
Anyway, SharedPreferences according to <a href="http://developer.android.com/guide/topics/data/data-storage.html#pref">the android documentation</a><br />
<blockquote>
is a class that provides a general framework that allows you to save and retrieve persistent key-value pairs of primitive data types.</blockquote>
Now let us take an example and try to mirror a database onto SharedPreferences.<br />
<br />
Say, we have a utility app that allows you to surf the net, and one of the features is to add bookmarks. Technically, the user has an array of Urls which are the bookmarks.<br />
On the first occasion, it would seem inconvenient to use Shared Preferences to save this array. However, it turns out that doing so is rather simple and to a very great extent has the same complexity of using a database.<br />
<br />
An array representation boils down to an integer representing a size and<br />
You could simply add a function setBookmarks that saves this array.<br />
Not to lose generality I will show a function that will actually save an array and I will call it saveArray.<br />
<br />
<br />
<pre class="java" name="code">public boolean saveArray(String[] array, String arrayName, Context mContext) {
SharedPreferences prefs = mContext.getSharedPreferences("preferencename", 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(arrayName +"_size", array.length);
for(int i=0;i<array.length;i++)
editor.putString(arrayName + "_" + i, array[i]);
return editor.commit();
}
</pre>
<br />
Now to get the array back, we just do the reverse operation and lets call it loadArray<br />
<br />
<br />
<pre class="java" name="code">public String[] loadArray(String arrayName, Context mContext) {
SharedPreferences prefs = mContext.getSharedPreferences("preferencename", 0);
int size = prefs.getInt(arrayName + "_size", 0);
String array[] = new String[size];
for(int i=0;i<size;i++)
array[i] = prefs.getString(arrayName + "_" + i, null);
return array;
}
</pre>
<br />
This whole implementation could actually be used to save a bit more complex classes.<br />
Say that your Bookmark entity has an integer representing maybe the number of views.<br />
The same approach can be made to save the whole data of the ith object.<br />
<br />
a sort of serialization.Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com52tag:blogger.com,1999:blog-646415056870061414.post-75876788382595409932012-01-31T23:23:00.000+02:002012-06-10T15:19:21.475+03:00ListView with ability to hide rowsI do not know why! but someone on stackoverflow was asking about setting the height of a row of a ListView to zero.<br />
<br />
Setting the height to zero will not work indeed! Many suggest to use View.Gone for the visibility of the row. However this is a ListView and therefore, setting a Visibility of Gone to one of the views will actually keep its space occupied while making its content vanish.<br />
<br />
Anyway, here is an implementation of <i>ArrayAdapter </i>that has two public functions hide(int) and unHide(int) allowing the user to hide/unhide the row at the position.<br />
<br />
<a name='more'></a>The approach taken here is based on keeping the views used by the <i>ListView </i>intact. This means we do not want memory to be overflowed and at the same time we want to be able to hide some entries.<br />
<br />
My way depends on, as usual, an array of objects. Without loss of generality, lets consider that this <i>Adapter </i>takes an array of <i>String</i> type and keeps it locally. To be able to hide, we need an array of <i>Boolean </i>type of the same size. These two arrays are <b>items</b> and <b>hidden </b>respectively. We initialize the <b>hidden </b>array to false in the constructor.<br />
<br />
<pre class="java" name="code">public class SherifHidingAdapter extends ArrayAdapter<String> {
String[] items = null;
boolean[] hidden = null;
public SherifHidingAdapter(Context mContext, String[] objects) {
super(mContext, R.layout.some_layout, objects);
items = objects;
hidden = new boolean[objects.length];
for (int i = 0; i < objects.length; i++)
hidden[i] = false;
}
}
</pre>
<br />
Now <i>ArrayAdapter </i>needs to override the function <i>getCount()</i>. This function usually returns the number of objects (rows) in the <i>ListView</i>. Typically, that would be the size of array <b>items</b>. In our case however, this size is not the real size of the <i>ListView</i>. We should subtract the number of hidden items from the size of <b>items</b>. And thereby:<br />
<br />
<pre class="java" name="code">@Override
public int getCount() {
return (items.length - getHiddenCount());
}
private int getHiddenCount() {
int count = 0;
for(int i=0;i<items.length;i++)
if(hidden[i])
count++;
return count;
}</pre>
<br />
Our public interface consists of two functions: one function <b>hide() </b>used to hide a certain row and another function <b>unHide()</b> used to <b>unHide()</b> a row. These function simply set the chosen index of the <b>hidden </b>array to true or false accordingly.<br />
<br />
<pre class="java" name="code">public void hide(int position) {
hidden[getRealPosition(position)] = true;
notifyDataSetChanged();
notifyDataSetInvalidated();
}
public void unHide(int position) {
hidden[getRealPosition(position)] = false;
notifyDataSetChanged();
notifyDataSetInvalidated();
}</pre>
<br />
Now the real work is done in the <i>getView()</i> function. The idea is very simple. Hopefully you can get it easily. We have a data source which is our array <b>items</b> in this case. We will create our Views as normally. However, we need some kind of translation between the position supplied by <i>getView()</i> or, more generally, by the <i>ListView</i> and the position of data. For example, if index 0 is hidden. The <i>ListView</i> will call <i>getView</i> with position = 0 as expected. The only difference will be that the data supplied will not be from index 0 of the data but from index 1. This translation is supplied by the function <b>getRealPosition</b> as follows:<br />
<br />
<br />
<pre class="java" name="code">private int getRealPosition(int position) {
int hElements = getHiddenCountUpTo(position);
int diff = 0;
for(int i=0;i<hElements;i++) {
diff++;
if(hidden[position+diff])
i--;
}
return (position + diff);
}
private int getHiddenCountUpTo(int location) {
int count = 0;
for(int i=0;i<=location;i++) {
if(hidden[i])
count++;
}
return count;
}</pre>
<br />
The last step is to translate the position supplied by <i>getView </i>to the new position using <b>getRealPosition</b>.<br />
<br />
<pre class="java" name="code">@Override
public View getView(int index, View convertView, ViewGroup arg2) {
//this is the secret step:
int position = getRealPosition(index);
if(convertView == null) {
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.some_layout, null);
}
// bla bla bla
// initialise the view
// bla bla bla
return (convertView);
}</pre>
<br />
This is all what you need to get a <i>ListView </i>that has the capability of hiding and unhiding its rows.<br />
<br />
Enjoy<br />
<br />
<br />
<span style="font-size: x-large;"><a href="http://www.mediafire.com/?y2ivcisztqc9ima">Click here for Sample Project + Source Code</a> (download)</span><br />
<br />Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com17tag:blogger.com,1999:blog-646415056870061414.post-35750737216352388282012-01-31T13:49:00.000+02:002012-06-10T15:20:23.397+03:00Adding an Exit button to Android ApplicationSo what is the right way of closing the application!<br />
<br />
Usually it is "USER! press back ! press back! keep pressing back! we are close!! okay finally bye"<br />
<br />
<br />
Here is a small idea i had on adding an Exit button to application.<br />
<br />
<br />
In my personal opinion, even android users will find a Close button nice and friendly.<br />
<br />
<a name='more'></a><br />
<br />
<br />
<pre class="java" name="code">public class ExitNotifier {
public ExitNotifier getInstance() {
if(instance==null)
instance = new ExitNotifier();
return instance;
}
public boolean isExitting() {
return exit;
}
public void doExit(boolean exit) {
this.exit = exit;
}
private static ExitNotifier instance;
boolean exit;
private ExitNotifier() {
exit = false;
}
}</pre>
<br />
<br />
Whenever the exit button is pressed, do the following
<br />
<br />
<pre class="java" name="code">ExitNotifier.getInstance().doExit(true);
finish();</pre>
<br />
<br />
In all activities, in your <i>onActivityResult </i>function add the following line:
<br />
<br />
<pre class="java" name="code">if(ExitNotifier.getInstance().isExitting())
finish();</pre>
<br />
<br />
The only requirement for this is to always use <i>startActivityForResult </i>when starting new Activities.
<br />
If you want to eliminate this requirement, just add the code you added to the <i>onActivityResult</i> to your <i>onResume </i>of all your activities.<br />
<br />
Enjoy! I like it!!Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com19tag:blogger.com,1999:blog-646415056870061414.post-19857981491648191372012-01-29T03:44:00.000+02:002012-01-29T13:36:38.725+02:00Passing primitive Data types between activitiesAfter some time working on android projects, I discovered that it is actually very important to pass data between activities in most projects. It is rather simple when it comes to passing primitive datatypes.<br />
<br />
<a name='more'></a><br /><br />
To pass integers or strings, one could easily follow the normal path of calling another activity (jump from Activity1 to Activity2) using startActivity or startActivityForResult<br />
<br />
The difference between the two is the simple design choice: whether Activity1 needs to know that Activity2 is closed. When this is a requirement (maybe to update something or to know whether Activity2 did it job or not), you should use startActivityForResult. This function takes an Intent and an int that is an identifier of this activity. This identifier will help you know that the activity you just called Activity2 is closed.<br />
To use this function and send primitive datatypes to Activity2 you can add to the Intent you created values of types: int, string, boolean, byte, char, CharSequence, double, float, long, and short using the function putExtra.<br />
So you start with creating an intent that will jump to Activity2:<br />
<pre class="java" name="code">Intent intent = new Intent(Activity1.this, Activity2.class);</pre>
<br />
This intent now can receive extra data using the putExtra function. Each variable or data instance you want to send must have an identifier so that you can retrieve this value in Activity2.<br />
<br />
<pre class="java" name="code">intent.putExtra("theDataIdentifier", yourData);</pre>
<br />
Now you call the function startActivityForResult and besides the Intent you just created, you pass an integer which is the unique identifier, named <i>requestCode</i>, for this Activity. In this case the value will be 9090. You can choose any integer of course.<br />
<br />
<pre class="java" name="code">startActivityForResult(intent, 9090);</pre>
<br />
To put it all together:<br />
<br />
<pre class="java" name="code">Intent intent = new Intent(Activity1.this, Activity2.class);
intent.putExtra("theDataIdentifier", yourData);
startActivityForResult(intent, 9090);
</pre>
<br />
Now your Activity2 is started and to get this data in Activity2 all you have to is the following<br />
<br />
<pre class="java" name="code">String myData = getIntent().getStringExtra("theDataIdentifier");</pre>
<br />
If you have many values to send you just use in Activity1 more <i>putExtra</i> calls with different data identifiers and get them using the same way in Activity 2.<br />
<br />
When Activity2 is dismissed maybe using the back button or when you call finish() in Activity2, the function protected void <i>onActivityResult (int requestCode, int resultCode, Intent data)</i> in Activity1 is called.<br />
The <i>requestCode </i>will automatically be in this case 9090 and the <i>resultCode </i>is set in Activity2 using the function setResult(int resultCode) , finally <i>data </i>contains the data that Activity2 wants to send to Activity1. So for example if a user presses a button Exit in Activity2 which has the following code:<br />
<br />
<pre class="java" name="code">setResult(10);
finish();
</pre>
<br />
You should have the function <i>onActivityResult (int requestCode, int resultCode, Intent data) </i>in Activity1 to receive this result:<br />
<br />
<pre class="java" name="code">@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 9090)
{//this means that Activity2 is the one that finished (there might be more Activities)
if(resultCode == 10)
//this means that the user pressed the Exit button
//you might show him a message for example: Why didnt you answer the question or Why did you exit .. you see the point
}
}
</pre>
<br />
Finally, if Activity2 needs to send some data back to Activity1. Instead of simply using a <i>finish </i>followed by a <i>setResult</i>, one could add an intent to the setResult function in Activity2:<br />
<br />
<pre class="java" name="code">Intent data = new Intent();
data.putExtra("myResultIdentifier", myDataValue);
setResult(11, data);
finish();</pre>
<br />
Now in the function <i>onActivityResult</i>, you can use the third parameter of the function to retrieve this result in the exact same way using the <i>getStringExtra</i>/<i>getIntExtra</i>/<i>getBooleanExtra</i> ... functions:<br />
<br />
<pre class="java" name="code">@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 9090)
{//this means that Activity2 is the one that finished (there might be more Activities)
if(resultCode == 11) {
if(data.getBooleanExtra("myResultIdentifier",false) { //false is the default parameter in case there is no extra called "myResultIdentifier"
//bla bla
}
else {
//bli bla
}
}
}
}
</pre>
<br />
Now, Activity1 is able to send primitive data types to Activity2, and Activity2 is also able to send back primitive Data types.<br />
<br />
One application would be a "Choose a date" button in Activity1 which takes you to Activity2 where a calender is shown. You could decide to send back a boolean that indicates whether the user canceled the operation or something. And you could also send the date back to Activity1 as a float value.Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com9tag:blogger.com,1999:blog-646415056870061414.post-48229927648908810722011-10-27T17:46:00.000+03:002012-06-19T23:54:04.887+03:00Sax Class Generator -v1.0<br />
Sax Class Generator is a class used to create a java class that extends <b>org.xml.sax.helpers.DefaultHandler</b>.<br />
<br />
It creates everything based on the tree structure of the xml that you provide.<br />
<br />
Current version depends on the GUI interface to get the nodes(structure) of the xml.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFS_sQo7Q7DukqW5oU9-kJWrkJcy6Qu5Tkzp6H20DTh1Ve282BE0LGwB0xr5zbx8gSRF8WxfGa0vW5GStNtcnWyEv1cvFXIEgGQ2qA_FekOfxgVX8rEViQ1gsmIIQ1mXQC1zlHRal8bLm-/s1600/Sax+Class+Generator+-v1.0.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFS_sQo7Q7DukqW5oU9-kJWrkJcy6Qu5Tkzp6H20DTh1Ve282BE0LGwB0xr5zbx8gSRF8WxfGa0vW5GStNtcnWyEv1cvFXIEgGQ2qA_FekOfxgVX8rEViQ1gsmIIQ1mXQC1zlHRal8bLm-/s320/Sax+Class+Generator+-v1.0.jpg" width="268" /></a></div>
<br />
<br />
<a name='more'></a><br /><br />
How to use:<br />
<br />
1- Once launched, You will be looking at the nodes in the top level of the expected xml file.<br />
<br />
2- Add as much root nodes as you need.<br />
<br />
3- For any node with children, Browse it and add nodes inside.<br />
<br />
4- Do step 3 for all nodes with children.<br />
<br />
5- Finally export the structure -e.g. Create the java class.<br />
<br />
Enjoy it! I think it saves lots of times and spans your whole xml structure.<br />
<br />
I hope that you all know how to use the class. In case you don't.<br />
<br />
<br />
<pre class="java" name="code">String yourXmlString;
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
/* Create a new instance of the class generated */
GeneratedHandler handler = new GeneratedHandler ();
xr.setContentHandler(handler);
InputSource inputSource = new InputSource();
inputSource.setEncoding("UTF-8");
inputSource.setCharacterStream(new StringReader(response));
/* Start Parsing */
xr.parse(inputSource);
/* Parsing Done. */
</pre>
<br />
Your job is to fill in the blanks in the class created so that you can instantiate objects of your own or Arrays or any sort of data that the xml contains.<br />
<br />
<span class="Apple-style-span" style="font-size: x-large;"><a href="http://www.mediafire.com/?dt9z4dm5c41v8ww">Click here for Sample Project</a> (download)</span>Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com15tag:blogger.com,1999:blog-646415056870061414.post-17675193811837341202011-10-14T14:45:00.000+03:002012-06-19T23:54:35.271+03:00SoftKeyboard problem with Tabhost on bottom of screenMany people (including myself) have encountered the problem of having an activity with a <b>Tabhost </b>aligned at the bottom of screen instead of its top.<br />
<br />
What happens when you have an <b>EditText </b>or any other input method!<br />
A soft keyboard will appear and push your <b>TabHost </b>above itself.<br />
<br />
What's the solution for this? Turns out to be simple!<br />
<a name='more'></a><br />
<br />
The cause of this problem is the <b>android:windowSoftInputMethod </b>property of your activity's tag in the manifest file.<br />
<br />
To solve it use <b>android:windowSoftInputMode="adjustPan|adjustResize"</b>.<br />
Now your tabs will not float above the keyboard when an input starts.<br />
<br />
This is really simple and does not need any further explanation. Whenever you decide to use a Tabbed Activity and you place your tabs on the bottom of the screen, use this code to disable the floating behavior.<br />
Ba bye<br />
<br />Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com12tag:blogger.com,1999:blog-646415056870061414.post-84992483421302374052011-09-27T17:19:00.000+03:002011-10-06T14:50:41.598+03:00html and activity links in TextView<span class="Apple-style-span" style="color: #333333; font-family: 'trebuchet ms'; font-size: large;"><span class="Apple-style-span" style="line-height: 20px;">So we need to linkify a TextView.</span></span><br />
<span class="Apple-style-span" style="color: #333333; font-family: 'trebuchet ms';"><span class="Apple-style-span" style="line-height: 20px;"><br /></span></span><br />
<span class="Apple-style-span" style="color: #333333; font-family: 'trebuchet ms';"><span class="Apple-style-span" style="line-height: 20px;">By linkify, I mean adding links to a TextView. These links could either be html links that link to a webpage, or some url that links to an Activity (launches an activity).</span></span><br />
<span class="Apple-style-span" style="color: #333333; font-family: 'trebuchet ms';"><span class="Apple-style-span" style="line-height: 20px;"></span></span><br />
<a name='more'></a><span class="Apple-style-span" style="color: #333333; font-family: 'trebuchet ms';"><span class="Apple-style-span" style="line-height: 20px;"><br /></span></span><br />
<br />
<pre class="java" name="code">TextView mTextSample = (TextView) findViewById(R.id.yourTextView);
String text = "Visit my blog sherifandroid.blogspot.com or run the myactivity callback";
mTextSample.setText(text);
Pattern pattern = Pattern.compile("sherifandroid.blogspot.com");
Linkify.addLinks(mTextSample, pattern, "http://");
Pattern pattern2 = Pattern.compile("myactivity");
Linkify.addLinks(mTextSample, pattern2, "sherif-activity://");
</pre>
<br />
Now, the first pattern with the html link to my blogger page will work correctly.<br />
<br />
However the second part should startActivity myactivity.<br />
It will not work unless we specify that <b><i>sherif-activity://myactivity</i></b> is a callback url for an activity of our package.<br />
<br />
So, for this to work and call say <b>TestActivity</b>!<br />
Your <b>TestActivity</b> in <i>AndroidManifest.xml</i> should look like this:<br />
<br />
<pre class="xml" name="code"><activity
android:label="@string/app_name"
android:name=".TestActivity">
<intent-filter>
<data android:host="myactivity" android:scheme="sherif-activity" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
</pre>
<br />
Congradulations, you just created a link to your website and to your activity in the same TextView.<br />
<br />
How about sending some data to the new activity using the link!?<br />
<br />
Lets first investigate the other way of linkifying a TextView. This would be using normal html tags, namely the <a> tag.<br />
<br />
<br />
<pre class="java" name="code">TextView mTextSample = (TextView) findViewById(R.id.yourTextView);
String text = "Visit my blog <a href=\"http://sherifandroid.blogspot.com/\">mysite</a> or run the <a href=\"sherif-activity://myactivity\">myactivity</a> callback";
mTextSample.setText(Html.fromHtml(text));
mTextSample.setMovementMethod(LinkMovementMethod.getInstance());
</pre>
<br />
This will work perfectly like the previous approach.<br />
<br />
Now, let us add some data to the link and ultimately the intent that will open the new activity.<br />
We will use the normal GET parameters to add data.<br />
<br />
so will will comment out one line of code which initializes <i style="font-weight: bold;">text</i> and change it so some data is added to linked activity.<br />
<br />
<br />
<pre class="java" name="code">String text = "Visit my blog <a href=\"http://sherifandroid.blogspot.com/\">mysite</a> or run the <a href=\"sherif-activity://myactivity?author=sherif&nick=king\">myactivity</a> callback";
//String text = "Visit my blog <a href=\"http://sherifandroid.blogspot.com/\">mysite</a> or run the <a href=\"sherif-activity://myactivity\">myactivity</a> callback";
</pre>
<br />
So How can we retrieve these extra parameters in the TestActivity!? In this case, the parameter <b>author </b>and <b>nick
</b><br />
It is rather simple:<br />
<br />
<br />
<pre class="java" name="code">String author = getIntent().getData().getQueryParameter("author");
String nick = getIntent().getData().getQueryParameter("nick");
</pre>
<br />
<br />
<span style="font-size: x-large;"><a href="http://www.mediafire.com/?cx8wc4bpexs4zxt">Click here for Sample Project + Source Code</a> (download) </span>Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com190tag:blogger.com,1999:blog-646415056870061414.post-7155756634073862232011-09-22T11:14:00.000+03:002011-09-24T10:05:37.836+03:00VibratingTextView<a href="http://www.blogger.com/blogger.g?blogID=646415056870061414#disclaimer">please read the disclaimer</a><br />
<a href="http://www.blogger.com/blogger.g?blogID=646415056870061414#formatting">please read the formatting notes</a><br />
<br />
<a href="http://www.blogger.com/blogger.g?blogID=646415056870061414#" name="beginning">.</a>
<br />
Here it comes the first real widget in this library which by the way I am still unsure about its name or its components' names.<br />
<br />
What's this really:<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/SOQ0nefS1lw" width="420"></iframe>
<br />
<br />
Anyway what is the idea behind this!?<br />
<br />
To be able to move each letter, <br />
<a name='more'></a>someone could guess that each letter is actually a view in this video. Ok someone caught me! yes in reality it is!<br />
<br />
In the sample project here is the xml for adding the VibratingTextView you see in the video:<br />
<br />
<pre class="xml" name="code"><sherif.android.textview.VibratingTextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:text="outToRight" >
</sherif.android.textview.VibratingTextView></pre>
<br />
Notice that it takes the usual attributes of a TextView. However also notice the <b>android:orientation</b>! Yes it is true it could have a vertical orientation as well.<br />
<br />
Ok the real idea behind it is that a <b><i>VibratingTextView </i></b>is actually a <b>LinearLayout </b>with each letter mapped to a <b>TextView </b>in the <b>LinearLayout</b>. It is done this way (Remember you must conclude by now that <i><b>VibratingTextView</b> </i>extends <b>LinearLayout</b>):<br />
<pre class="java" name="code">TextView[] array;
TextView view;
public VibratingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs);
view = new TextView(context, attrs, defStyle);
update();
}
public void update() {
String mText = view.getText().toString();
Context mContext = view.getContext();
setBackgroundDrawable(view.getBackground());
setPadding(view.getPaddingLeft(), view.getPaddingTop(), view
.getPaddingRight(), view.getPaddingBottom());
array = new TextView[mText.length()];
for (int i = 0; i < mText.length(); i++) {
array[i] = new TextView(mContext);
array[i].setText(String.valueOf(mText.charAt(i)));
array[i].setTextScaleX(view.getTextScaleX());
array[i]
.setTextSize(TypedValue.COMPLEX_UNIT_PX, view.getTextSize());
array[i].setTextColor(view.getTextColors());
addView(array[i]);
Log.v("", array[i].getText().toString());
}
animating = new boolean[array.length];
for (int i = 0; i < array.length; i++) {
animating[i] = false;
}
}</pre>
<br />
Notice that first we initialize a <b>TextView view</b> using the attributes that are passed to the constructor ~F~. The <b><i>VibratingTextView </i></b>(<b>this</b>) has its padding and background set to the padding of this view ~F~.<br />
<br />
Notice that there is an array of <b>TextView</b>s which is initialized. <b>TextView <i>i</i></b> has its text set to character <i><b>i</b></i> of the text. Many attributes of the <b>TextView</b> are given to the elements of this array using setters ~F~.<br />
<br />
Finally, <i>animating </i>is an array of <b>boolean </b>that reflects the state of each character (whether it is animating or not). This is also an ~F~ because I am not sure if this will be needed; however I kept it because it may be used to add some nice functionalities.<br />
<br />
How does it vibrate? using the <i>vibrate()</i> function<br />
<pre class="java" name="code">private void vibrate() {
for (int i = 0; i < array.length; i++) {
animate(i);
}
}
private void animate(int index) {
Animation animation = randomizeAnimation();
animation.setDuration(duration);
animation.setInterpolator(new AccelerateInterpolator());
animation.setAnimationListener(new AnimationsListener(index));
array[index].startAnimation(animation);
}
private Animation randomizeAnimation() {
return new TranslateAnimation(Animation.RELATIVE_TO_SELF,
0.0f, Animation.RELATIVE_TO_SELF, (float) Math.random() * 0.1f * (((float) Math.random() > 0.5) ? -1 : +1),
Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_SELF, (float) Math.random() * 0.1f * (((float) Math.random() > 0.5) ? -1 : +1));
}
</pre>
<br />
Pretty simple; the function is called on touch events. <i>vibrate </i>will call <i>animate </i>for each of the <b>TextView</b>s in <i>array</i>. aimate in turn will create an animation using <i>randomizeAnimation </i>which returns a <b>TranslateAnimation </b>that will cause the view to move in its own vicinity (not too far). <i>randomizeAnimation </i>is straight-forward and uses the <b>Math</b>'s random function to create a random movement. Now after getting the animation using this randomization function, a duration is set (<i>duration = 100</i>) and an <b>AccelerateInterpolator</b> is set for the animation ~F~. Finally before starting the animation, a listener is set for it which is of type <b><i>AnimationsListener </i></b>which is a private class. I will not elaborate but it actually decides whether to continue animating and repeat this cycle (re generate an animation) or to stop.<br />
<br />
This is working fine but of course I will work on enhancing it (this is just the first version).<br />
<br />
<br />
<span style="font-size: x-large;"><a href="http://www.mediafire.com/?q78igr6i1bvjy00">Click here for Sample Project + Source Code</a> (download) </span>not always as up-to-date as github<br />
<br />
<span style="font-size: x-large;"><a href="https://github.com/sre08/AnimationFactory">Click here for Sample Project + Source Code</a> (github)</span>
<br />
<br />
<a href="http://www.blogger.com/blogger.g?blogID=646415056870061414#" name="formatting">Formatting notes</a>:<br />
<b>Bold</b>: a class in the android packages<br />
<b><i>BoldandItalic</i></b>: a class created by me<br />
<i>Italic</i>: a function or object in my code<br />
<span class="Apple-style-span" style="font-size: xx-small;"><a href="http://www.blogger.com/blogger.g?blogID=646415056870061414#beginning">back to top</a></span>
<br />
<br />
<a href="http://www.blogger.com/blogger.g?blogID=646415056870061414#" name="disclaimer">Disclaimer</a>:<br />
<span class="Apple-style-span" style="font-size: x-small;">There are some code snippets or statements that you may have comments on.</span><br />
<span class="Apple-style-span" style="font-size: x-small;">Please do not comment on any statement with a <b>~F~</b> beside it because this means on of these three things:</span><br />
<span class="Apple-style-span" style="font-size: x-small;">"THIS NEEDS TO BE FIXED"<br />"THIS IS NOT THE BEST PRACTICE"<br />"THERE IS A BETTER APPROACH"</span><br />
<span class="Apple-style-span" style="font-size: x-small;">In one word, I know about it and I will fix it in later commits.</span><br />
<span class="Apple-style-span" style="font-size: xx-small;"><a href="http://www.blogger.com/blogger.g?blogID=646415056870061414#beginning">back to top</a></span>Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com12tag:blogger.com,1999:blog-646415056870061414.post-51468315104335857762011-09-20T15:20:00.000+03:002011-09-20T15:20:29.946+03:00HorizontalEscape Animation (Android Animation Factory)ok so this is the first update for the animationfactory project
I have created a very nice function called <b>outHorizontal! </b><br />
<br />
This function will basically work only for textview at least for now (i hope to be able to extend this to other views)<br />
anyway it will cause this nice animation to take place:<br />
<iframe allowfullscreen="" frameborder="0" height="349" src="http://www.youtube.com/embed/rERGZW3Fydo?hl=en&fs=1" width="425"></iframe>
<br />
<br />
<br />
I will post a tutorial as soon as I encapsulate this Animation and create a class for it probably tonight.<br />
<br />
Cee ya<br />
<br />
<span style="font-size: x-large;"><a href="http://www.mediafire.com/?q78igr6i1bvjy00">Click here for Sample Project + Source Code</a> (download) </span>not always as up-to-date as github<br />
<br />
<span style="font-size: x-large;"><a href="https://github.com/sre08/AnimationFactory">Click here for Sample Project + Source Code</a> (github)</span>Sherif elKhatibhttp://www.blogger.com/profile/11936538108217113720noreply@blogger.com4