text:
![viper logo](https://cloud.githubusercontent.com/assets/173412/10886745/998df88a-8151-11e5-9448-4736db51020d.png)

Go configuration with fangs!

Many Go projects are built using Viper including:

* [Hugo](http://gohugo.io)
* [EMC RexRay](http://rexray.readthedocs.org/en/stable/)
* [Imgur's Incus](https://github.com/Imgur/incus)
* [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack)
* [Docker Notary](https://github.com/docker/Notary)
* [BloomApi](https://www.bloomapi.com/)
* [doctl](https://github.com/digitalocean/doctl)
* [Clairctl](https://github.com/jgsqware/clairctl)

[![Build Status](https://travis-ci.org/spf13/viper.svg)](https://travis-ci.org/spf13/viper) [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![GoDoc](https://godoc.org/github.com/spf13/viper?status.svg)](https://godoc.org/github.com/spf13/viper)

## What is Viper?

Viper is a complete configuration solution for Go applications including 12-Factor apps. It is designed
to work within an application, and can handle all types of configuration needs
and formats. It supports:

* setting defaults
* reading from JSON, TOML, YAML, HCL, and Java properties config files
* live watching and re-reading of config files (optional)
* reading from environment variables
* reading from remote config systems (etcd or Consul), and watching changes
* reading from command line flags
* reading from buffer
* setting explicit values

Viper can be thought of as a registry for all of your applications
configuration needs.

## Why Viper?

When building a modern application, you don't want to worry about
configuration file formats; you want to focus on building awesome software.
Viper is here to help with that.

Viper does the following for you:

1. Find, load, and unmarshal a configuration file in JSON, TOML, YAML, HCL, or Java properties formats.
2. Provide a mechanism to set default values for your different
   configuration options.
3. Provide a mechanism to set override values for options specified through
   command line flags.
4. Provide an alias system to easily rename parameters without breaking existing
   code.
5. Make it easy to tell the difference between when a user has provided a
   command line or config file which is the same as the default.

Viper uses the following precedence order. Each item takes precedence over the
item below it:

 * explicit call to Set
 * flag
 * env
 * config
 * key/value store
 * default

Viper configuration keys are case insensitive.

## Putting Values into Viper

### Establishing Defaults

A good configuration system will support default values. A default value is not
required for a key, but it's useful in the event that a key hasn't been set via
config file, environment variable, remote configuration or flag.

Examples:

```go
viper.SetDefault("ContentDir", "content")
viper.SetDefault("LayoutDir", "layouts")
viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
```

### Reading Config Files

Viper requires minimal configuration so it knows where to look for config files.
Viper supports JSON, TOML, YAML, HCL, and Java Properties files. Viper can search multiple paths, but
currently a single Viper instance only supports a single configuration file.
Viper does not default to any configuration search paths leaving defaults decision
to an application.

Here is an example of how to use Viper to search for and read a configuration file.
None of the specific paths are required, but at least one path should be provided
where a configuration file is expected.

```go
viper.SetConfigName("config") // name of config file (without extension)
viper.AddConfigPath("/etc/appname/")   // path to look for the config file in
viper.AddConfigPath("$HOME/.appname")  // call multiple times to add many search paths
viper.AddConfigPath(".")               // optionally look for config in the working directory
err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
	panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
```

### Watching and re-reading config files

Viper supports the ability to have your application live read a config file while running.

Gone are the days of needing to restart a server to have a config take effect,
viper powered applications can read an update to a config file while running and
not miss a beat.

Simply tell the viper instance to watchConfig.
Optionally you can provide a function for Viper to run each time a change occurs.

**Make sure you add all of the configPaths prior to calling `WatchConfig()`**

```go
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
	fmt.Println("Config file changed:", e.Name)
})
```

### Reading Config from io.Reader

Viper predefines many configuration sources such as files, environment
variables, flags, and remote K/V store, but you are not bound to them. You can
also implement your own required configuration source and feed it to viper.

```go
viper.SetConfigType("yaml") // or viper.SetConfigType("YAML")

// any approach to require this configuration into your program.
var yamlExample = []byte(`
Hacker: true
name: steve
hobbies:
- skateboarding
- snowboarding
- go
clothing:
  jacket: leather
  trousers: denim
age: 35
eyes : brown
beard: true
`)

viper.ReadConfig(bytes.NewBuffer(yamlExample))

viper.Get("name") // this would be "steve"
```

### Setting Overrides

These could be from a command line flag, or from your own application logic.

```go
viper.Set("Verbose", true)
viper.Set("LogFile", LogFile)
```

### Registering and Using Aliases

Aliases permit a single value to be referenced by multiple keys

```go
viper.RegisterAlias("loud", "Verbose")

viper.Set("verbose", true) // same result as next line
viper.Set("loud", true)   // same result as prior line

viper.GetBool("loud") // true
viper.GetBool("verbose") // true
```

### Working with Environment Variables

Viper has full support for environment variables. This enables 12 factor
applications out of the box. There are four methods that exist to aid working
with ENV:

 * `AutomaticEnv()`
 * `BindEnv(string...) : error`
 * `SetEnvPrefix(string)`
 * `SetEnvKeyReplacer(string...) *strings.Replacer`

_When working with ENV variables, it's important to recognize that Viper
treats ENV variables as case sensitive._

Viper provides a mechanism to try to ensure that ENV variables are unique. By
using `SetEnvPrefix`, you can tell Viper to use a prefix while reading from
the environment variables. Both `BindEnv` and `AutomaticEnv` will use this
prefix.

`BindEnv` takes one or two parameters. The first parameter is the key name, the
second is the name of the environment variable. The name of the environment
variable is case sensitive. If the ENV variable name is not provided, then
Viper will automatically assume that the key name matches the ENV variable name,
but the ENV variable is IN ALL CAPS. When you explicitly provide the ENV
variable name, it **does not** automatically add the prefix.

One important thing to recognize when working with ENV variables is that the
value will be read each time it is accessed. Viper does not fix the value when
the `BindEnv` is called.

`AutomaticEnv` is a powerful helper especially when combined with
`SetEnvPrefix`. When called, Viper will check for an environment variable any
time a `viper.Get` request is made. It will apply the following rules. It will
check for a environment variable with a name matching the key uppercased and
prefixed with the `EnvPrefix` if set.

`SetEnvKeyReplacer` allows you to use a `strings.Replacer` object to rewrite Env
keys to an extent. This is useful if you want to use `-` or something in your
`Get()` calls, but want your environmental variables to use `_` delimiters. An
example of using it can be found in `viper_test.go`.

#### Env example

```go
SetEnvPrefix("spf") // will be uppercased automatically
BindEnv("id")

os.Setenv("SPF_ID", "13") // typically done outside of the app

id := Get("id") // 13
```

### Working with Flags

Viper has the ability to bind to flags. Specifically, Viper supports `Pflags`
as used in the [Cobra](https://github.com/spf13/cobra) library.

Like `BindEnv`, the value is not set when the binding method is called, but when
it is accessed. This means you can bind as early as you want, even in an
`init()` function.

For individual flags, the `BindPFlag()` method provides this functionality.

Example:

```go
serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
```

You can also bind an existing set of pflags (pflag.FlagSet):

Example:

```go
pflag.Int("flagname", 1234, "help message for flagname")

pflag.Parse()
viper.BindPFlags(pflag.CommandLine)

i := viper.GetInt("flagname") // retrieve values from viper instead of pflag
```

The use of [pflag](https://github.com/spf13/pflag/) in Viper does not preclude
the use of other packages that use the [flag](https://golang.org/pkg/flag/)
package from the standard library. The pflag package can handle the flags
defined for the flag package by importing these flags. This is accomplished
by a calling a convenience function provided by the pflag package called
AddGoFlagSet().

Example:

```go
package main

import (
	"flag"
	"github.com/spf13/pflag"
)

func main() {

	// using standard library "flag" package
	flag.Int("flagname", 1234, "help message for flagname")

	pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
	pflag.Parse()
	viper.BindPFlags(pflag.CommandLine)

	i := viper.GetInt("flagname") // retrieve value from viper

	...
}
```

#### Flag interfaces

Viper provides two Go interfaces to bind other flag systems if you don't use `Pflags`.

`FlagValue` represents a single flag. This is a very simple example on how to implement this interface:

```go
type myFlag struct {}
func (f myFlag) HasChanged() bool { return false }
func (f myFlag) Name() string { return "my-flag-name" }
func (f myFlag) ValueString() string { return "my-flag-value" }
func (f myFlag) ValueType() string { return "string" }
```

Once your flag implements this interface, you can simply tell Viper to bind it:

```go
viper.BindFlagValue("my-flag-name", myFlag{})
```

`FlagValueSet` represents a group of flags. This is a very simple example on how to implement this interface:

```go
type myFlagSet struct {
	flags []myFlag
}

func (f myFlagSet) VisitAll(fn func(FlagValue)) {
	for _, flag := range flags {
		fn(flag)
	}
}
```

Once your flag set implements this interface, you can simply tell Viper to bind it:

```go
fSet := myFlagSet{
	flags: []myFlag{myFlag{}, myFlag{}},
}
viper.BindFlagValues("my-flags", fSet)
```

### Remote Key/Value Store Support

To enable remote support in Viper, do a blank import of the `viper/remote`
package:

`import _ "github.com/spf13/viper/remote"`

Viper will read a config string (as JSON, TOML, YAML or HCL) retrieved from a path
in a Key/Value store such as etcd or Consul.  These values take precedence over
default values, but are overridden by configuration values retrieved from disk,
flags, or environment variables.

Viper uses [crypt](https://github.com/xordataexchange/crypt) to retrieve
configuration from the K/V store, which means that you can store your
configuration values encrypted and have them automatically decrypted if you have
the correct gpg keyring.  Encryption is optional.

You can use remote configuration in conjunction with local configuration, or
independently of it.

`crypt` has a command-line helper that you can use to put configurations in your
K/V store. `crypt` defaults to etcd on http://127.0.0.1:4001.

```bash
$ go get github.com/xordataexchange/crypt/bin/crypt
$ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json
```

Confirm that your value was set:

```bash
$ crypt get -plaintext /config/hugo.json
```

See the `crypt` documentation for examples of how to set encrypted values, or
how to use Consul.

### Remote Key/Value Store Example - Unencrypted

#### etcd
```go
viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json")
viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop"
err := viper.ReadRemoteConfig()
```

#### Consul
You need to set a key to Consul key/value storage with JSON value containing your desired config.
For example, create a Consul key/value store key `MY_CONSUL_KEY` with value:

```json
{
    "port": 8080,
    "hostname": "myhostname.com"
}
```

```go
viper.AddRemoteProvider("consul", "localhost:8500", "MY_CONSUL_KEY")
viper.SetConfigType("json") // Need to explicitly set this to json
err := viper.ReadRemoteConfig()

fmt.Println(viper.Get("port")) // 8080
fmt.Println(viper.Get("hostname")) // myhostname.com
```

### Remote Key/Value Store Example - Encrypted

```go
viper.AddSecureRemoteProvider("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg")
viper.SetConfigType("json") // because there is no file extension in a stream of bytes,  supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop"
err := viper.ReadRemoteConfig()
```

### Watching Changes in etcd - Unencrypted

```go
// alternatively, you can create a new viper instance.
var runtime_viper = viper.New()

runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml")
runtime_viper.SetConfigType("yaml") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop"

// read from remote config the first time.
err := runtime_viper.ReadRemoteConfig()

// unmarshal config
runtime_viper.Unmarshal(&runtime_conf)

// open a goroutine to watch remote changes forever
go func(){
	for {
	    time.Sleep(time.Second * 5) // delay after each request

	    // currently, only tested with etcd support
	    err := runtime_viper.WatchRemoteConfig()
	    if err != nil {
	        log.Errorf("unable to read remote config: %v", err)
	        continue
	    }

	    // unmarshal new config into our runtime config struct. you can also use channel
	    // to implement a signal to notify the system of the changes
	    runtime_viper.Unmarshal(&runtime_conf)
	}
}()
```

## Getting Values From Viper

In Viper, there are a few ways to get a value depending on the value's type.
The following functions and methods exist:

 * `Get(key string) : interface{}`
 * `GetBool(key string) : bool`
 * `GetFloat64(key string) : float64`
 * `GetInt(key string) : int`
 * `GetString(key string) : string`
 * `GetStringMap(key string) : map[string]interface{}`
 * `GetStringMapString(key string) : map[string]string`
 * `GetStringSlice(key string) : []string`
 * `GetTime(key string) : time.Time`
 * `GetDuration(key string) : time.Duration`
 * `IsSet(key string) : bool`
 * `AllSettings() : map[string]interface{}`

One important thing to recognize is that each Get function will return a zero
value if it's not found. To check if a given key exists, the `IsSet()` method
has been provided.

Example:
```go
viper.GetString("logfile") // case-insensitive Setting & Getting
if viper.GetBool("verbose") {
    fmt.Println("verbose enabled")
}
```
### Accessing nested keys

The accessor methods also accept formatted paths to deeply nested keys. For
example, if the following JSON file is loaded:

```json
{
    "host": {
        "address": "localhost",
        "port": 5799
    },
    "datastore": {
        "metric": {
            "host": "127.0.0.1",
            "port": 3099
        },
        "warehouse": {
            "host": "198.0.0.1",
            "port": 2112
        }
    }
}

```

Viper can access a nested field by passing a `.` delimited path of keys:

```go
GetString("datastore.metric.host") // (returns "127.0.0.1")
```

This obeys the precedence rules established above; the search for the path
will cascade through the remaining configuration registries until found.

For example, given this configuration file, both `datastore.metric.host` and
`datastore.metric.port` are already defined (and may be overridden). If in addition
`datastore.metric.protocol` was defined in the defaults, Viper would also find it.

However, if `datastore.metric` was overridden (by a flag, an environment variable,
the `Set()` method, …) with an immediate value, then all sub-keys of
`datastore.metric` become undefined, they are "shadowed" by the higher-priority
configuration level.

Lastly, if there exists a key that matches the delimited key path, its value
will be returned instead. E.g.

```json
{
    "datastore.metric.host": "0.0.0.0",
    "host": {
        "address": "localhost",
        "port": 5799
    },
    "datastore": {
        "metric": {
            "host": "127.0.0.1",
            "port": 3099
        },
        "warehouse": {
            "host": "198.0.0.1",
            "port": 2112
        }
    }
}

GetString("datastore.metric.host") // returns "0.0.0.0"
```

### Extract sub-tree

Extract sub-tree from Viper.

For example, `viper` represents:

```json
app:
  cache1:
    max-items: 100
    item-size: 64
  cache2:
    max-items: 200
    item-size: 80
```

After executing:

```go
subv := viper.Sub("app.cache1")
```

`subv` represents:

```json
max-items: 100
item-size: 64
```

Suppose we have:

```go
func NewCache(cfg *Viper) *Cache {...}
```

which creates a cache based on config information formatted as `subv`.
Now it's easy to create these 2 caches separately as:

```go
cfg1 := viper.Sub("app.cache1")
cache1 := NewCache(cfg1)

cfg2 := viper.Sub("app.cache2")
cache2 := NewCache(cfg2)
```

### Unmarshaling

You also have the option of Unmarshaling all or a specific value to a struct, map,
etc.

There are two methods to do this:

 * `Unmarshal(rawVal interface{}) : error`
 * `UnmarshalKey(key string, rawVal interface{}) : error`

Example:

```go
type config struct {
	Port int
	Name string
	PathMap string `mapstructure:"path_map"`
}

var C config

err := Unmarshal(&C)
if err != nil {
	t.Fatalf("unable to decode into struct, %v", err)
}
```

### Marshalling to string

You may need to marhsal all the settings held in viper into a string rather than write them to a file.
You can use your favorite format's marshaller with the config returned by `AllSettings()`.

```go
import (
    yaml "gopkg.in/yaml.v2"
    // ...
)

func yamlStringSettings() string {
    c := viper.AllSettings()
	bs, err := yaml.Marshal(c)
	if err != nil {
        t.Fatalf("unable to marshal config to YAML: %v", err)
    }
	return string(bs)
}
```

## Viper or Vipers?

Viper comes ready to use out of the box. There is no configuration or
initialization needed to begin using Viper. Since most applications will want
to use a single central repository for their configuration, the viper package
provides this. It is similar to a singleton.

In all of the examples above, they demonstrate using viper in its singleton
style approach.

### Working with multiple vipers

You can also create many different vipers for use in your application. Each will
have its own unique set of configurations and values. Each can read from a
different config file, key value store, etc. All of the functions that viper
package supports are mirrored as methods on a viper.

Example:

```go
x := viper.New()
y := viper.New()

x.SetDefault("ContentDir", "content")
y.SetDefault("ContentDir", "foobar")

//...
```

When working with multiple vipers, it is up to the user to keep track of the
different vipers.

## Q & A

Q: Why not INI files?

A: Ini files are pretty awful. There's no standard format, and they are hard to
validate. Viper is designed to work with JSON, TOML or YAML files. If someone
really wants to add this feature, I'd be happy to merge it. It's easy to specify
which formats your application will permit.

Q: Why is it called "Viper"?

A: Viper is designed to be a [companion](http://en.wikipedia.org/wiki/Viper_(G.I._Joe))
to [Cobra](https://github.com/spf13/cobra). While both can operate completely
independently, together they make a powerful pair to handle much of your
application foundation needs.

Q: Why is it called "Cobra"?

A: Is there a better name for a [commander](http://en.wikipedia.org/wiki/Cobra_Commander)?


text:
package com.example.user.nimingapp.activity;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.example.user.nimingapp.Config;
import com.example.user.nimingapp.R;
import com.example.user.nimingapp.customView.MyTitle;
import com.example.user.nimingapp.net.PubComment;
import com.example.user.nimingapp.net.Publish;

public class PublishActivity extends Activity {
    private MyTitle myTitle;
    private EditText etPublish;
    private Button btnPublish;
    private String phone_md5;
    private String token;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_publish);
        Intent intent=getIntent();
        phone_md5=intent.getStringExtra(Config.KEY_PHONE_MD5);
        token=intent.getStringExtra(Config.KEY_TOKEN);
        myTitle= (MyTitle) findViewById(R.id.title_publish);
        etPublish= (EditText) findViewById(R.id.et_publish);
        findViewById(R.id.btn_publidh).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String etpublish=etPublish.getText().toString();
                if(TextUtils.isEmpty(etPublish.getText())){
                    Toast.makeText(PublishActivity.this,"消息不能为空",Toast.LENGTH_SHORT).show();
                    return;
                }
                if (etpublish.contains("%")||etpublish.contains("&")){
                    Toast.makeText(PublishActivity.this,"消息中含非法字符,请重新输入",Toast.LENGTH_LONG).show();
                    etPublish.setText("");
                    return;
                }
                final ProgressDialog pd = ProgressDialog.show(PublishActivity.this,"消息发布","正在提交中...");
                new Publish(phone_md5, token, etPublish.getText().toString(), new Publish.SuccessCallback() {
                    @Override
                    public void onSuccess() {
                        pd.dismiss();
                        Toast.makeText(PublishActivity.this,"发布消息成功!",Toast.LENGTH_LONG).show();
                        etPublish.setText("");

                    }
                }, new Publish.FailCallback() {
                    @Override
                    public void onFail(int errerCode) {
                        pd.dismiss();
                        switch (errerCode){
                            case Config.RESULT_SELF_PROGRAM_FAIL:
                                Toast.makeText(PublishActivity.this,"发布消息失败,请检查网络", Toast.LENGTH_LONG).show();
                                break;
                            case Config.RESULT_STATUS_FAIL:
                                Toast.makeText(PublishActivity.this,"发布消息失败,服务器拒绝", Toast.LENGTH_LONG).show();
                                break;
                            case Config.RESULT_STATUS_INVALID_TOKEN:
                                AlertDialog.Builder builder = new AlertDialog.Builder(PublishActivity.this);
                                 builder.setMessage("发布消息失败,账号过期,请重新登入");
                                builder.setTitle("提示");
                              builder.setPositiveButton("是", new DialogInterface.OnClickListener() {
                               @Override
                                public void onClick(DialogInterface dialog, int which) {
                                 dialog.dismiss();
                                   startActivity(new Intent(PublishActivity.this, LoginActivity.class));
                                     PublishActivity.this.finish();
                                  }
                               });
                             builder.setNegativeButton("否", new DialogInterface.OnClickListener() {
                                @Override
                                 public void onClick(DialogInterface dialog, int which) {
                                    dialog.dismiss();
                                   }
                               });
                                builder.create().show();

                            default:
                                break;
                        }
                    }
                });
            }
        });

        myTitle.setOnTitleClickListener(new MyTitle.OnTitleClickListener() {
            @Override
            public void onBtnLeft() {
                Intent i=new Intent(PublishActivity.this,TimeLineActivity.class);
                i.putExtra(Config.KEY_TOKEN,Config.getCachedToken(PublishActivity.this));
                i.putExtra(Config.KEY_PHONE_NUM,Config.getCachedPhoneNum(PublishActivity.this));
                startActivity(i);
                PublishActivity.this.finish();
            }
            @Override
            public void onBtnRight() {
            }
        });

    }
}

redpajama_set_name: RedPajamaGithub


text:
{% extends "withnav_template.html" %}
{% from "components/page-header.html" import page_header %}
{% from "govuk_frontend_jinja/components/back-link/macro.html" import govukBackLink %}

{% block service_page_title %}
  Choose where to send this alert
{% endblock %}

{% block backLink %}
  {{ govukBackLink({ "href": url_for(".preview_broadcast_areas", service_id=current_service.id, broadcast_message_id=broadcast_message.id) }) }}
{% endblock %}

{% block maincolumn_content %}

  {{ page_header("Choose where to send this alert") }}

  {% for area in broadcast_message.ancestor_areas %}
    {{ area.name }}
    {% if loop.last %}

    {% endif %}
  {% endfor %}

  {% for library in libraries|sort %}
    {{ library.name }}

{{ library.get_examples() }}

  {% endfor %}

{% endblock %}


text:
[Story Map Journal](http://storymaps.arcgis.com/en/app-list/map-journal/) is ideal when you want to combine narrative text with maps and other embedded content.

The typical Map Journal creation involve using the integrated WYSIWYG builder. This application let you generate a Map Journal through a simple JSON document, without using that builder.

The resulting application is hosted in ArcGIS Online, you can edit it later as if you had used the builder.

[Live demo](https://rawgit.com/glazou/story-map-journal-generator/master/index.html) |
[Map Journal GitHub repository](https://github.com/Esri/map-journal-storytelling-template-js) |
[Map Journal page on Esri Story Maps website](http://storymaps.arcgis.com/en/app-list/map-journal/)

## Instructions

The usage scenarios are:
* as a node.js application with ArcGIS Online credentials defined in the configuration file
* as a web application with authentication through ArcGIS Online OAuth dialog
* as a web application with authentication through ArcGIS Online credentials defined provided in the configuration file

### As a Node.js application

1. Install [Node.js](http://nodejs.org/)
2. Download the application
3. Run `npm install` in the application root
4. Edit app/app.js with your credentials
5. Optionally, if you are going to use the `webmap-x-y` type, edit app/app.js with a webmap ID
6. Run `node index`

### As a web application with OAuth authentication

1. Download the application
2. Deploy on a web server
4. Edit app/app.js with an ArcGIS Online OAuth App ID, see [adding apps for ArcGIS Online for Organizations users](http://doc.arcgis.com/en/arcgis-online/share-maps/add-items.htm#ESRI_SECTION1_0D1B620254F745AE84F394289F8AF44B) or [registering your application for ArcGIS for Developers users](https://developers.arcgis.com/authentication/signing-in-arcgis-online-users/#registering-your-application)
5. Optionally, if you are going to use the `webmap-x-y` type, edit app/app.js with a webmap ID
6. Access the application over the web server

### As a web application with ArcGIS Online credentials

1. Install [Node.js](http://nodejs.org/)
2. Download the application
3. Run `npm install` in the application root
4. Edit app/app.js with your credentials
5. Optionally, if you are going to use the `webmap-x-y` type, edit app/app.js with a webmap ID
6. Access the application over the web server

**Make sure to not deploy the application on a public server to avoid exposing your credentials.**

## JSON configuration

The JSON defining the Map Journal to be generated can be edited through `data/map-journal-sample.json`.

The general JSON structure is:
```
{
	title: 'Journal title',
	sections: [
		{
			// First
		},
		{
			// Second section
		},
		// ... limited to 99 sections ...
	]
}
```

With a section being defined by a title a `Main Stage` (larger area) and `Panel` (smaller scollable area intended for the narrative).

```
{
	"title": "",
	"mainstage": {
		// Image, video, webpage or map configure goes here
	},
	"panel": {
		// Text, can include images, videos, webpage and map
	}
}
```

See some examples below.

**Section with an image in the Main Stage**

```
{
	"title": "<p>First section</p>",
	"mainstage": {
		"type": "image",
		"url": "http://farm8.static.flickr.com/7373/13429795365_f57595be16_b.jpg"
	},
	"panel": {
		"text": "<p>Image in main stage</p>"
	}
}
```

**Section with a web map in the Main Stage**
 ```
{
	"title": "<p>Second section</p>",
	"mainstage": {
		"type": "webmap",
		"id": "ea8e4e1f70014f3a8919dc3d8c08a29e"
	},
	"panel": {
		"text": "<p>A Map of <strong>world volcanoes</strong>.</p><p> </p><p> </p><p>Open the Map in <a href=\"http://www.arcgis.com/home/webmap/viewer.html?webmap=2a45f767a6e549bfbe5bf638681f1fac\" target=\"_blank\">ArcGIS Online Map Viewer</a>.<p> </p><p> </p></p>"
	}
}
```

**Section with a web page in the Main Stage and an image in the panel**
 ```
{
	"title": "<p>Third section</p>",
	"mainstage": {
		"type": "webpage",
		"url": "http://story.maps.arcgis.com/home/webscene/viewer.html?webscene=fd3b56984801434aa71f75b1926bde5b&ui=min"
	},
	"panel": {
		"text": "<p>Embed of ArcGIS Online scene viewer, <a href=\"http://story.maps.arcgis.com/home/webscene/viewer.html?webscene=fd3b56984801434aa71f75b1926bde5b\" target=\"_blank\">link to the scene</a>.</p><p><div class=\"image-container activate-fullscreen\"><img src=\"http://upload.wikimedia.org/wikipedia/commons/thumb/3/37/Fimmvorduhals_2010_03_27_dawn.jpg/1280px-Fimmvorduhals_2010_03_27_dawn.jpg\" /></div></p>"
	}
}
```

**Section with a map in the Main Stage and in the panel configured as an x/y**
 ```
{
	"title": "<p>Fourth <strong style=\"color:red\">section</strong></p>",
	"mainstage": {
		"type": "map-x-y",
		"x": -116.538019,
		"y": 33.825898,
		"mapScale": "block",
		"mapSymbolUrl": "http://static.arcgis.com/images/Symbols/Shapes/PurplePin2LargeB.png",
		"popupContents": "Hello Palm Springs!"
	},
	"panel": {
		"text": "<p>Map in the main stage <strong>+ map in the panel</strong></p>",
		"map": {
			"x": -116.538019,
			"y": 33.825898,
			"mapScale": "continent"
		}
	}
}
```


text:
using namespace std;

class Client {

public:
	Client();
	virtual ~Client();

	bool create();
	bool connect_fd(string, unsigned int const);
	bool send_fd(const string);

private:
	int sockfd;
};

#endif


text:
#include <string>
#include <stdint.h>
#include <DRAMSim.h>

class some_object
{
	public:
		void read_complete(unsigned, uint64_t, uint64_t);
		void write_complete(unsigned, uint64_t, uint64_t);
		int add_one_and_run(DRAMSim::MultiChannelMemorySystem *mem, uint64_t addr);
};


text:
LOREM IPSUM LOREM IPSUM LOREM IPSUM

LOREM IPSUM LOREM IPSUM LOREM IPSUM

LOREM IPSUM LOREM IPSUM LOREM IPSUM

LOREM IPSUM LOREM IPSUM LOREM IPSUM

LOREM IPSUM LOREM IPSUM LOREM IPSUM


text:
package com.navercorp.pinpoint.common.profiler.concurrent;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author emeroad
 */
public final class ExecutorFactory {

    private static final ThreadFactory DEFAULT_THREAD_FACTORY = new PinpointThreadFactory("Pinpoint-defaultThreadFactory", true);

    private ExecutorFactory() {
    }

    public static ThreadPoolExecutor newFixedThreadPool(int nThreads, int workQueueMaxSize, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(workQueueMaxSize), threadFactory);
    }

    public static ThreadPoolExecutor newFixedThreadPool(int nThreads, int workQueueMaxSize) {
        return newFixedThreadPool(nThreads, workQueueMaxSize, DEFAULT_THREAD_FACTORY);
    }

    public static ThreadPoolExecutor newFixedThreadPool(int nThreads, int workQueueMaxSize, String threadFactoryName, boolean daemon) {
        ThreadFactory threadFactory = new PinpointThreadFactory(threadFactoryName, daemon);
        return newFixedThreadPool(nThreads, workQueueMaxSize, threadFactory);
    }

}


text:
package org.apache.taverna.mobile.fragments.workflowdetails;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.DownloadManager;
import android.app.LoaderManager;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Loader;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.util.Base64;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import com.dropbox.client2.DropboxAPI;
import com.dropbox.client2.ProgressListener;
import com.dropbox.client2.android.AndroidAuthSession;
import com.dropbox.client2.exception.DropboxException;
import com.dropbox.client2.session.AppKeyPair;

import org.apache.taverna.mobile.R;
import org.apache.taverna.mobile.activities.DashboardMainActivity;
import org.apache.taverna.mobile.adapters.WorkflowAdapter;
import org.apache.taverna.mobile.tavernamobile.TavernaPlayerAPI;
import org.apache.taverna.mobile.tavernamobile.User;
import org.apache.taverna.mobile.tavernamobile.Workflow;
import org.apache.taverna.mobile.utils.DetailsLoader;
import org.apache.taverna.mobile.utils.RunTask;
import org.apache.taverna.mobile.utils.WorkflowDownloadManager;
import org.apache.taverna.mobile.utils.Workflow_DB;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

/**
 * Created by Larry Akah on 6/9/15.
 */
public class WorkflowdetailFragment extends Fragment implements View.OnClickListener,LoaderManager.LoaderCallbacks<Workflow>{
    /**
     * The fragment argument representing the section number for this
     * fragment.
     */
    private static final String ARG_SECTION_NUMBER = "section_number";
    private DownloadManager downloadManager;
    static View rootView;
    private static ProgressDialog progressDialog;
    public AlertDialog runDialog;
    public AlertDialog.Builder alertDialogBuilder;
    private static String download_url;
    public static String WORKFLO_ID = "";
    public static Context cont;
    SharedPreferences sharedPreferences;
    private static boolean LOAD_STATE = false;
    private static boolean DROPUPLOAD = false;
    static Animation zoomin;
    static Animation zoomout;
    public boolean isZoomIn;
    public static String workflow_uri ;
    final static private String BOX_APP_KEY = "doicbvkfyzligh2";
    final static private String BOX_APP_SECRET = "3uuuw36mm7jkflc";
    static Workflow currentWorkflow =  null;
    private long wid;

    private DropboxAPI<AndroidAuthSession> mDBApi;

    /**
     * Returns a new instance of this fragment for the given section
     * number.
     */
    public static WorkflowdetailFragment newInstance(int sectionNumber) {
        WorkflowdetailFragment fragment = new WorkflowdetailFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        fragment.setArguments(args);
        return fragment;
    }

    public static WorkflowdetailFragment getInstance(){
        return new WorkflowdetailFragment();
    }

    public WorkflowdetailFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {

        AppKeyPair appKeys = new AppKeyPair(BOX_APP_KEY, BOX_APP_SECRET);
        AndroidAuthSession session = new AndroidAuthSession(appKeys);
        mDBApi = new DropboxAPI<AndroidAuthSession>(session);
    //    long workflowid = getActivity().getIntent().getLongExtra("workflowid", 0);
        rootView = inflater.inflate(R.layout.fragment_workflow_detail, container, false);
        progressDialog = new ProgressDialog(getActivity());
        progressDialog.setMessage(getActivity().getResources().getString(R.string.loading));
        progressDialog.setCancelable(false);
     //   WORKFLO_ID = workflowid;
        zoomin = AnimationUtils.loadAnimation(getActivity(), R.anim.zoomin);
        zoomout = AnimationUtils.loadAnimation(getActivity(), R.anim.zoomout);

        isZoomIn = false;
         sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
        wid = getActivity().getIntent().getLongExtra("wid", 0);
        Button createRun = (Button) rootView.findViewById(R.id.run_wk);
        createRun.setOnClickListener(this);
        Button download = (Button) rootView.findViewById(R.id.download_wk);
        download.setOnClickListener(this);
        Button mark_workflow = (Button) rootView.findViewById(R.id.mark_wk);
        mark_workflow.setOnClickListener(this);
        final String favs = sharedPreferences.getString(WorkflowAdapter.FAVORITE_LIST_DB, "");
        String[] ids = favs.split(",");
        if(ids.length > 0) {
            for (String id : ids)
                if (id.equalsIgnoreCase("" +wid )){
                    mark_workflow.setBackgroundResource(R.drawable.abc_list_selector_disabled_holo_light);
                    break;
                }
        }
        rootView.findViewById(R.id.saveToDropboxButton).setOnClickListener(this);
        rootView.findViewById(R.id.saveToGoogleDriveButton).setOnClickListener(this);
        (rootView.findViewById(R.id.wkf_image)).setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                view.setAnimation(zoomin);
                return true;
            }
        });
        downloadManager = (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
        return rootView;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        cont = getActivity();
    }

    @Override
    public void onClick(View view) {
        switch(view.getId()){
            case R.id.run_wk:
                if (((TextView)rootView.findViewById(R.id.wtype)).getText().toString().contains("Taverna 2"))
                    new WorkflowProcessTask(getActivity()).execute(download_url);
                else
                    Toast.makeText(getActivity(), "Sorry! Only Taverna 2 workflows can be run.", Toast.LENGTH_LONG).show();
                break;
            case R.id.download_wk:
                // start the android Download manager to start downloading a remote workflow file
                WorkflowDownloadManager dmgr = new WorkflowDownloadManager(getActivity(), downloadManager);
                try {
                    dmgr.downloadWorkflow(new File(PreferenceManager.getDefaultSharedPreferences(getActivity()).getString(
                                    DashboardMainActivity.APP_DIRECTORY_NAME, "/")),
                            download_url);
                } catch (Exception e) {
                    e.printStackTrace();
                }

                break;
            case R.id.mark_wk:

                ArrayList<Object> mfav = new ArrayList<>();
                String favs = sharedPreferences.getString(WorkflowAdapter.FAVORITE_LIST_DB, "");
                //save current workflow as favorite
                mfav.add(currentWorkflow.getId());
                mfav.add(currentWorkflow.getWorkflow_author());
                mfav.add(currentWorkflow.getWorkflow_title());
                mfav.add(currentWorkflow.getWorkflow_description());
                mfav.add(SimpleDateFormat.getDateTimeInstance().format(new Date()));
                mfav.add(currentWorkflow.getWorkflow_details_url());
                mfav.add(((TextView) rootView.findViewById(R.id.wkf_author)).getText());
                int result = new Workflow_DB(getActivity(), WorkflowAdapter.WORKFLOW_FAVORITE_KEY).insert(mfav);
                if(result >0) {
                    sharedPreferences.edit().putString(WorkflowAdapter.FAVORITE_LIST_DB, favs+wid+",").apply();
                    Toast.makeText(getActivity(), "Workflow marked as favorite", Toast.LENGTH_SHORT).show();
                     view.setBackgroundResource(R.drawable.abc_list_selector_disabled_holo_light);

                }else if(result == -1){
                    Toast.makeText(getActivity(),"Sorry! This workflow has already been marked as a favourite",Toast.LENGTH_SHORT).show();
                }else
                    Toast.makeText(getActivity(),"Error!, please try again",Toast.LENGTH_SHORT).show();
                break;
            case R.id.saveToDropboxButton:
                String authToken = PreferenceManager.getDefaultSharedPreferences(getActivity()).getString("dropboxauth", "");
                if (authToken.isEmpty())
                    mDBApi.getSession().startOAuth2Authentication(getActivity());
                else {
                    mDBApi.getSession().setOAuth2AccessToken(authToken);
                    new WorkflowDriveUpload().execute(download_url);
                }
                break;
            case R.id.saveToGoogleDriveButton:
                break;
        }
    }

    /**
     * Called when the fragment is visible to the user and actively running.
     * This is generally
     * tied to {@link android.app.Activity#onResume() Activity.onResume} of the containing
     * Activity's lifecycle.
     */
    @Override
    public void onResume() {
        super.onResume();
        if(!LOAD_STATE)
        workflow_uri = getActivity().getIntent().getStringExtra("uri");

        getActivity().getLoaderManager().initLoader(1, null, this).forceLoad();

        if (mDBApi.getSession().authenticationSuccessful() && !DROPUPLOAD) {
            try {
                // Required to complete auth, sets the access token on the session
                mDBApi.getSession().finishAuthentication();
                String accessToken = mDBApi.getSession().getOAuth2AccessToken();
                PreferenceManager.getDefaultSharedPreferences(getActivity()).edit().putString("dropboxauth", accessToken).commit();
                new WorkflowDriveUpload().execute(download_url);
            } catch (IllegalStateException e) {
                Log.i("DbAuthLog", "Error authenticating", e);
            }
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        LOAD_STATE = true;
    }

    @Override
    public Loader<Workflow> onCreateLoader(int i, Bundle bundle) {
        progressDialog.show();
        return new DetailsLoader(getActivity(),
                DetailsLoader.LOAD_TYPE.TYPE_WORKFLOW_DETAIL,
                workflow_uri);
    }

    @Override
    public void onLoadFinished(Loader<Workflow> workflowLoader, Workflow workflow) {

    }

    @Override
    public void onLoaderReset(Loader<Workflow> workflowLoader) {
        workflowLoader.reset();
    }

    public static void setWorkflowDetails(final Workflow wk){
        currentWorkflow = wk;
        final TextView author = (TextView) rootView.findViewById(R.id.wkf_author_text);
        //final TextView updated = (TextView) rootView.findViewById(R.id.wupdatedat);
        final TextView type = (TextView) rootView.findViewById(R.id.wtype);
        final TextView title = (TextView) rootView.findViewById(R.id.wtitle);
        final TextView desc = (TextView) rootView.findViewById(R.id.wdescription);
        final TextView createdat = (TextView) rootView.findViewById(R.id.wcreatedat);
        final ImageView preview = (ImageView) rootView.findViewById(R.id.wkf_image);
        WORKFLO_ID = wk.getWorkflow_title();
        ((Activity)cont).runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //load necessary widgets

                //set widget data
                //Use android resources to insert text into placeholder
                Resources resources = cont.getResources();
                User uploader = wk.getUploader();
                //String uploaderText = String.format(resources.getString(R.string.workflow_author), uploader != null ? uploader.getName():"Unknown");
                author.setText((uploader != null) ? uploader.getName() : "Unknown");
                title.setText(wk.getWorkflow_title());
                if (wk.getWorkflow_description() != null) {
                    desc.setText(wk.getWorkflow_description());
                } else {
                    desc.setVisibility(View.GONE); //Not sure I trust this! Needs investigating.
                }
                String createdAtText = String.format(resources.getString(R.string.created), wk.getWorkflow_datecreated());
                createdat.setText(createdAtText);
                //updated.setText("Workflow Description");
                String typeText = String.format(resources.getString(R.string.workflow_type_text), wk.getWorkflow_Type());
                type.setText(typeText);

                  //preview.setImageURI(Uri.parse(wk.getWorkflow_preview()));
                new LoadImageThread(preview, wk.getWorkflow_preview()).execute();
                download_url =wk.getWorkflow_remote_url();
                zoomin.setAnimationListener(new Animation.AnimationListener() {

                    @Override
                    public void onAnimationStart(Animation arg0) {
                        // TODO Auto-generated method stub
                        preview.startAnimation(zoomout);
                    }

                    @Override
                    public void onAnimationRepeat(Animation arg0) {
                        // TODO Auto-generated method stub

                    }

                    @Override
                    public void onAnimationEnd(Animation arg0) {
                        preview.startAnimation(zoomout);

                    }
                });
                zoomout.setAnimationListener(new Animation.AnimationListener() {

                    @Override
                    public void onAnimationStart(Animation arg0) {
                        // TODO Auto-generated method stub
                        preview.startAnimation(zoomin);
                    }

                    @Override
                    public void onAnimationRepeat(Animation arg0) {
                        // TODO Auto-generated method stub

                    }

                    @Override
                    public void onAnimationEnd(Animation arg0) {
                        preview.startAnimation(zoomin);

                    }
                });
                progressDialog.dismiss();
            }
        });
      //  preview.setOnClickListener(WorkflowdetailFragment.getInstance());
    }

    private static class LoadImageThread extends AsyncTask<String, Void, Bitmap>{
          ImageView imageView;
          String src ;
        public LoadImageThread(ImageView image, String source) {
            imageView = image;
            src = source;
        }

        @Override
        protected Bitmap doInBackground(String... strings) {
            Bitmap myBitmap = null;
            try {
                URL url = new URL(src);
                HttpURLConnection connection;
                connection = (HttpURLConnection) url.openConnection();
                connection.setDoInput(true);
                connection.connect();
                InputStream input = connection.getInputStream();
                 myBitmap = BitmapFactory.decodeStream(input);
//                imageView.setImageBitmap(myBitmap);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return myBitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            imageView.setImageBitmap(bitmap);
        }
    }

    //create and return a new TextView
    public TextView createTextView(Context mcontext, String placeholder){
        TextView tv = new TextView(mcontext);
        tv.setText(placeholder);
        tv.setMinLines(2);

        return tv;
    }

    //create and return a new EdiText view
    public EditText createEditText(Context ctx, int i){
        EditText edt;
        edt = new EditText(ctx);
        edt.setHint("Enter Value");
        edt.setMinLines(1);
        edt.setId(i);
        return edt;
    }
    //fetch and compute the framework on which the run inputs are to be built and entered
    private class WorkflowRunTask extends AsyncTask<String, Void, String>{

        private Context context;
        TavernaPlayerAPI tavernaPlayerAPI = new TavernaPlayerAPI();

        private WorkflowRunTask(Context context) {
            this.context = context;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            progressDialog.setMessage(this.context.getResources().getString(R.string.fetchrun));
            progressDialog.show();
        }

        @Override
        protected String doInBackground(String... params) {
            StringBuffer sb = new StringBuffer();
            try {

                URL workflowurl = new URL(new TavernaPlayerAPI(this.context).PLAYER_RUN_FRAMEWORK_URL+params[0]);
                HttpURLConnection connection = (HttpURLConnection) workflowurl.openConnection();
                String userpass = tavernaPlayerAPI.getPlayerUserName(this.context) + ":" + tavernaPlayerAPI.getPlayerUserPassword(this.context);
                String basicAuth = "Basic " + Base64.encodeToString(userpass.getBytes(), Base64.DEFAULT);

                connection.setRequestProperty("Authorization", basicAuth);
                connection.setRequestProperty("Accept", "application/json");
                connection.setRequestMethod("GET");
                // connection.setDoInput(true);
                //  connection.setDoOutput(true);
                connection.connect(); //send request
                Log.i("RESPONSE Code", "" + connection.getResponseCode());
                Log.i("RESPONSE Messsage", "" + connection.getResponseMessage());
                Log.i("Authorization ", "" + connection.getRequestProperty("Authorization"));

                InputStream dis = connection.getInputStream();
                BufferedReader br = new BufferedReader(new InputStreamReader(dis));

                String jsonData = "";
                while ((jsonData = br.readLine()) != null) {
                    sb.append(jsonData);
                }
                dis.close();
                br.close();
                return sb.toString();

            }catch (IOException ex){
                ex.printStackTrace();
            }
            return sb.toString();
        }

        @Override
        protected void onPostExecute(String result) {
            //show the skeleton to the user in a dialog box
            final Context ctx = this.context;
            final LinearLayout ll = new LinearLayout(ctx);
            ScrollView sv = new ScrollView(ctx);
            ll.setOrientation(LinearLayout.VERTICAL);
            sv.addView(ll);

            try {
                final JSONObject json = new JSONObject(result); //main server response json
                JSONObject mjson = json.getJSONObject("run"); //main framework response json
                String name = mjson.getString("name"); //a name that can be configured or edited for the new run to be created
                ll.addView(createTextView(ctx, name));
                final JSONArray attr_array = mjson.getJSONArray("inputs_attributes");
                for(int i=0; i<attr_array.length(); i++){
                    JSONObject obj = attr_array.getJSONObject(i);
                    ll.addView(createTextView(ctx, obj.getString("name")));
                    ll.addView(createEditText(ctx, i));
                }

                alertDialogBuilder = new AlertDialog.Builder(ctx);
                alertDialogBuilder.setView(sv);
 //               alertDialogBuilder.setMessage(result);
                alertDialogBuilder.setIcon(ctx.getResources().getDrawable(R.mipmap.ic_launcher));
                alertDialogBuilder.setTitle("New Workflow Run");
                alertDialogBuilder.setPositiveButton("Execute", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        int n = attr_array.length();
                        for(int j=0; j<n; j++){
                            try {
                                EditText inputText = (EditText) ll.findViewById(j);
                                String value = inputText.getText().toString();//get input entry entered by the user
                                JSONObject jojb = attr_array.getJSONObject(j); //get the input attributes provided by the skeleton
                                jojb.put("value", value); //replace value field in object with the entry provided by the user
                                attr_array.put(j, jojb); //replace the input entry with the new name/input json object

                            } catch (JSONException e) {
                                e.printStackTrace();
                            }

                        }
                        try {
                            json.put("inputs_attributes", attr_array);
                            Log.i("RUN FRAMEWORK", json.toString(2));
                            //start a run task to execute the run.
                            new RunTask(ctx).execute(json.toString());
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }catch (Exception ex){
                            ex.printStackTrace();
                        }

                    }
                });
                alertDialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialogInterface.dismiss();
                    }
                });

                runDialog = alertDialogBuilder.create();

            } catch (JSONException e) {
                e.printStackTrace();
            }
            progressDialog.dismiss();

            runDialog.show();
        }
    }

    /**
     *    Send request for the execution of a run on a Taverna server through the Taverna player using the Player API
     *    This process passes through several steps,\n
     *    1- Downloading and caching a local version of the workflow whose run we need \n
     *    2- uploading the workflow through the portal to register it so a run can be generated from it. The request requires some authentication
     *    3- Retrieving the results and extracting data required to create a run (the workflow_id) as provided by the results
     *    4- Posting a run request to the player so that a new run can be created and started
     *    5- retrieving a run framework so that users can know what types of input is expected for a successful run
     *    6- retrieving and displaying run results
     */
    private class WorkflowProcessTask extends AsyncTask<String, Void, String>{

        private Context context;

        private WorkflowProcessTask(Context context) {
            this.context = context;
        }

        @Override
        protected void onPreExecute() {
            progressDialog.setMessage("Uploading Workflow ... ");
            progressDialog.show();
        }

        @Override
        protected String doInBackground(String... params) {
            StringBuffer sb = new StringBuffer();
            try {
                //prepare connection requests
                URL workflowurl = new URL(params[0]); //the resource xml file representing the workflow to be uploaded to the player
                String playerurl = new TavernaPlayerAPI(this.context).PLAYER_BASE_URL+"workflows.json";
                TavernaPlayerAPI tavernaPlayerAPI = new TavernaPlayerAPI();

                URL posturl = new URL(playerurl);
                HttpURLConnection connection = (HttpURLConnection) posturl.openConnection();
                HttpURLConnection wconn = (HttpURLConnection) workflowurl.openConnection();
                    wconn.setRequestMethod("GET");
                    wconn.setDoOutput(true);
                    wconn.setRequestProperty("Accept", "application/xml");
                    wconn.connect();

                String user = tavernaPlayerAPI.getPlayerUserName(this.context) + ":" + tavernaPlayerAPI.getPlayerUserPassword(this.context);
                String basicAuth = "Basic " + Base64.encodeToString(user.getBytes(), Base64.DEFAULT);
                //read the file from remote resource and encode the stream with a base64 algorithm
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(wconn.getInputStream()));
                String str = "";
                while ((str = bufferedReader.readLine()) != null)
                    sb.append(str); //in this string builder we have read the workflow( as .t2flow or .xml) workflow from remote resource. Now we need to post that to the player.
                bufferedReader.close();
                wconn.disconnect();

                String data = "{\"document\":\"data:application/octet-stream;base64," +
                        Base64.encodeToString(sb.toString().getBytes("UTF-8"), Base64.URL_SAFE|Base64.NO_WRAP).replace('-','+')+"\"}";
                String post = "{\"workflow\":"+data+"}";
                //clear sb so that we can use it again to fetch results from this post request
                sb.delete(0,sb.length()-1);
                System.out.println("BODY=>"+post);
                connection.setRequestMethod("POST");
                connection.setRequestProperty("Authorization", basicAuth);
                connection.setRequestProperty("Accept", "*/*");
                connection.setRequestProperty("Content-Type", "application/json");
                connection.setRequestProperty("Content-Encoding", "UTF-8");
                connection.setUseCaches (false);
                connection.setDoOutput(true);
                connection.connect(); //send request

                DataOutputStream dos = new DataOutputStream(connection.getOutputStream());

                dos.writeBytes(post);//write post data which is a formatted json data representing body of workflow

                dos.flush();
                dos.close();

                InputStream dis = connection.getInputStream();
                BufferedReader br = new BufferedReader(new InputStreamReader(dis));
                while ((str = br.readLine())!= null)
                    sb.append(str);
                connection.disconnect();
            }catch (IOException e){
                e.printStackTrace();
                sb.append("Error reading remote workflow. Please try again later");
            }

            return sb.toString();
        }

        /**
         * Receives a result from the player as a json describing the workflow that has just been uploaded along with key components that
         * can be used to generate a run from thw workflow. A run is started that would fetch and build a sample UI for a workflow run to be executed
         * @param s the json result that describes the uploaded workflow
         */
        @Override
        protected void onPostExecute(String s) {
            progressDialog.dismiss();
            s = s.substring(1, s.length());
            try {
                JSONObject workflowJson = new JSONObject(s);
                new WorkflowRunTask(getActivity()).execute(workflowJson.getString("id"));

            } catch (JSONException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * Upload workflow from myexperiment to DropBox
     */
    private class WorkflowDriveUpload extends  AsyncTask<String, Void, String>{
        @Override
        protected void onPreExecute() {
            Toast.makeText(getActivity(), "Saving workflow to dropBox", Toast.LENGTH_LONG).show();
        }

        @Override
        protected String doInBackground(String... files) {
           // File file = new File(files[0]);
            HttpURLConnection mconn ;
         //   FileInputStream inputStream = null;
            DropboxAPI.Entry response = null;
            DropboxAPI.Entry metaDataEntry = null;
            try {
                mconn = (HttpURLConnection) new URL(files[0]).openConnection();
                mconn.setRequestMethod("GET");
                mconn.connect();

              //  inputStream = new FileInputStream(file);

                 response = mDBApi.putFile("/"+ Uri.parse(files[0]).getLastPathSegment(), mconn.getInputStream(),
                        mconn.getContentLength(), null, new ProgressListener() {
                             @Override
                             public void onProgress(long l, long l2) {
                                 if (l==l2){
                                     Toast.makeText(getActivity(), "Upload complete", Toast.LENGTH_LONG).show();
                                 }
                             }
                         });

                Log.i("DbExampleLog", "The uploaded file's rev is: " + response.rev);
              //  metaDataEntry = mDBApi.metadata("/"+Uri.parse(files[0]).getLastPathSegment(), 1, null, false, null);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (DropboxException e) {
                e.printStackTrace();
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return response.rev;
        }

        @Override
        protected void onPostExecute(String s) {
            if(null != s) {
                Toast.makeText(getActivity(), "File Saved to dropbox. Reference: " + s, Toast.LENGTH_LONG).show();
                DROPUPLOAD = true;
            }
            else{
                Toast.makeText(getActivity(), "Failed to save to dropbox "+s, Toast.LENGTH_LONG).show();
            }
        }
    }

}


text:
package org.wso2.carbon.event.output.adaptor.core.message.config;

import java.util.HashMap;
import java.util.Map;

/**
 * Output event message configuration details
 */
public class OutputEventAdaptorMessageConfiguration {

    /**
     * Map contains the output message property configuration details
     */
    private Map<String, String> outputMessageProperties;

    public OutputEventAdaptorMessageConfiguration() {

        this.outputMessageProperties = new HashMap<String, String>();
    }

    public void addOutputMessageProperty(String name, String value) {
        this.outputMessageProperties.put(name, value);
    }

    public Map<String, String> getOutputMessageProperties() {
        return outputMessageProperties;
    }

    public void setOutputMessageProperties(Map<String, String> outputMessageProperties) {
        this.outputMessageProperties = outputMessageProperties;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof OutputEventAdaptorMessageConfiguration)) {
            return false;
        }

        OutputEventAdaptorMessageConfiguration that = (OutputEventAdaptorMessageConfiguration) o;

        if (outputMessageProperties != null ? !outputMessageProperties.equals(that.outputMessageProperties) : that.outputMessageProperties != null) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result = outputMessageProperties != null ? outputMessageProperties.hashCode() : 0;
        return result;
    }
}