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  * @author jsharkey
30  */
31 public class PromptHelper {
32 	private final Object tag;
33 
34 	private Handler handler = null;
35 
36 	private Semaphore promptToken;
37 	private Semaphore promptResponse;
38 
39 	public String promptInstructions = null;
40 	public String promptHint = null;
41 	public Object promptRequested = null;
42 
43 	private Object response = null;
44 
PromptHelper(Object tag)45 	public PromptHelper(Object tag) {
46 		this.tag = tag;
47 
48 		// Threads must acquire this before they can send a prompt.
49 		promptToken = new Semaphore(1);
50 
51 		// Responses will release this semaphore.
52 		promptResponse = new Semaphore(0);
53 	}
54 
55 
56 	/**
57 	 * Register a user interface handler, if available.
58 	 */
setHandler(Handler handler)59 	public void setHandler(Handler handler) {
60 		this.handler = handler;
61 	}
62 
63 	/**
64 	 * Set an incoming value from an above user interface. Will automatically
65 	 * notify any waiting requests.
66 	 */
setResponse(Object value)67 	public void setResponse(Object value) {
68 		response = value;
69 		promptRequested = null;
70 		promptInstructions = null;
71 		promptHint = null;
72 		promptResponse.release();
73 	}
74 
75 	/**
76 	 * Return the internal response value just before erasing and returning it.
77 	 */
popResponse()78 	protected Object popResponse() {
79 		Object value = response;
80 		response = null;
81 		return value;
82 	}
83 
84 
85 	/**
86 	 * Request a prompt response from parent. This is a blocking call until user
87 	 * interface returns a value.
88 	 * Only one thread can call this at a time. cancelPrompt() will force this to
89 	 * immediately return.
90 	 */
requestPrompt(String instructions, String hint, Object type)91 	private Object requestPrompt(String instructions, String hint, Object type) throws InterruptedException {
92 		Object response = null;
93 
94 		promptToken.acquire();
95 
96 		try {
97 			promptInstructions = instructions;
98 			promptHint = hint;
99 			promptRequested = type;
100 
101 			// notify any parent watching for live events
102 			if (handler != null)
103 				Message.obtain(handler, -1, tag).sendToTarget();
104 
105 			// acquire lock until user passes back value
106 			promptResponse.acquire();
107 
108 			response = popResponse();
109 		} finally {
110 			promptToken.release();
111 		}
112 
113 		return response;
114 	}
115 
116 	/**
117 	 * Request a string response from parent. This is a blocking call until user
118 	 * interface returns a value.
119 	 * @param hint prompt hint for user to answer
120 	 * @return string user has entered
121 	 */
requestStringPrompt(String instructions, String hint)122 	public String requestStringPrompt(String instructions, String hint) {
123 		String value = null;
124 		try {
125 			value = (String)this.requestPrompt(instructions, hint, String.class);
126 		} catch(Exception e) {
127 		}
128 		return value;
129 	}
130 
131 	/**
132 	 * Request a boolean response from parent. This is a blocking call until user
133 	 * interface returns a value.
134 	 * @param hint prompt hint for user to answer
135 	 * @return choice user has made (yes/no)
136 	 */
requestBooleanPrompt(String instructions, String hint)137 	public Boolean requestBooleanPrompt(String instructions, String hint) {
138 		Boolean value = null;
139 		try {
140 			value = (Boolean)this.requestPrompt(instructions, hint, Boolean.class);
141 		} catch(Exception e) {
142 		}
143 		return value;
144 	}
145 
146 	/**
147 	 * Cancel an in-progress prompt.
148 	 */
cancelPrompt()149 	public void cancelPrompt() {
150 		if (!promptToken.tryAcquire()) {
151 			// A thread has the token, so try to interrupt it
152 			response = null;
153 			promptResponse.release();
154 		} else {
155 			// No threads have acquired the token
156 			promptToken.release();
157 		}
158 	}
159 }
160