---
layout: post
title: Build An Application Launcher For Android
mydate: Jul 5 2011
tags: [java]
comments: true
catagories: [mobile, tutorial, android]
description: In this tutorial we'll create an interesting android application which has the ability to launch any other app you have installed on your phone. If you've ever wanted to know how Android applications perform cross app communication you should read on(this tutorial assumes familiarity with the Android API).
---
To start off create a new Android application project within Eclipse. Whenever I develop Android apps I usually start with the layout of the application and add functionality via Java classes later. Since our app will display the users installed apps within a ListView widget we have to first declare a layout to hold the ListView itself.
Create a new xml layout for the ListView by right clicking the res/layout folder and adding a new android xml file. Title it "applauncher.xml" and then add the code below to it.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/applauncher_linearlayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<TextView
android:id="@android:id/empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="List PlaceHolder"/>
</LinearLayout>
When using ListViews we can setup the overall look of each row by creating an xml layout with a skeleton of the widgets that we want to display in each row of our ListView. Think of the row layout as a "row template". If you look at the screenshots of the app you can tell that we only need two widgets within our row layout, an ImageView to hold the apps icon and a TextView to display the apps package name.
Create a new xml layout for the ListView rows by right clicking the res/layout folder and adding a new android xml file. Title it "applauncherrow.xml" and add the code below.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/applauncherrow_imagelinearlayout"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<ImageView
android:id="@+id/applauncherrow_icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="6dip"/>
<LinearLayout
android:id="@+id/applauncherrow_namelinearlayout"
android:orientation="vertical"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="fill_parent">
<TextView
android:id="@+id/applauncherrow_appname"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:gravity="center_vertical"/>
</LinearLayout>
</LinearLayout>
Next we're going to setup the main activity for the launcher app and register the activity in the manifest.
Create a new Java Class and name it "AppLauncher.java".
Next open up the manifest file for your application and make sure it looks like the code below.
<!-- Your package="" name might differ -->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="org.example.hello"
android:versionCode="1"
android:versionName="1.0">
<!-- Your default android:icon="" name might differ -->
<application
android:icon="@drawable/icon"
android:label="AppLauncher"
android:debuggable="true"
android:description="An Application Launcher">
<activity
android:name="AppLauncher"
android:label="AppLauncher">
<!-- This tag indicates the initial activity -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="8" />
</manifest>
Ok so now that the layouts are coded up and the activity is registered within the manifest open up AppLauncher.java and lets begin.
Lets start off with importing all the necessary libraries needed for our app.
//Your package name might differ
package org.example.hello;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
Our app will extend the ListActivity class in order to reduce the boiler plate needed to build a ListView.
public class AppLauncher extends ListActivity{
However, due to the structure of the app we'll build the classes needed and stitch them together so just follow along with a text editor for now. To begin we'll need a class to hold all the useful data we can obtain from a given app. We'll call it AppInfo, its very basic so go ahead and type it into your editor.
public class AppInfo{
private String appName = "";
private String packageName = "";
private String className = "";
private String versionName = "";
private Integer versionCode = 0;
private Drawable icon = null;
public String getAppName(){
return appName;
}
public String getPackageName(){
return packageName;
}
public String getClassName(){
return className;
}
public String getVersionName(){
return versionName;
}
public Integer getVersionCode(){
return versionCode;
}
public Drawable getIcon(){
return icon;
}
}
The next class we need is a class that will be used to obtain the application info for the AppInfo class. This class is the bread and butter of our AppLauncher and it together with the AppInfo class can be ported to any application which needs to access other apps via intents.
public class Applications{
private ArrayList packageList = null;
private List activityList = null;
private Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
private PackageManager packMan = null;
For the Applications constructor we will pass in the all important PackageManager from the main context.
public Applications(PackageManager packManager){
packMan = packManager;
packageList = this.createPackageList(false);
activityList = this.createActivityList();
this.addClassNamesToPackageList();
}
Below are some getter methods for the package and activities list. The difference between the two is that the package list contains the info about the apps data and the activity list contains info about the intents the app responds to.
public ArrayList getPackageList(){
return packageList;
}
public List getActivityList(){
return activityList;
}
The next method is the createPackageList method. All it does is simply extract a list of all the installed apps via the PackageManager that we passed into the constructor. Next we iterate over the list and copy the info we need into a AppInfo object and then shove that object into the ArrayList<AppInfo> that will be returned.
private ArrayList createPackageList(boolean getSysPackages){
ArrayList pList = new ArrayList();
List packs = getPackageManager(
).getInstalledPackages(0);
for(int i = 0; i < packs.size(); i++){
PackageInfo packInfo = packs.get(i);
if((!getSysPackages) && (packInfo.versionName == null)){
continue ;
}
AppInfo newInfo = new AppInfo();
newInfo.appName = packInfo.applicationInfo.loadLabel(
getPackageManager()).toString();
newInfo.packageName = packInfo.packageName;
newInfo.versionName = packInfo.versionName;
newInfo.versionCode = packInfo.versionCode;
newInfo.icon = packInfo.applicationInfo.loadIcon(
getPackageManager());
pList.add(newInfo);
}
return pList;
}
Similar to the createPakageList method, createActivityList will return a list with all of the apps installed intent data but unlike createPackageList its only 8 lines of code.
private List createActivityList(){
List aList = packMan.queryIntentActivities(mainIntent, 0);
Collections.sort(aList,
new ResolveInfo.DisplayNameComparator(packMan));
return aList;
}
The next two methods are purely for dubugging purposes and can actually be left out but we'll include them for completeness or in case you use this class in another application.
private void packageDebug(){
if(null == packageList){
return;
}
for(int i = 0; i < packageList.size(); ++i){
Log.v("PACKINFO: ", "\t" +
packageList.get(i).appName + "\t" +
packageList.get(i).packageName + "\t" +
packageList.get(i).className + "\t" +
packageList.get(i).versionName + "\t" +
packageList.get(i).versionCode);
}
}
private void activityDebug(){
if(null == activityList){
return;
}
for(int i = 0; i < activityList.size(); i++){
ActivityInfo currentActivity = activityList.get(
i).activityInfo;
Log.v("ACTINFO",
"pName="
+ currentActivity.applicationInfo.packageName +
" cName=" + currentActivity.name);
}
}
The next method is needed only because in order to launch an app you need both its package name and its intent name that connects to the main class of the app. However, if you only use the packageList you'll lack the intent data, so the last method of the Application class adds the class name(intent data) to the packageList making it such that client of the class can have everything bundled up nicely in the packageList instead of having to rely on using both the activity and packageList in unison.
For the method addClassNameToPackageList we'll iterate over the packageList and for each packageName we'll find its match within the activityList by packageName and then extract the className from the activityList.
private void addClassNamesToPackageList(){
if(null == activityList || null == packageList){
return;
}
String tempName = "";
for(int i = 0; i < packageList.size(); ++i){
tempName = packageList.get(i).packageName;
for(int j = 0; j < activityList.size(); ++j){
if(tempName.equals(activityList.get(
j).activityInfo.applicationInfo.packageName)){
packageList.get(i).className = activityList.get(
j).activityInfo.name;
}
}
}
}
}
Below is the complete Application class
public class Applications{
private ArrayList packageList = null;
private List activityList = null;
private Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
private PackageManager packMan = null;
public Applications(PackageManager packManager){
packMan = packManager;
packageList = this.createPackageList(false);
activityList = this.createActivityList();
this.addClassNamesToPackageList();
}
public ArrayList getPackageList(){
return packageList;
}
public List getActivityList(){
return activityList;
}
private ArrayList createPackageList(boolean getSysPackages){
ArrayList pList = new ArrayList();
List packs = getPackageManager(
).getInstalledPackages(0);
for(int i = 0; i < packs.size(); i++){
PackageInfo packInfo = packs.get(i);
if((!getSysPackages) && (packInfo.versionName == null)){
continue ;
}
AppInfo newInfo = new AppInfo();
newInfo.appName = packInfo.applicationInfo.loadLabel(
getPackageManager()).toString();
newInfo.packageName = packInfo.packageName;
newInfo.versionName = packInfo.versionName;
newInfo.versionCode = packInfo.versionCode;
newInfo.icon = packInfo.applicationInfo.loadIcon(
getPackageManager());
pList.add(newInfo);
}
return pList;
}
private List createActivityList(){
List aList = packMan.queryIntentActivities(mainIntent, 0);
Collections.sort(aList,
new ResolveInfo.DisplayNameComparator(packMan));
return aList;
}
private void packageDebug(){
if(null == packageList){
return;
}
for(int i = 0; i < packageList.size(); ++i){
Log.v("PACKINFO: ", "\t" +
packageList.get(i).appName + "\t" +
packageList.get(i).packageName + "\t" +
packageList.get(i).className + "\t" +
packageList.get(i).versionName + "\t" +
packageList.get(i).versionCode);
}
}
private void activityDebug(){
if(null == activityList){
return;
}
for(int i = 0; i < activityList.size(); i++){
ActivityInfo currentActivity = activityList.get(
i).activityInfo;
Log.v("ACTINFO",
"pName="
+ currentActivity.applicationInfo.packageName +
" cName=" + currentActivity.name);
}
}
private void addClassNamesToPackageList(){
if(null == activityList || null == packageList){
return;
}
String tempName = "";
for(int i = 0; i < packageList.size(); ++i){
tempName = packageList.get(i).packageName;
for(int j = 0; j < activityList.size(); ++j){
if(tempName.equals(activityList.get(
j).activityInfo.applicationInfo.packageName)){
packageList.get(i).className = activityList.get(
j).activityInfo.name;
}
}
}
}
}
Remember the AppInfo class that we made and the applauncherrow.xml layout. Well, now we'll utilize both within the custom ArrayAdapter extension we'll use to populate the data of our List. ArrayAdapters are used to fill in the data fields of a ListView widget by storing a layout(android uses the term inflate) holding data into a single row of the list.
public class ApplicationAdapter extends ArrayAdapter{
private ArrayList items;
The constructor for our ArrayAdapter extension...
public ApplicationAdapter(Context context, int textViewResourceId, ArrayList items){
super(context, textViewResourceId, items);
this.items = items;
}
Being an ArrayAdapter extension means that we have to override the getView method, this is where the applauncherrow layout is populated with data and is used to represent each row of the ListView. To start we'll reference the applauncherrow layout and inflate it to become a view.
@Override
public View getView(int position, View convertView,
ViewGroup parent){
View view = convertView;
if(view == null){
LayoutInflater layout = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
view = layout.inflate(R.layout.applauncherrow, null);
}
Now that the view is inflated with the applauncherrow.xml layout we can now match the ImageView and TextView with the contents of a AppInfo object in preparation for moving it into the ListView.
AppInfo appInfo = items.get(position);
if(appInfo != null){
TextView appName = (TextView) view.findViewById(
R.id.applauncherrow_appname);
ImageView appIcon = (ImageView) view.findViewById(
R.id.applauncherrow_icon);
if(appName != null){
appName.setText(appInfo.getAppName());
}
if(appIcon != null){
appIcon.setImageDrawable(appInfo.getIcon());
}
}
return view;
}
}
Here is the complete ApplicationAdapter class, you should add it to your text file.
public class ApplicationAdapter extends ArrayAdapter{
private ArrayList items;
public ApplicationAdapter(Context context, int textViewResourceId,
ArrayList items){
super(context, textViewResourceId, items);
this.items = items;
}
@Override
public View getView(int position, View convertView,
ViewGroup parent){
View view = convertView;
if(view == null){
LayoutInflater layout = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
view = layout.inflate(R.layout.applauncherrow, null);
}
AppInfo appInfo = items.get(position);
if(appInfo != null){
TextView appName = (TextView) view.findViewById(
R.id.applauncherrow_appname);
ImageView appIcon = (ImageView) view.findViewById(
R.id.applauncherrow_icon);
if(appName != null){
appName.setText(appInfo.getAppName());
}
if(appIcon != null){
appIcon.setImageDrawable(appInfo.getIcon());
}
}
return view;
}
}
We are done with the utility classes so the next step is to add some methods to the AppLauncher class itself. The first one we're going to add is a method called getApps() which stores the pacakageList from an Applications object and passes it off to Java Runnable to be ran on a thread.
private void getApps(){
try{
myApps = new Applications(getPackageManager());
packageList = myApps.getPackageList();
}
catch(Exception exception){
Log.e("BACKGROUND PROC:", exception.getMessage());
}
this.runOnUiThread(returnRes);
}
If you look at the getApps() method the last line before the class ends is a method called this.runOnUiThread(returnRes), now what this means is that the main thread will be locked while we pump AppInfo objects into our ListView via the ArrayAdapter. If you are experienced with concurrent programming you might be wondering why lock the mainUIThread, the answer is because our app can't do anything until all objects have been added to the list. If we by chance were dynamically loading things from the Internet via HTTP requests then we would opt instead for a lazy loader which spawns a thread for each view object pumped into the ListVIew and the list would slowly populate as each item was fetched live from the web. So anyways here is our Runnable method that checks first that the packageList exists and then uses an object from our ApplicationAdapter to fill up our ListView widget.
private Runnable returnRes = new Runnable(){
public void run(){
if(packageList != null && packageList.size() > 0){
appAdapter.notifyDataSetChanged();
for(int i = 0; i < packageList.size(); ++i){
appAdapter.add(packageList.get(i));
}
}
progressDialog.dismiss();
appAdapter.notifyDataSetChanged();
}
};
We're almost done so now open up your Eclipse IDE to the AppLauncher project and click on the AppLauncher.java class. If you remember from earlier where we left off go ahead and add the data members below to the AppLauncher class; notice the line numbers for reference.
ApplicationAdapter appAdapter = null;
ProgressDialog progressDialog = null;
Runnable viewApps = null;
ArrayList packageList = null;
Applications myApps = null;
For the onCreate() method there are some things you sould notice. First since AppLauncher is a ListActivity extension it inherits the setListAdapter() method. Second we'll initialize the viewApps Runnable to call our getApps() within its run(). This means that any thread created with the viewApps as it's runnable will load up our packages into the ListView. In addition since getApps() locks the mainUIThread users of this app won't be able to do anything until the thread finishes.
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
this.setContentView(R.layout.applauncher);
packageList = new ArrayList();
appAdapter = new ApplicationAdapter(this, R.layout.applauncherrow,
packageList);
this.setListAdapter(appAdapter);
viewApps = new Runnable(){
public void run(){
getApps();
}
};
Thread appLoaderThread = new Thread(null, viewApps,
"AppLoaderThread");
appLoaderThread.start();
Now that the thread has started and the app has been officially locked, how do we notify the user? The answer is with the android.app.ProgressDialog widget. Remember those loading screen from popular video games, that's all ProgressDialogs do.
progressDialog = ProgressDialog.show(AppLauncher.this, "Hold on...", "Loading your apps...", true);
}
Right now our app can successfully load up a list of Icons and application names but how then do we launch the apps the user touches from our list? The answer is via starting a new intent by overriding the onListItemClick method. So go open up your text file that contains the utility classes and methods and add the onListItemClick() method to it.
@Override
protected void onListItemClick(ListView list, View view, int position,
long id){
super.onListItemClick(list, view, position, id);
AppInfo rowClicked = (AppInfo)this.getListAdapter().getItem(
position);
Intent startApp = new Intent();
ComponentName component = new ComponentName(
rowClicked.getPackageName(),
rowClicked.getClassName());
startApp.setComponent(component);
startApp.setAction(Intent.ACTION_MAIN);
startActivity(startApp);
}
Before you run the AppLauncher, be sure to copy and paste the classes and methods into the AppLauncher class so it resembles the complete file below.
package org.example.hello;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
public class AppLauncher extends ListActivity{
ApplicationAdapter appAdapter = null;
ProgressDialog progressDialog = null;
Runnable viewApps = null;
ArrayList packageList = null;
Applications myApps = null;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
this.setContentView(R.layout.applauncher);
packageList = new ArrayList();
appAdapter = new ApplicationAdapter(this, R.layout.applauncherrow,
packageList);
this.setListAdapter(appAdapter);
viewApps = new Runnable(){
public void run(){
getApps();
}
};
Thread appLoaderThread = new Thread(null, viewApps,
"AppLoaderThread");
appLoaderThread.start();
progressDialog = ProgressDialog.show(AppLauncher.this,
"Hold on...", "Loading your apps...", true);
}
public class Applications{
private ArrayList packageList = null;
private List activityList = null;
private Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
private PackageManager packMan = null;
public Applications(PackageManager packManager){
packMan = packManager;
packageList = this.createPackageList(false);
activityList = this.createActivityList();
this.addClassNamesToPackageList();
}
public ArrayList getPackageList(){
return packageList;
}
public List getActivityList(){
return activityList;
}
private ArrayList createPackageList(boolean getSysPackages){
ArrayList pList = new ArrayList();
List packs = getPackageManager(
).getInstalledPackages(0);
for(int i = 0; i < packs.size(); i++){
PackageInfo packInfo = packs.get(i);
if((!getSysPackages) && (packInfo.versionName == null)){
continue ;
}
AppInfo newInfo = new AppInfo();
newInfo.appName = packInfo.applicationInfo.loadLabel(
getPackageManager()).toString();
newInfo.packageName = packInfo.packageName;
newInfo.versionName = packInfo.versionName;
newInfo.versionCode = packInfo.versionCode;
newInfo.icon = packInfo.applicationInfo.loadIcon(
getPackageManager());
pList.add(newInfo);
}
return pList;
}
private List createActivityList(){
List aList = packMan.queryIntentActivities(mainIntent, 0);
Collections.sort(aList,
new ResolveInfo.DisplayNameComparator(packMan));
return aList;
}
private void packageDebug(){
if(null == packageList){
return;
}
for(int i = 0; i < packageList.size(); ++i){
Log.v("PACKINFO: ", "\t" +
packageList.get(i).appName + "\t" +
packageList.get(i).packageName + "\t" +
packageList.get(i).className + "\t" +
packageList.get(i).versionName + "\t" +
packageList.get(i).versionCode);
}
}
private void activityDebug(){
if(null == activityList){
return;
}
for(int i = 0; i < activityList.size(); i++){
ActivityInfo currentActivity = activityList.get(
i).activityInfo;
Log.v("ACTINFO",
"pName="
+ currentActivity.applicationInfo.packageName +
" cName=" + currentActivity.name);
}
}
private void addClassNamesToPackageList(){
if(null == activityList || null == packageList){
return;
}
String tempName = "";
for(int i = 0; i < packageList.size(); ++i){
tempName = packageList.get(i).packageName;
for(int j = 0; j < activityList.size(); ++j){
if(tempName.equals(activityList.get(
j).activityInfo.applicationInfo.packageName)){
packageList.get(i).className = activityList.get(
j).activityInfo.name;
}
}
}
}
}
public class AppInfo{
private String appName = "";
private String packageName = "";
private String className = "";
private String versionName = "";
private Integer versionCode = 0;
private Drawable icon = null;
public String getAppName(){
return appName;
}
public String getPackageName(){
return packageName;
}
public String getClassName(){
return className;
}
public String getVersionName(){
return versionName;
}
public Integer getVersionCode(){
return versionCode;
}
public Drawable getIcon(){
return icon;
}
}
public class ApplicationAdapter extends ArrayAdapter{
private ArrayList items;
public ApplicationAdapter(Context context, int textViewResourceId,
ArrayList items){
super(context, textViewResourceId, items);
this.items = items;
}
@Override
public View getView(int position, View convertView,
ViewGroup parent){
View view = convertView;
if(view == null){
LayoutInflater layout = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
view = layout.inflate(R.layout.applauncherrow, null);
}
AppInfo appInfo = items.get(position);
if(appInfo != null){
TextView appName = (TextView) view.findViewById(
R.id.applauncherrow_appname);
ImageView appIcon = (ImageView) view.findViewById(
R.id.applauncherrow_icon);
if(appName != null){
appName.setText(appInfo.getAppName());
}
if(appIcon != null){
appIcon.setImageDrawable(appInfo.getIcon());
}
}
return view;
}
}
private void getApps(){
try{
myApps = new Applications(getPackageManager());
packageList = myApps.getPackageList();
}
catch(Exception exception){
Log.e("BACKGROUND PROC:", exception.getMessage());
}
this.runOnUiThread(returnRes);
}
private Runnable returnRes = new Runnable(){
public void run(){
if(packageList != null && packageList.size() > 0){
appAdapter.notifyDataSetChanged();
for(int i = 0; i < packageList.size(); ++i){
appAdapter.add(packageList.get(i));
}
}
progressDialog.dismiss();
appAdapter.notifyDataSetChanged();
}
};
@Override
protected void onListItemClick(ListView list, View view, int position,
long id){
super.onListItemClick(list, view, position, id);
AppInfo rowClicked = (AppInfo)this.getListAdapter().getItem(
position);
Intent startApp = new Intent();
ComponentName component = new ComponentName(
rowClicked.getPackageName(),
rowClicked.getClassName());
startApp.setComponent(component);
startApp.setAction(Intent.ACTION_MAIN);
startActivity(startApp);
}
}
Enjoy your AppLauncher.


I had not seen this idea posted on here maybe on the official battlelog forums but there is a lot of spam there. I'd like to see a couple things for server browser history, the ability to sort time visited and number of times visited. The third thing which maybe more of an issue for ea/dice would be to be able to delete servers in the history either have it so you can clear all or maybe just a couple of servers. Great plugin though thanks for providing it for the community.
ORFK
Global Moderator
hello
i know that there are some problems with the server history in our profiles.
i really don't know wich server will be listed and wich not or in wich order.
it could be possible (by our addon) that you will get a own server history, for that battlelog should be activated all the time, and it could save a list with the servers. but you wouldn't be able to show this list to other, because that's require a online save of your history (same discussion like by avatars without gravatar)
don't know what brian will say to this idea. we will see =)
Topicstarter
Dr_LaVey
very cool wish I had logged in when I posted oh well thanks.
ORFK
Global Moderator
Dr_LaVey wrote:
very cool wish I had logged in when I posted oh well thanks.
*changed*
BrainFooLong
Administrator
Because this data isn't stored anywhere we cannot do that kind of sorts.
And also we cannot modify existing data more than battlelog already can.


Input masks can be used to force the user to enter data conform a specific format. Unlike validation, the user can't enter any other key than the ones specified by the mask. Developed by Arnold Daniels at Jansy Bootstrap.
Add data attributes to register an element with inputmask functionality as shown in the example above.
Call the input mask via javascript:
{% highlight js %} $('.inputmask').inputmask({ mask: '999-99-999-9999-9' }) {% endhighlight %}Options can be passed via data attributes or JavaScript. For data attributes, append the option name to data-
, as in data-mask="999-99-999-9999-9"
.
| Name | type | default | description |
|---|---|---|---|
| mask | string | '' | A string of formatting and literal characters, defining the input mask (see below). |
| placeholder | string | '_' | The character that is displayed where something needs to be typed. |
Each typed character needs to match exactly one character in the mask
option.
| Character | Description |
|---|---|
| 9 | Number |
| a | Letter |
| ? | Alphanumeric |
| * | Any character |
| ? | Optional - any characters following will become optional |
Initializes an input element with an input mask.


Structures
The following structures are available globally.
-
See moreCodingKey
for converting a decoding container to a dictionary where any key in the dictionary is accessible.Declaration
Swift
public struct AnyCodingKey : CodingKey
-
Wrapper for any codable array.
See moreDeclaration
Swift
public struct AnyCodableArray : Codable
-
Wrapper for any codable dictionary.
See moreDeclaration
Swift
public struct AnyCodableDictionary : Codable
-
RSDActiveUIStepCommand
is anOptionSet
for certain commonly used commands that are used at the beginning and end of an active step.See moreSeealso
RSDActiveUIStep
Declaration
Swift
public struct RSDActiveUIStepCommand : RSDStringLiteralOptionSet
-
See moreRSDAnswerResultObject
is a concrete implementation of a result that can be described using a single value.Declaration
Swift
public struct RSDAnswerResultObject : RSDAnswerResult, Codable
-
See moreRSDAnswerResultType
is aCodable
struct that can be used to describe how to encode and decode anRSDAnswerResult
. It carries information about the type of the value and how to encode it. This struct serves a different purpose from theRSDFormDataType
because it only carries information required to store a result and not additional information about presentation style.Declaration
Swift
public struct RSDAnswerResultType : Codable, Hashable, Equatable
-
The type of the async action configuration. This is used to decode async action configurations using an instance of
RSDFactory
.See moreSeealso
RSDAsyncActionConfiguration
Declaration
Swift
public struct RSDAsyncActionType : RSDFactoryTypeRepresentable, Codable, Hashable
-
See moreRSDChoiceObject
is a concrete implementation ofRSDChoice
that can be used to track a multiple choice, single choice, or multiple component input field where each choice in the input field maps to a specific value.Declaration
Swift
public struct RSDChoiceObject<T> : RSDChoice, RSDComparable, RSDEmbeddedIconVendor, Codable where T : Decodable, T : Encodable
-
A simple struct that can be used to implement the
See moreRSDChoiceOptions
protocol.Declaration
Swift
public struct RSDChoiceOptionsObject : RSDChoiceOptions
-
Concrete implementation of the
See moreRSDCohortNavigationRule
.Declaration
Swift
public struct RSDCohortNavigationRuleObject : RSDCohortNavigationRule, Codable
-
See moreRSDCollectionResultObject
is used include multiple results associated with a single step or async action that may have more that one result.Declaration
Swift
public struct RSDCollectionResultObject : RSDCollectionResult, RSDNavigationResult, Codable, RSDCopyWithIdentifier
-
RSDColorPlacementThemeElementObject
tells the UI what the background color and foreground color are for a given view as well as whether or not the foreground elements should uselight style
.The mapping is handled using a dictionary of color placements to the color style for that placement.
See moreDeclaration
Swift
public struct RSDColorPlacementThemeElementObject : RSDColorMappingThemeElement, RSDDecodableBundleInfo
-
RSDSingleColorThemeElementObject
tells the UI what the background color and foreground color are for a given view as well as whether or not the foreground elements should uselight style
.The mapping is handled using a single style for the entire view.
See moreDeclaration
Swift
public struct RSDSingleColorThemeElementObject : RSDColorMappingThemeElement, RSDDecodableBundleInfo
-
The type of the color mapping theme element. This is used to decode a
See moreRSDColorMappingThemeElement
using aRSDFactory
. It can also be used to customize the UI.Declaration
Swift
public struct RSDColorMappingThemeElementType : RSDFactoryTypeRepresentable, Codable, Equatable, Hashable
-
A color library is a versioned collection of color swatches.
See moreDeclaration
Swift
public struct RSDColorLibrary : Codable, Equatable, Hashable, Comparable
-
The color palette struct is used to describe the color palette for a given application. This is intended for applications that use the Sage Design System.
See moreDeclaration
Swift
public struct RSDColorPalette : Codable, Equatable, Hashable
-
The color key is a model object that contains the information needed by the color design system to describe the colors associated with a given set of components.
See moreDeclaration
Swift
public struct RSDColorKey : Codable, Equatable, Hashable, RSDColorMapping
-
A color swatch refers to a series of color tiles within the same family.
See moreDeclaration
Swift
public struct RSDColorSwatch : Codable, Equatable, Hashable, RSDColorFamily
-
A special-case color swatch for gray scale components.
See moreDeclaration
Swift
public struct RSDGrayScale : Codable, Equatable, Hashable, RSDColorFamily
-
A color tile is a single color and whether or not that color uses light style.
See moreDeclaration
Swift
public struct RSDColorTile : Codable, Equatable, Hashable
-
See moreRSDComparableSurveyRuleObject
is a survey rule that matches an expected result to the answer and vends a skip identifier if the match is evaluated totrue
.Declaration
Swift
public struct RSDComparableSurveyRuleObject<T> : RSDComparableSurveyRule, Codable where T : Decodable, T : Encodable
-
See moreRSDConditionalStepNavigatorObject
is a concrete implementation of theRSDConditionalStepNavigator
protocol.Declaration
Swift
public struct RSDConditionalStepNavigatorObject : RSDConditionalStepNavigator, RSDCopyStepNavigator, Decodable
-
RSDDateCoderObject
provides a concrete implementation of aRSDDateCoder
. The date coder is used by theRSDDateRangeObject
to encode and decode theminDate
andmaxDate
properties as well as to get which components of a date should be stored in the answer for a givenRSDInputField
.This coder uses ISO 8601 format to determine which calendar components to request from the user and to store from the input result. The
See morecalendar
is ISO8601 and theresultFormatter
is determined from thecalendarComponents
using the sharedRSDFactory
or the factory associated with the decoder if instantiated using aDecoder
. The locale for the date formatters isen_US_POSIX
by default.Declaration
Swift
public struct RSDDateCoderObject : RSDDateCoder, RawRepresentable
-
A simple struct that can be used to implement the
See moreRSDDatePickerDataSource
protocol.Declaration
Swift
public struct RSDDatePickerDataSourceObject : RSDDatePickerDataSource
-
See moreRSDDateRangeObject
is a concrete implementation of aRSDDateRange
that defines the range of values appropriate for adate
data type.Declaration
Swift
public struct RSDDateRangeObject : RSDDateRange, Codable
-
RSDDeviceType
describes various devices. It can be used by a task to vend different steps or async actions based upon what is supported by a given device type.See moreNote
This is not currently used and may be deprecated.Declaration
Swift
public struct RSDDeviceType : RSDFactoryTypeRepresentable, Codable, Hashable
-
The default configuration to use for a
RSDDistanceRecorder
.- example:
See more// Example json for a codable configuration. let json = """ { "identifier": "foo", "type": "distance", "motionStepIdentifier": "run" "startStepIdentifier": "countdown", "stopStepIdentifier": "rest", } """.data(using: .utf8)! // our data in native (JSON) format
Declaration
Swift
@available(iOS 10.0, *) public struct RSDDistanceRecorderConfiguration : RSDRecorderConfiguration, Codable
-
See moreRSDDurationPickerDataSourceObject
is a concrete implementation of aRSDMultipleComponentChoiceOptions
that can be used to select a duration using duration units for each component of the duration.Declaration
Swift
public struct RSDDurationPickerDataSourceObject : RSDMultipleComponentPickerDataSource
-
See moreRSDDurationRangeObject
extends the properties of anRSDInputField
for a.duration
data type.Declaration
Swift
public struct RSDDurationRangeObject : RSDDurationRange, RSDRangeWithFormatter, Codable
-
See moreRSDErrorResult
is a result that holds information about an error.Declaration
Swift
public struct RSDErrorResultObject : RSDErrorResult, Codable
-
A manifest for a given file that includes the filename, content type, and creation timestamp.
See moreDeclaration
Swift
public struct RSDFileManifest : Codable, Hashable, Equatable
-
See moreRSDFileResultObject
is a concrete implementation of a result that holds a pointer to a file url.Declaration
Swift
public struct RSDFileResultObject : RSDFileResult, Codable
-
The
See moreRSDFormUIHint
enum is a key word that can be used to describe the preferred UI for a form input field. This is intended as ahint
that the designers and developers can use to indicate the preferred input style for an input field. Not all ui hints are applicable to all data types or devices, and therefore the ui hint may be ignored by the application displaying the input field to the user.Declaration
Swift
public struct RSDFormUIHint : RawRepresentable, Codable, Hashable
-
RSDGenericStepObject
is a step with key/value pairs decoded from a dictionary. This is the default step returned byRSDFactory
for an unrecoginized type.This step is intended for use as a placeholder step for decoding a step that may be defined using a customized subtype or for replacing properties on an
See moreRSDMutableStep
.Declaration
Swift
public struct RSDGenericStepObject : RSDGenericStep, Decodable
-
The icon info is a simple image holder that also contains a title and subtitle for the image.
See moreDeclaration
Swift
public struct RSDIconInfo : Codable
-
See moreRSDIdentifier
is intended to allow a developer to define constants for the identifiers that are used to define the tasks, steps, input fields, and async actions associated with a given task or task group.Declaration
Swift
public struct RSDIdentifier : RawRepresentable, Codable, Hashable
-
A concrete implementation for a
See moreRSDImagePickerStep
.Declaration
Swift
public struct RSDImagePickerStepObject : RSDImagePickerStep, Codable
-
A hint as to where the UI should place an image.
See moreDeclaration
Swift
public struct RSDImagePlacementType : RawRepresentable, Codable, Hashable, Equatable
-
The type of the image theme. This is used to decode a
See moreRSDImageThemeElement
using aRSDFactory
. It can also be used to customize the UI.Declaration
Swift
public struct RSDImageThemeElementType : RSDFactoryTypeRepresentable, Codable, Equatable, Hashable
-
See moreRSDImageWrapper
vends an image. It does not handle image caching. If your app using a custom image caching, then you will need to use the shared delegate to implement this. The image wrapper is designed to allow coding of images using animageName
property as a key for accessing the image.Declaration
Swift
public struct RSDImageWrapper
-
See moreRSDFetchableImageThemeElementObject
is aCodable
concrete implementation ofRSDFetchableImageThemeElement
.Declaration
Swift
public struct RSDFetchableImageThemeElementObject : RSDFetchableImageThemeElement, RSDDecodableBundleInfo, Codable
-
See moreRSDAnimatedImageThemeElementObject
is aCodable
concrete implementation ofRSDAnimatedImageThemeElement
.Declaration
Swift
public struct RSDAnimatedImageThemeElementObject : RSDAnimatedImageThemeElement, RSDDecodableBundleInfo, Codable
-
The default configuration to use for a
RSDMotionRecorder
.- example:
See more// Example json for a codable configuration. let json = """ { "identifier": "foo", "type": "motion", "startStepIdentifier": "start", "stopStepIdentifier": "stop", "requiresBackgroundAudio": true, "recorderTypes": ["accelerometer", "gyro", "magnetometer"], "frequency": 50 } """.data(using: .utf8)! // our data in native (JSON) format
Declaration
Swift
@available(iOS 10.0, *) public struct RSDMotionRecorderConfiguration : RSDRestartableRecorderConfiguration, Codable
-
A simple struct that can be used to implement the
See moreRSDMultipleComponentOptions
protocol.Declaration
Swift
public struct RSDMultipleComponentOptionsObject : RSDMultipleComponentOptions
-
A simple struct that can be used to implement the
See moreRSDNumberPickerDataSource
protocol.Declaration
Swift
public struct RSDNumberPickerDataSourceObject : RSDNumberPickerDataSource
-
See moreRSDNumberRangeObject
extends the properties of anRSDInputField
for adecimal
orinteger
data type.Declaration
Swift
public struct RSDNumberRangeObject : RSDNumberRange, RSDRangeWithFormatter, Codable
-
The
See moreRSDRegExValidatorObject
is a concrete implementation of theRSDCodableRegExMatchValidator
that can be used to create a regex validation of an input string using a regex pattern.Declaration
Swift
public struct RSDRegExValidatorObject : RSDCodableRegExMatchValidator
-
See moreRSDResourceType
is an extendable struct for describing the type of a resource. By default, these values will map to the file extension.Declaration
Swift
public struct RSDResourceType : RawRepresentable, Equatable, Hashable, Codable
-
See moreRSDResultObject
is a concrete implementation of the base result associated with a task, step, or asynchronous action.Declaration
Swift
public struct RSDResultObject : RSDNavigationResult, Codable
-
See moreRSDResultType
is an extendable string enum used byRSDFactory
to create the appropriate result type.Declaration
Swift
public struct RSDResultType : RSDFactoryTypeRepresentable, Codable, Hashable
-
See moreRSDRecordMarker
is a concrete implementation ofRSDSampleRecord
that can be used to mark the step transitions for a recording.Declaration
Swift
public struct RSDRecordMarker : RSDSampleRecord
-
Implementation of the
See moreRSDStringSeparatedEncodingFormat
protocol that wraps a comma separated encodable.Declaration
Swift
public struct CSVEncodingFormat<K> : RSDStringSeparatedEncodingFormat where K : RSDDelimiterSeparatedEncodable
-
See moreRSDSchemaInfoObject
is a concrete implementation of theRSDSchemaInfo
protocol.Declaration
Swift
public struct RSDSchemaInfoObject : RSDSchemaInfo, Codable, Hashable
-
Default implementation for building a task scoring.
See moreDeclaration
Swift
public struct RSDDefaultScoreBuilder : RSDScoreBuilder
-
See moreRSDSectionStepObject
is used to define a logical subgrouping of steps such as a section in a longer survey or an active step that includes an instruction step, countdown step, and activity step.Declaration
Swift
public struct RSDSectionStepObject : RSDSectionStep, RSDConditionalStepNavigator, RSDStepValidator, RSDCopyStep, Decodable
-
See moreRSDStandardAsyncActionConfiguration
is a concrete implementation ofRSDRecorderConfiguration
that can be used to decode an async configuration for a recorder.Declaration
Swift
public struct RSDStandardAsyncActionConfiguration : RSDRecorderConfiguration, Codable
-
The type of the step. This is used to decode the step using a
See moreRSDFactory
. It can also be used to customize the UI.Declaration
Swift
public struct RSDStepNavigatorType : RSDFactoryTypeRepresentable, Codable, Hashable
-
See moreRSDStepTransformerObject
is used in decoding a step with replacement properties for some or all of the steps in a section that is defined using a different resource. The factory will convert this step into an appropriateRSDSectionStep
from the decoded object.Declaration
Swift
public struct RSDStepTransformerObject : RSDStepTransformer, Decodable
-
The type of the step. This is used to decode the step using a
See moreRSDFactory
. It can also be used to customize the UI.Declaration
Swift
public struct RSDStepType : RSDFactoryTypeRepresentable, Codable, Hashable
-
See moreRSDTaskGroupObject
is a concrete implementation of theRSDTaskGroup
protocol.Declaration
Swift
public struct RSDTaskGroupObject : RSDTaskGroup, RSDEmbeddedIconVendor, Decodable
-
See moreRSDTaskInfoStepObject
is a concrete implementation of theRSDTaskInfoStep
protocol.Declaration
Swift
public struct RSDTaskInfoStepObject : RSDTaskInfoStep
-
The metadata for a task result archive that can be zipped using the app developer’s choice of third-party archival tools.
See moreDeclaration
Swift
public struct RSDTaskMetadata : Codable
-
See moreRSDTaskResultObject
is a result associated with a task. This object includes a step history, task run UUID, schema identifier, and asynchronous results.Declaration
Swift
public struct RSDTaskResultObject : RSDTaskRunResult, Codable
-
See moreRSDTextFieldOptionsObject
defines the options for a text field.Declaration
Swift
public struct RSDTextFieldOptionsObject : RSDTextFieldOptions, Codable
-
See moreRSDUIActionObject
is a concrete implementation ofRSDUIAction
that can be used to customize the title and image displayed for a given action of the UI.Declaration
Swift
public struct RSDUIActionObject : RSDEmbeddedResourceUIAction, Codable
-
See moreRSDNavigationUIActionObject
implements an action for navigating to another step in a task.Declaration
Swift
public struct RSDNavigationUIActionObject : RSDEmbeddedResourceUIAction, RSDNavigationUIAction, Codable
-
See moreRSDReminderUIActionObject
implements an action for setting up a local notification to remind the participant about doing a particular task later.Declaration
Swift
public struct RSDReminderUIActionObject : RSDEmbeddedResourceUIAction, RSDReminderUIAction, Codable
-
See moreRSDWebViewUIActionObject
implements an action that includes a pointer to a url that can display in a webview. The url can either be fully qualified or optionally point to an embedded resource.Declaration
Swift
public struct RSDWebViewUIActionObject : RSDEmbeddedResourceUIAction, RSDWebViewUIAction, Codable
-
See moreRSDVideoViewUIActionObject
implements an action that includes a pointer to a url that can display in aAVPlayerViewController
. The url can either be fully qualified or optionally point to an embedded resource.Declaration
Swift
public struct RSDVideoViewUIActionObject : RSDEmbeddedResourceUIAction, RSDVideoViewUIAction, Codable
-
The type of the ui action. This is used to decode a
See moreRSDUIAction
using aRSDFactory
. It can also be used to customize the UI.Declaration
Swift
public struct RSDUIActionObjectType : RSDFactoryTypeRepresentable, Codable, Hashable
-
The type of transition style to use for displaying a UI component.
See moreDeclaration
Swift
public struct RSDTransitionStyle : RawRepresentable, Codable, Hashable
-
See moreRSDUSHeightPickerDataSourceObject
is a custom height picker for use when theLocale
uses US Customary units (not metric system).Declaration
Swift
public struct RSDUSHeightPickerDataSourceObject : RSDUSMeasurementPickerDataSource
-
See moreRSDUSInfantMassPickerDataSource
is a custom weight picker for use when theLocale
uses US Customary units (not metric system) and the mass is for an infant inlb, oz
.Declaration
Swift
public struct RSDUSInfantMassPickerDataSourceObject : RSDUSMeasurementPickerDataSource
-
See moreRSDUnitConverter
is a static struct for defined unit converters for converting between US English (imperial) to metric units. This is used to allow for displaying a person’s height or weight in imperial units, while storing the value in standardized metric units.Declaration
Swift
public struct RSDUnitConverter
-
See moreRSDViewThemeElementObject
tells the UI where to find the view controller to use when instantiating theRSDStepController
.Declaration
Swift
public struct RSDViewThemeElementObject : RSDViewThemeElement, RSDDecodableBundleInfo, Codable
-
A weekly schedule item is a light-weight codable struct that can be used to store and track events that happen at regularily scheduled intervals. This schedule assumes a ISO8601 7-day calendar.
- example:
Codable
protocol schema.let json = """ { "daysOfWeek": [1, 3, 5], "timeOfDay": "08:00" } """.data(using: .utf8)! // our data in native (JSON) format
Declaration
Swift
public struct RSDWeeklyScheduleObject : Codable, RSDSchedule
- example:


Menu
Home
About
Our Rooms
Reservation
Blog
Contact
Menu
Connect With Us
Reservation Form
First Name
Last Name
Email
Room
Select a Room
Classic Room
Ultra Superior Room
Grand Deluxe Room
Arrival
Departure
Adults
Number of Adults
1
2
3
4+
Children
Number of Children
1
2
3
4+


{{a.a}}
Title
Drag me
{{drag.x + ", " + drag.y}}
This is a title
This is a subtitle
This is a title
This is a title
This is a title
This is a title
This is a title
//Default template name 'untitled template'


Indian doctors remove giant 7.4kg kidney from man
NEW DELHI: Indian surgeons have cut a kidney weighing the same as a bowling ball out of a man with a life-threatening genetic condition, a surgeon said on Monday (Nov 25).
The 7.4kg kidney is one of the biggest ever removed in an operation.
"It was a huge lump that was occupying half of his abdomen. We knew it was a big kidney but never thought it would be this heavy," said Sachin Kathuria, a member of the surgical team, told AFP.
The 56-year-old patient, suffering from autosomal dominant polycystic kidney disease, underwent the two-hour operation at the Sir Ganga Ram Hospital in Delhi last month.
A normal kidney weighs about 120g to 150g and is 12cm long.
Kathuria said the kidney cut out from the man was nearly 45cm long.
The Guinness Book of Records says the largest kidney removed from a human was 4.25kg in an operation in Dubai in 2017.
Doctors at the New Delhi hospital said however they had found medical accounts of a kidney weighing 9kg being cut from a patient.
The disease the unnamed man was suffering from causes cysts full of fluids to start growing in kidneys. Doctors called for the operation when they detected internal bleeding and spreading infections.
Kathuria said the man was now in good condition and on dialysis awaiting a kidney transplant.


Details regarding synchronous and synchronous response support
Both synchronous and asynchronous responses utilize the same ActionResponse model, but display a different status.
Synchronous repsonses are the preferred option in every case; always try to return synchronous responses when your system supports them. Synchronous repsonses must always have a ResponseStatus that is not 0 / AsyncResponsePending.
- {
- "ResponseBody": { },
- "Status": "Complete",
- "PendingUri": null,
- "Errors": [ ]
- }
It is possible that your system may not be able to perform some operations synchronously. For example, creating hundreds of product listings may take a few minutes to process, longer to process than is reasonable to wait for an HTTP response. In these cases, your API will return a null ResponseBody in its ActionResponse and a fully-qualified URL for Voyageone to poll.
- {
- "ResponseBody": null,
- "Status": "AsyncResponsePending",
- "PendingUri": "http://api.your-url.com/v1/pending-responses?workItemID=123456",
- "Errors": [ ]
- }
Voyageone will poll that URL every few seconds until it returns a valid response with a finalized ResponseStatus (not 0 / AsyncResponsePending).
- {
- "ResponseBody": { },
- "Status": "Complete",
- "PendingUri": null,
- "Errors": [ ]
- }
Because the Voyageone integration currently utilizes a synchronous feed process that waits for asynchronous responses to complete, no other work can continue for a feed while the feed waits for the polling URL to return a final status. For this reason, all asynchronous repsonses must complete within the configured asynchronous response timeout window (5 minutes by default). If your API is unable to respond within the allowed time window, the integration will give up, error the request internally, and move on to other work. This timeout will be treated as a system error. Currently, the only exception to this rule is the Create and update products endpoint when the Long-running batch workflow is being used.
If you find that a 5 minute timeout is insufficient for your system, please email your technical contact.


Build Your Own
Enter a path to image:
Initial Number of Worms:
Maximum Number of Worms:
Background Color:
Worm Color: Check for Random Colors:
or specify a Worm color (in HEX): Red:
Green:
Blue:
Your drawing will appear here.


Software production is rarely fully controled. We usually produce code that we rewrite later, figuring out iteratively what works and what doesn't. As a result, Git commits can be quite messy if we create them chronologically. A single change can be distributed over several commits, and a single commit can contain several changes. At the end, we often reach a state where the commits history doesn't help us understanding what was done.
This issue is very common: it is often the difference between a beginner and an experienced versionning user. Others have shared their own perspectives on this topic:
We won't dive deep in the reasons why you may be interested in cleaning your branch. We can summarize with generalities: just choose between investing time doing the right thing now, or save time now at the price of a higher cost later. As a non-professional, think about your learning priorities. As a professional, think about the consequences on:
If you are still here, you think that cleaning your Git branch makes some sense. So let's dig in.
A Git branch comes with a history of commits. Moreover, each commit comes with a content (the committed code) and a message (the text of the commit). Cleaning a branch requires to take care of each of these aspects. In other words, we want to know here:
All three points are related, in the sense that they all participate in the "story" told by the commits history. We will address each of them separately.
The commit message is the first thing we see when looking at a commit. However, it is not always fully exploited. This comes with a reason: we do not always provide useful information there. Did you never read commit messages like "Correction", "Fix a problem", "Add a new function", or "Remove old stuff"? Well, surely the commit provides some changes, and add or remove something, hopefully for the best. But we can guess this far already, so what is this commit for?
Even with more specific details, a recurrent issue is that the message focuses too much on what the commit does. Like "Add an uppercase option to the formatting method", or "Create a JSON formatter in the formatter package". But we can guess that much again by looking at the content of the commit. We can see that a parameter named "upperCase" has been added to a function. We can see that a JsonFormatter class has been added with the formatter package. We can see that a piece of code have been removed. But telling what has been done does not tell why we needed that in the first place. It might seem obvious when the change is done, otherwise we wouldn't do it in the first place. But after a few months, or after you have moved to another mission and your successor come back to your change, how does it help?
When you write a commit message, think about a complete stranger who will have to maintain your code. This person needs to know what was the purpose of the change, usually what was going wrong before changing it. For the information to be complete, one needs to know:
The point 2 is addressed with the content of the commit, not its message. You can think about it like the difference between the code of a function and a commentary that tells what this function does. Surely, if the function does something, it is first of all based on its code, not based on what the commentary says. While a commentary can be complementary, the code remains the one deciding about what is actually done. Similarly, you may provide additional information in the commit message, but rely first on the commit content to tell what has been done. In other words, make the changes themselves clear enough for a future reader.
The point 1 is the most important to address in the commit message. It motivates the change and help the future reader to evaluate it on the spot. Think about it: if this change was done because of issue A, and a recent update offers a better solution to issue A, it might be relevant to come back to this change. At the opposite, if this change was for issue B, then the new solution should not be applied before we ensure that we do not regress on issue B. Saying that the change was for issue A or issue B changes the interpretation of the changes without having to dig in the code and speculate.
Finally, once we know what needed to be changed (message) and how we changed it (content), then we can assume that the need has been fulfilled. If not, or not entirely, or if particular points of attention need to be highlighted, then the commit message can be enriched to tackle point 3. It will participate in setting up the context for future changes that may do better. Some people might consider that this information should not be in the commit, but in the code. Indeed, documenting the code is important and we read more often the documentation than the commit history. But others will also say that this information is in the ticket associated to the change. All these people, as well as those thinking it has its place in the commit, are right: it depends on the context. At the end, the point is that the information should be easily available from where we look at. Write the information where it is the most relevant, and link to it elsewhere.
As a reminder, a commit message is composed of a title (first line) and optional details (remaining lines). Ensure that the title provides the minimal information to understand the purpose of the commit at a glance. Use the details as you see fit to ensure that a reader can access all the additional information easily.
The content of the commit tells what has changed. In order to understand that part, you should first understand the purpose of the commit. If you followed until here, this is the responsibility of the commit message. But you can properly redact this message only if the commit content fits the bill.
The main objective when creating a clean commit is atomicity:
Indeed, if you spread the changes among several commits, it becomes harder to understand a commit without knowing the others. Similarly, if you mix several purposes into a single commit, it becomes harder to understand which change relates to which purpose. By making a one-to-one relation between a purpose and a commit, you focus the attention on a single purpose at a time while ensuring it contains all the relevant information. Here are some examples of changes that are prone to relate to the same purpose and be found in the same commit:
You don't need to wait having all the relevant changes before to create a commit. While developing, it is often a good practie to commit your work in progress and push it on some remote to avoid losing it. But once you have finished, you can go through your history of commits to clean it. This is when you have to care about your commits atomicity. And the various ways to achieve that are explained in the next section.
The commits history tells, to some extents, how the changes fit together. You can tell a "nice story" by organising your commits in a way that facilitates the understanding of the evolution you made. Several things can be done for that, and each are detailed in specific articles:
Feel free to combine them as you see fit, depending on your needs.
Don't forget however that you are rewriting the history!
If it is only your local stuff, that is OK.
If you rewrite stuff already shared, be sure to get the go from your colleagues.
Once done, you will have to push your changes with git push --force-with-lease
(or git push --force
if your Git is old).
If you feel unsafe touching the commits history, take the relevant
Once you master these operations, you can combine them to do more complex things. Here are some examples:
These procedures can be combined and greatly simplified with git rebase -i
.
Mastering rebasing and the rebase command can save a lot of time to rewrite your history.
For the ones who still struggle with Git, I strongly recommend to go iteratively.
Clarify the first issue you want to tackle and select the procedure to apply.
Check that the new state of your branch corresponds to what you expect, then pass to the next issue.
If the branch is so messed up that you don't even know where to start from, prefer to
You may think that all these commits moving around means a lot of conflicts to resolve. However, if you properly design and organise your code, you can use independent pieces thate won't conflict with each other when moved. If you happen to have a lot of conflcits, you may think again about what you are doing.


Session and other user activity events
By having Sense forward events to Butler when a user (for example) starts a session, Butler can then forward this event to other tools and systems that need to take action when users start or stop using Sense.
The features described on this page use the same concept as email notifications on reload failures, i.e. UDP messages sent from Sense to Butler when certain log events occur.
A slightly silly - but fun - use of these events is to blink a light when when users open apps, log into Sense etc.
Here is a blog post describing this, with a video showing what it looks like.
Green light, someone opens a Sense app. Red light, someone closes a Sense app.
Session start/stop, connection open/close
Remember
The text below assumes the log appenders have been set up for user activity notifications.
Make extra sure the log appender XML file contains the correct IP/port info specified in Butler’s UDP server config settings.
The user events log appender that’s included with Butler (GitHub link) captures the following events:
- Session start: When a user logs into Sense and does not have an already active Sense session open in some other browser tab or window.
- Session end: When a user has not been active for a while and the session timeout is reached, or when the user logs out.
- Connection open: When a user opens a new app or does a browser refresh of a Sense app.
- Connection close: When a user does a browser refresh of a Sense app or closes a browser window or tab in which a Sense app was open.
The response time is usually quite good, typically Butler gets the notifiction from Sense within a few seconds of the actual event taking place.
The information sent to Butler is
- Server name where the log message originated.
- What command is associated with the message (session start/stop, connection open/close).
- What Sense user directory the associated user belongs to.
- The username of the associated user.
Butler will then use this information and take either of several possible actions depending on which event was received and settings in Butler’s config file.
Sample Slack and Teams messages
Messages sent to Slack and Teams can look like this:
Enabling Slack, Teams and MQTT messages
The message destinations can be indidually enabled or disabled.
- To enable Slack messages,
Butler.slackNotification.enable
andButler.slackNotification.userSessionEvents.enable
should both betrue
. - To enable Teams messages,
Butler.teamsNotification.enable
andButler.teamsNotification.userSessionEvents.enable
should both betrue
. - To enable MQTT messages,
Butler.mqttConfig.enable
should betrue
.
For Slack and Teams, all messages received from the log appender will be forwarded to the respective destination (assuming the destination is enabled). This means you can modify the appender XML files so that Butler received more/fewer/other messages.
The MQTT message is a bit different as it looks at the “command” field in the UDP message and then publish a MQTT message on topic that depends on which command was received:
- “Session start” events will be published to the topic defined in the
Butler.mqttConfig.sessionStartTopic
config setting. - “Session stop” events will be published to the topic defined in the
Butler.mqttConfig.sessionStopTopic
config setting. - “Connection open” events will be published to the topic defined in the
Butler.mqttConfig.connectionOpenTopic
config setting. - “Connection close” events will be published to the topic defined in the
Butler.mqttConfig.connectionCloseTopic
config setting.
References
-
Qlik’s documenation around log appenders and how to hook into the Sense logs is somewhat brief, but does provide a starting point if you want to dive deeper into this topic.
-
The main log4net documentation (log4net is the logging framework used by Qlik Sense Enterprise) can also be useful.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.


Post Code Finder
Enter any address to find the postcode.
Find Postcode
Success!
Could not a postcode for that address. Please try again.
Could not connect to server


|
Panda3D
|
Public Member Functions | |
| def | __init__ (self) |
| def | clear (self) |
| def | getDelta (self) |
| def | getFrameNetworkTime (self, bits=16, ticksPerSec=NetworkTimePrecision) |
| def | getLastResync (self) |
| def | getRealNetworkTime (self, bits=16, ticksPerSec=NetworkTimePrecision) |
| Convenience functions ###. More... | |
| def | getUncertainty (self) |
| def | localElapsedTime (self, networkTime, bits=16, ticksPerSec=NetworkTimePrecision) |
| def | localToNetworkTime (self, localTime, bits=16, ticksPerSec=NetworkTimePrecision) |
| def | networkToLocalTime (self, networkTime, now=None, bits=16, ticksPerSec=NetworkTimePrecision) |
| Primary interface functions ###. More... | |
| def | newDelta (self, localTime, newDelta, newUncertainty, trustNew=1) |
| def | peerToPeerResync (self, avId, timestamp, serverTime, uncertainty) |
| def | resynchronize (self, localTime, networkTime, newUncertainty, trustNew=1) |
Public Attributes | |
| delta | |
| globalClock | |
| lastResync | |
| uncertainty | |
Static Public Attributes | |
| notify = DirectNotifyGlobal.directNotify.newCategory('ClockDelta') | |
The ClockDelta object converts between universal ("network") time, which is used for all network traffic, and local time (e.g. as returned by getFrameTime() or getRealTime()), which is used for everything else.
| def __init__ | ( | self | ) |
| def clear | ( | self | ) |
Throws away any previous synchronization information.
| def getDelta | ( | self | ) |
| def getFrameNetworkTime | ( | self, | |
bits = 16 , |
|||
ticksPerSec = NetworkTimePrecision |
|||
| ) |
Returns the current getFrameTime() expressed as a network time.
| def getLastResync | ( | self | ) |
| def getRealNetworkTime | ( | self, | |
bits = 16 , |
|||
ticksPerSec = NetworkTimePrecision |
|||
| ) |
Convenience functions ###.
Returns the current getRealTime() expressed as a network time.
| def getUncertainty | ( | self | ) |
| def localElapsedTime | ( | self, | |
| networkTime, | |||
bits = 16 , |
|||
ticksPerSec = NetworkTimePrecision |
|||
| ) |
localElapsedTime(self, int networkTime) Returns the amount of time elapsed (in seconds) on the client since the server message was sent. Negative values are clamped to zero.
| def localToNetworkTime | ( | self, | |
| localTime, | |||
bits = 16 , |
|||
ticksPerSec = NetworkTimePrecision |
|||
| ) |
localToNetworkTime(self, float localTime) Converts the indicated localTime to the corresponding networkTime value.
| def networkToLocalTime | ( | self, | |
| networkTime, | |||
now = None , |
|||
bits = 16 , |
|||
ticksPerSec = NetworkTimePrecision |
|||
| ) |
Primary interface functions ###.
networkToLocalTime(self, int networkTime) Converts the indicated networkTime to the corresponding localTime value. The time is assumed to be within +/- 5 minutes of the current local time given in now, or getRealTime() if now is not specified.
| def newDelta | ( | self, | |
| localTime, | |||
| newDelta, | |||
| newUncertainty, | |||
trustNew = 1 |
|||
| ) |
Accepts a new delta and uncertainty pair, understood to represent time as of localTime. Improves our current notion of the time delta accordingly. The return value is true if the new measurement was used, false if it was discarded.
| def peerToPeerResync | ( | self, | |
| avId, | |||
| timestamp, | |||
| serverTime, | |||
| uncertainty | |||
| ) |
Accepts an AI time and uncertainty value from another client, along with a local timestamp value of the message from this client which prompted the other client to send us its delta information. The return value is true if the other client's measurement was reasonably close to our own, or false if the other client's time estimate was wildly divergent from our own; the return value is negative if the test was not even considered (because it happened too soon after another recent request).
| def resynchronize | ( | self, | |
| localTime, | |||
| networkTime, | |||
| newUncertainty, | |||
trustNew = 1 |
|||
| ) |
resynchronize(self, float localTime, int32 networkTime, float newUncertainty) Accepts a new networkTime value, which is understood to represent the same moment as localTime, plus or minus uncertainty seconds. Improves our current notion of the time delta accordingly.
| delta |
| globalClock |
| lastResync |
|
static |
| uncertainty |


OBS Login
Host
Password
Small Player Settings
Number of Small Players
0 Players
1 Player
2 Players
3 Players
4 Players
5 Players
6 Players


menu
Amazing Dental Hospital
Kimathi Dental Services
Mercy Dentistry
Dominion Dental Hoapital
Smile Dental
Hope Dentistry
Blake Dental Care
Davis Dental Care
Smile Dental
myDentist.com
1 / 3
Amazing Dental Hospital
2 / 3
Mercy Dental Care
3 / 3
Kimathi Dental Services
❮
❯


|
News
[Mar 2023] 1 paper was accepted by CVPR 2023.
[Jul 2022] 1 paper was accepted by ECCV 2022.
[Jul 2021] Join SenseTime as a fulltime researcher working on generative models and neural rendering. If you are interested in a research internship position, please contact us.
[Oct 2020] 1 paper on face swapping was accepted by NeurIPS 2020.
[Mar 2020] 1 paper on talking face was accepted by IJCAI 2020.
[Sep 2018] Start my graduate study and research at Anhui University and CRIPAC CASIA.
|
|
Selected Publications [full list]
I'm interested in machine learning, computer vision, and neural rendering. Much of my research is about high-quality generative models.
|
|
|
CelebV-Text: A large-scale facial text-video datase
Jianhui Yu*, Hao Zhu*, Liming Jiang, Chen Change Loy, Weidong Cai, Wayne Wu
CVPR, 2023
Project Page
/
Dataset
/
arXiv
A rich and high relavance text and facial video dataset containing 70,000 video clips.
|
|
|
CelebV-HQ: A Large-Scale Video Facial Attributes Dataset
Hao Zhu*,
Wayne Wu✉*,
Wentao Zhu,
Liming Jiang,
Siwei Tang,
Li Zhang,
Ziwei Liu, and
Chen Change Loy
ECCV, 2022
Project Page
/
Dataset
/
arXiv
A large-scale, high-quality, and diverse video dataset with rich facial attribute annotations. CelebV-HQ contains 35,666 video clips with the resolution of 512x512 at least. All clips are labeled manually with 83 facial attributes, covering appearance, action, and emotion.
|
|
|
Deep audio-visual learning: A survey
Hao Zhu,
Mandi Luo,
Rui Wang,
Aihua Zheng, and
Ran He
IJAC, 2021
PDF
we provide a comprehensive survey of recent audio-visual learning development.
|
|
|
AOT: Appearance Optimal Transport Based Identity Swapping for Forgery Detection
Hao Zhu*
Chaoyou Fu*,
Qianyi Wu,
Wayne Wu,
Chen Qian, and
Ran He
NeurIPS, 2020
project page
/
Code & Dataset
/
arXiv
A new identity swapping algorithm with large differences in appearance for forgery detection.
|
|
|
Arbitrary Talking Face Generation via Attentional Audio-visual Coherence Learning
Hao Zhu,
Huaibo Huang,
Yi Li,
Aihua Zheng, and
Ran He
IJCAI, 2020
arXiv
A novel arbitrary talking face generation framework by discovering the audio-visual coherence via the proposed Asymmetric Mutual Information Estimator (AMIE).
|
|
Awards
ACF Outstanding Master's Thesis. 2021
Outstanding Master's Graduate, Anhui Province, China. 2021
National Scholarship, AHU. 2020.
|
|


Lookups tables allow Cadet to automatically bulk update your
annotations to save time. This method eliminates the need to re-enter information every time that it
appears in your corpus. Keep in mind that simple lookups do not account for context or semantic variation.
The table cannot predict if "Chicago" is a place, a theater, or a musical. If you add "Chicago" as a place
lookup, all matching tokens in your corpus will be marked as "place." You will have the opportunity to
adjust these values during annotation, but you'll need to strike a balance between saving time with bulk
updates and losing time by having to change incorrect annotations.
If you have existing dictionaries or other linguistic data, please create separate files for lemmata, part of speech
and entities. You can then upload them here as json or CSV files. If you don't have this data, just go to the next step.
If you have data for entities (or similar data), you can add this data as a gazetteer in INCEpTION.


{%extends 'mgmt/index.html'%} {% load static%} {% block content %} {% load widget_tweaks %}
Search through a list of created-orders!
| Description | P# | Part-Type | Ordered by | Reserved for | Qty | Ordered On: |
|---|---|---|---|---|---|---|
| {{instance.description}} | {{instance.part_number}} | {{instance.part_type}} | {{instance.ordered_by}} | {{instance.tail_number}} | {{instance.order_quantity}} | {{instance.date_ordered|date:"M d, Y"}} |


Welcome to the NDPP User’s Guide! This tutorial will guide you through the essential aspects of using NDPP to convert continuous-energy data in to a form which can be readily tallied in a Monte Carlo neutron transport code with a significantly enhanced Figure-of-Merit.


Rasa-Host
Dashboard
NLU
md files
Core
stories files
domain file
chat
conversations
logs
analyze
memoization policy
Other
all logs
{% block content %}{% endblock %}
{% block scripts %}{% endblock %}