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 
18 package org.connectbot.service;
19 
20 import java.util.concurrent.Semaphore;
21 
22 import android.os.Handler;
23 import 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  */
30 public 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 
PromptHelper(Object tag)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 	 */
setHandler(Handler handler)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 	 */
setResponse(Object value)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 	 */
popResponse()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 	 */
requestPrompt(String instructions, String hint, Object type)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 	 */
requestStringPrompt(String instructions, String hint)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 	 */
requestBooleanPrompt(String instructions, String hint)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 	 */
cancelPrompt()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