1/*
2 * ConnectBot: simple, powerful, open-source SSH client for Android
3 * Copyright 2007 Kenny Root, Jeffrey Sharkey
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package org.connectbot.service;
19
20import java.util.concurrent.Semaphore;
21
22import android.os.Handler;
23import android.os.Message;
24
25/**
26 * Helps provide a relay for prompts and responses between a possible user
27 * interface and some underlying service.
28 *
29 */
30public class PromptHelper {
31	private final Object tag;
32
33	private Handler handler = null;
34
35	private Semaphore promptToken;
36	private Semaphore promptResponse;
37
38	public String promptInstructions = null;
39	public String promptHint = null;
40	public Object promptRequested = null;
41
42	private Object response = null;
43
44	public PromptHelper(Object tag) {
45		this.tag = tag;
46
47		// Threads must acquire this before they can send a prompt.
48		promptToken = new Semaphore(1);
49
50		// Responses will release this semaphore.
51		promptResponse = new Semaphore(0);
52	}
53
54
55	/**
56	 * Register a user interface handler, if available.
57	 */
58	public void setHandler(Handler handler) {
59		this.handler = handler;
60	}
61
62	/**
63	 * Set an incoming value from an above user interface. Will automatically
64	 * notify any waiting requests.
65	 */
66	public void setResponse(Object value) {
67		response = value;
68		promptRequested = null;
69		promptInstructions = null;
70		promptHint = null;
71		promptResponse.release();
72	}
73
74	/**
75	 * Return the internal response value just before erasing and returning it.
76	 */
77	protected Object popResponse() {
78		Object value = response;
79		response = null;
80		return value;
81	}
82
83
84	/**
85	 * Request a prompt response from parent. This is a blocking call until user
86	 * interface returns a value.
87	 * Only one thread can call this at a time. cancelPrompt() will force this to
88	 * immediately return.
89	 */
90	private Object requestPrompt(String instructions, String hint, Object type) throws InterruptedException {
91		Object response = null;
92
93		promptToken.acquire();
94
95		try {
96			promptInstructions = instructions;
97			promptHint = hint;
98			promptRequested = type;
99
100			// notify any parent watching for live events
101			if (handler != null)
102				Message.obtain(handler, -1, tag).sendToTarget();
103
104			// acquire lock until user passes back value
105			promptResponse.acquire();
106
107			response = popResponse();
108		} finally {
109			promptToken.release();
110		}
111
112		return response;
113	}
114
115	/**
116	 * Request a string response from parent. This is a blocking call until user
117	 * interface returns a value.
118	 * @param hint prompt hint for user to answer
119	 * @return string user has entered
120	 */
121	public String requestStringPrompt(String instructions, String hint) {
122		String value = null;
123		try {
124			value = (String)this.requestPrompt(instructions, hint, String.class);
125		} catch(Exception e) {
126		}
127		return value;
128	}
129
130	/**
131	 * Request a boolean response from parent. This is a blocking call until user
132	 * interface returns a value.
133	 * @param hint prompt hint for user to answer
134	 * @return choice user has made (yes/no)
135	 */
136	public Boolean requestBooleanPrompt(String instructions, String hint) {
137		Boolean value = null;
138		try {
139			value = (Boolean)this.requestPrompt(instructions, hint, Boolean.class);
140		} catch(Exception e) {
141		}
142		return value;
143	}
144
145	/**
146	 * Cancel an in-progress prompt.
147	 */
148	public void cancelPrompt() {
149		if (!promptToken.tryAcquire()) {
150			// A thread has the token, so try to interrupt it
151			response = null;
152			promptResponse.release();
153		} else {
154			// No threads have acquired the token
155			promptToken.release();
156		}
157	}
158}
159