Skip to content

Commit 009920d

Browse files
authored
Phi 3 Android app Update (#465)
* Update Android app layout * add settings functionality * Log prompt processing time and add screenshot * add popup to display token generation rates and apply feedback
1 parent f41e1ed commit 009920d

File tree

8 files changed

+260
-19
lines changed

8 files changed

+260
-19
lines changed

Diff for: mobile/examples/phi-3/android/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,4 @@ Here are some sample example screenshots of the app.
5858

5959
<img width=20% src="images/Local_LLM_3.jpg" alt="App Screenshot 3" />
6060

61+
<img width=20% src="images/Local_LLM_4.png" alt="App Screenshot 3" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package ai.onnxruntime.genai.demo;
2+
3+
import android.os.Bundle;
4+
import android.view.LayoutInflater;
5+
import android.view.View;
6+
import android.view.ViewGroup;
7+
import android.widget.Button;
8+
import android.widget.EditText;
9+
import androidx.annotation.NonNull;
10+
import androidx.annotation.Nullable;
11+
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
12+
13+
public class BottomSheet extends BottomSheetDialogFragment {
14+
private EditText maxLengthEditText;
15+
private EditText lengthPenaltyEditText;
16+
private SettingsListener settingsListener;
17+
18+
public interface SettingsListener {
19+
void onSettingsApplied(int maxLength, float lengthPenalty);
20+
}
21+
22+
public void setSettingsListener(SettingsListener listener) {
23+
this.settingsListener = listener;
24+
}
25+
26+
@Nullable
27+
@Override
28+
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
29+
View view = inflater.inflate(R.layout.bottom_sheet, container, false);
30+
31+
maxLengthEditText = view.findViewById(R.id.idEdtMaxLength);
32+
lengthPenaltyEditText = view.findViewById(R.id.idEdtLengthPenalty);
33+
34+
Button applyButton = view.findViewById(R.id.applySettingsButton);
35+
36+
applyButton.setOnClickListener(v -> {
37+
if (settingsListener != null) {
38+
int maxLength = Integer.parseInt(maxLengthEditText.getText().toString());
39+
float lengthPenalty = Float.parseFloat(lengthPenaltyEditText.getText().toString());
40+
settingsListener.onSettingsApplied(maxLength, lengthPenalty);
41+
dismiss();
42+
}
43+
});
44+
45+
return view;
46+
}
47+
}

Diff for: mobile/examples/phi-3/android/app/src/main/java/ai/onnxruntime/genai/demo/MainActivity.java

+81-7
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
import androidx.appcompat.app.AppCompatActivity;
44

5+
import android.app.Dialog;
56
import android.content.Context;
67
import android.os.Bundle;
78
import android.text.method.ScrollingMovementMethod;
89
import android.util.Log;
910
import android.util.Pair;
1011
import android.view.View;
1112
import android.view.WindowManager;
13+
import android.widget.Button;
1214
import android.widget.EditText;
1315
import android.widget.ImageButton;
1416
import android.widget.TextView;
@@ -41,7 +43,10 @@ public class MainActivity extends AppCompatActivity implements Consumer<String>
4143
private TextView generatedTV;
4244
private TextView promptTV;
4345
private TextView progressText;
46+
private ImageButton settingsButton;
4447
private static final String TAG = "genai.demo.MainActivity";
48+
private int maxLength = 100;
49+
private float lengthPenalty = 1.0f;
4550

4651
private static boolean fileExists(Context context, String fileName) {
4752
File file = new File(context.getFilesDir(), fileName);
@@ -55,6 +60,13 @@ protected void onCreate(Bundle savedInstanceState) {
5560
binding = ActivityMainBinding.inflate(getLayoutInflater());
5661
setContentView(binding.getRoot());
5762

63+
sendMsgIB = findViewById(R.id.idIBSend);
64+
userMsgEdt = findViewById(R.id.idEdtMessage);
65+
generatedTV = findViewById(R.id.sample_text);
66+
promptTV = findViewById(R.id.user_text);
67+
progressText = findViewById(R.id.progress_text);
68+
settingsButton = findViewById(R.id.idIBSettings);
69+
5870
// Trigger the download operation when the application is created
5971
try {
6072
downloadModels(
@@ -63,10 +75,20 @@ protected void onCreate(Bundle savedInstanceState) {
6375
throw new RuntimeException(e);
6476
}
6577

66-
sendMsgIB = findViewById(R.id.idIBSend);
67-
userMsgEdt = findViewById(R.id.idEdtMessage);
68-
generatedTV = findViewById(R.id.sample_text);
69-
promptTV = findViewById(R.id.user_text);
78+
settingsButton.setOnClickListener(v -> {
79+
BottomSheet bottomSheet = new BottomSheet();
80+
bottomSheet.setSettingsListener(new BottomSheet.SettingsListener() {
81+
@Override
82+
public void onSettingsApplied(int maxLength, float lengthPenalty) {
83+
MainActivity.this.maxLength = maxLength;
84+
MainActivity.this.lengthPenalty = lengthPenalty;
85+
Log.i(TAG, "Setting max response length to: " + maxLength);
86+
Log.i(TAG, "Setting length penalty to: " + lengthPenalty);
87+
}
88+
});
89+
bottomSheet.show(getSupportFragmentManager(), "BottomSheet");
90+
});
91+
7092

7193
Consumer<String> tokenListener = this;
7294

@@ -99,6 +121,7 @@ public void onClick(View v) {
99121

100122
// Disable send button while responding to prompt.
101123
sendMsgIB.setEnabled(false);
124+
sendMsgIB.setAlpha(0.5f);
102125

103126
promptTV.setText(promptQuestion);
104127
// Clear Edit Text or prompt question.
@@ -117,22 +140,53 @@ public void run() {
117140

118141
generatorParams = model.createGeneratorParams();
119142
//examples for optional parameters to format AI response
120-
//generatorParams.setSearchOption("length_penalty", 1000);
121-
//generatorParams.setSearchOption("max_length", 500);
143+
// https://onnxruntime.ai/docs/genai/reference/config.html
144+
generatorParams.setSearchOption("length_penalty", lengthPenalty);
145+
generatorParams.setSearchOption("max_length", maxLength);
122146

123147
encodedPrompt = tokenizer.encode(promptQuestion_formatted);
124148
generatorParams.setInput(encodedPrompt);
125149

126150
generator = new Generator(model, generatorParams);
127151

152+
// try to measure average time taken to generate each token.
153+
long startTime = System.currentTimeMillis();
154+
long firstTokenTime = startTime;
155+
long currentTime = startTime;
156+
int numTokens = 0;
128157
while (!generator.isDone()) {
129158
generator.computeLogits();
130159
generator.generateNextToken();
131160

132161
int token = generator.getLastTokenInSequence(0);
133-
162+
163+
if (numTokens == 0) { //first token
164+
firstTokenTime = System.currentTimeMillis();
165+
}
166+
134167
tokenListener.accept(stream.decode(token));
168+
169+
170+
Log.i(TAG, "Generated token: " + token + ": " + stream.decode(token));
171+
Log.i(TAG, "Time taken to generate token: " + (System.currentTimeMillis() - currentTime)/ 1000.0 + " seconds");
172+
currentTime = System.currentTimeMillis();
173+
numTokens++;
135174
}
175+
long totalTime = System.currentTimeMillis() - firstTokenTime;
176+
177+
float promptProcessingTime = (firstTokenTime - startTime)/ 1000.0f;
178+
float tokensPerSecond = (1000 * (numTokens -1)) / totalTime;
179+
180+
runOnUiThread(() -> {
181+
sendMsgIB.setEnabled(true);
182+
sendMsgIB.setAlpha(1.0f);
183+
184+
// Display the token generation rate in a dialog popup
185+
showTokenPopup(promptProcessingTime, tokensPerSecond);
186+
});
187+
188+
Log.i(TAG, "Prompt processing time (first token): " + promptProcessingTime + " seconds");
189+
Log.i(TAG, "Tokens generated per second (excluding prompt processing): " + tokensPerSecond);
136190
}
137191
catch (GenAIException e) {
138192
Log.e(TAG, "Exception occurred during model query: " + e.getMessage());
@@ -146,6 +200,7 @@ public void run() {
146200

147201
runOnUiThread(() -> {
148202
sendMsgIB.setEnabled(true);
203+
sendMsgIB.setAlpha(1.0f);
149204
});
150205
}
151206
}).start();
@@ -256,4 +311,23 @@ public void setVisibility() {
256311
TextView botView = (TextView) findViewById(R.id.sample_text);
257312
botView.setVisibility(View.VISIBLE);
258313
}
314+
315+
private void showTokenPopup(float promptProcessingTime, float tokenRate) {
316+
317+
final Dialog dialog = new Dialog(MainActivity.this);
318+
dialog.setContentView(R.layout.info_popup);
319+
320+
TextView promptProcessingTimeTv = dialog.findViewById(R.id.prompt_processing_time_tv);
321+
TextView tokensPerSecondTv = dialog.findViewById(R.id.tokens_per_second_tv);
322+
Button closeBtn = dialog.findViewById(R.id.close_btn);
323+
324+
promptProcessingTimeTv.setText(String.format("Prompt processing time: %.2f seconds", promptProcessingTime));
325+
tokensPerSecondTv.setText(String.format("Tokens per second: %.2f", tokenRate));
326+
327+
closeBtn.setOnClickListener(v -> dialog.dismiss());
328+
329+
dialog.show();
330+
}
331+
332+
259333
}

Diff for: mobile/examples/phi-3/android/app/src/main/res/drawable/rounded_corner2.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
33

4-
<solid android:color="#828380" />
4+
<solid android:color="#03A9F4" />
55

66
<padding
77
android:left="1dp"

Diff for: mobile/examples/phi-3/android/app/src/main/res/layout/activity_main.xml

+41-11
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99

1010
<!--recycler view to display our chats-->
1111

12+
1213
<TextView
13-
android:visibility="invisible"
1414
android:id="@+id/user_text"
1515
android:layout_width="0dp"
1616
android:layout_height="wrap_content"
@@ -22,39 +22,42 @@
2222
android:textAlignment="textEnd"
2323
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
2424
android:textSize="16sp"
25+
android:visibility="invisible"
2526
app:layout_constraintBottom_toBottomOf="parent"
2627
app:layout_constraintEnd_toEndOf="parent"
2728
app:layout_constraintHorizontal_bias="0.855"
2829
app:layout_constraintStart_toStartOf="parent"
2930
app:layout_constraintTop_toTopOf="parent"
30-
app:layout_constraintVertical_bias="0.0" />
31+
app:layout_constraintVertical_bias="0.0"
32+
tools:visibility="visible" />
3133

3234
<TextView
33-
android:visibility="invisible"
3435
android:id="@+id/sample_text"
35-
36-
android:background="@drawable/rounded_corner2"
3736
android:layout_width="0dp"
38-
android:layout_height="0dp"
37+
38+
android:layout_height="wrap_content"
3939
android:layout_marginStart="32dp"
4040
android:layout_marginTop="16dp"
4141
android:layout_marginEnd="64dp"
4242
android:layout_marginBottom="16dp"
43+
android:background="@drawable/rounded_corner2"
4344
android:padding="8dp"
4445
android:scrollbars="vertical"
4546
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
4647
android:textSize="16sp"
48+
android:visibility="invisible"
4749
app:layout_constraintBottom_toTopOf="@+id/idEdtMessage"
4850
app:layout_constraintEnd_toEndOf="parent"
4951
app:layout_constraintHorizontal_bias="0.144"
5052
app:layout_constraintStart_toStartOf="parent"
5153
app:layout_constraintTop_toBottomOf="@+id/user_text"
52-
app:layout_constraintVertical_bias="0.0" />
54+
app:layout_constraintVertical_bias="0.0"
55+
tools:visibility="visible" />
5356

5457
<EditText
5558
android:id="@+id/idEdtMessage"
56-
android:layout_width="300dp"
57-
android:layout_height="60dp"
59+
android:layout_width="249dp"
60+
android:layout_height="63dp"
5861
android:layout_marginStart="16dp"
5962
android:layout_marginBottom="16dp"
6063
android:layout_weight="4"
@@ -70,8 +73,8 @@
7073
android:layout_width="66dp"
7174
android:layout_height="60dp"
7275
android:layout_gravity="center_vertical"
73-
android:layout_marginEnd="16dp"
74-
android:layout_marginBottom="16dp"
76+
android:layout_marginEnd="68dp"
77+
android:layout_marginBottom="12dp"
7578
android:layout_weight="1"
7679
android:adjustViewBounds="false"
7780
android:background="#89CC04"
@@ -83,4 +86,31 @@
8386
app:layout_constraintEnd_toEndOf="parent"
8487
tools:ignore="UseAppTint" />
8588

89+
<ImageButton
90+
android:id="@+id/idIBSettings"
91+
android:layout_width="50dp"
92+
android:layout_height="60dp"
93+
android:layout_marginEnd="8dp"
94+
android:layout_marginBottom="12dp"
95+
android:src="@android:drawable/ic_menu_preferences"
96+
android:background="#00BCD4"
97+
android:backgroundTint="#009688"
98+
android:tint="@color/white"
99+
android:hapticFeedbackEnabled="true"
100+
app:layout_constraintBottom_toBottomOf="parent"
101+
app:layout_constraintEnd_toEndOf="parent"
102+
tools:ignore="UseAppTint" />
103+
104+
<TextView
105+
android:id="@+id/progress_text"
106+
android:layout_width="wrap_content"
107+
android:layout_height="wrap_content"
108+
android:text="TextView"
109+
android:visibility="invisible"
110+
app:layout_constraintBottom_toBottomOf="parent"
111+
app:layout_constraintEnd_toEndOf="parent"
112+
app:layout_constraintStart_toStartOf="parent"
113+
app:layout_constraintTop_toTopOf="parent"
114+
tools:visibility="invisible" />
115+
86116
</androidx.constraintlayout.widget.ConstraintLayout>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools"
4+
android:layout_width="match_parent"
5+
android:layout_height="wrap_content"
6+
android:orientation="vertical"
7+
android:padding="16dp">
8+
9+
<!-- Max Response Length -->
10+
<TextView
11+
android:layout_width="wrap_content"
12+
android:layout_height="wrap_content"
13+
android:text="Max Response Length"
14+
android:textSize="16sp"
15+
android:textColor="@color/black"
16+
android:layout_marginBottom="8dp"/>
17+
18+
<EditText
19+
android:id="@+id/idEdtMaxLength"
20+
android:layout_width="match_parent"
21+
android:layout_height="wrap_content"
22+
android:inputType="number"
23+
android:hint="Enter max response length"
24+
android:text="100"
25+
android:padding="8dp" />
26+
27+
<!-- Length Penalty -->
28+
<TextView
29+
android:layout_width="wrap_content"
30+
android:layout_height="wrap_content"
31+
android:text="Length Penalty"
32+
android:textSize="16sp"
33+
android:textColor="@color/black"
34+
android:layout_marginTop="16dp"
35+
android:layout_marginBottom="8dp"/>
36+
37+
<EditText
38+
android:id="@+id/idEdtLengthPenalty"
39+
android:layout_width="match_parent"
40+
android:layout_height="wrap_content"
41+
android:hint="Enter length penalty"
42+
android:inputType="number|numberDecimal"
43+
android:padding="8dp"
44+
android:text="1.0" />
45+
46+
<!-- Save Button -->
47+
<Button
48+
android:id="@+id/applySettingsButton"
49+
android:layout_width="wrap_content"
50+
android:layout_height="wrap_content"
51+
android:layout_gravity="center_horizontal"
52+
android:layout_marginTop="24dp"
53+
android:background="#89CC04"
54+
android:backgroundTint="#89CC04"
55+
android:tint="@color/white"
56+
android:padding="8dp"
57+
android:text="Apply"
58+
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
59+
android:visibility="visible"
60+
tools:visibility="visible" />
61+
</LinearLayout>

0 commit comments

Comments
 (0)