Merge "Work profile edu" into ub-launcher3-master
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index a1033f0..893d796 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -41,7 +41,7 @@
android:paddingLeft="12dp"
android:paddingRight="12dp" >
- <com.android.launcher3.ExtendedEditText
+ <com.android.launcher3.folder.FolderNameEditText
android:id="@+id/folder_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index 5b453c3..d64967b 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -21,15 +21,11 @@
import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.View;
-import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
-import com.android.launcher3.folder.FolderNameProvider;
import com.android.launcher3.util.UiThreadHelper;
-import java.util.List;
-
/**
* The edit text that reports back when the back key has been pressed.
@@ -105,25 +101,6 @@
UiThreadHelper.hideKeyboardAsync(getContext(), getWindowToken());
}
- @Override
- public void onCommitCompletion(CompletionInfo text) {
- setText(text.getText());
- setSelection(text.getText().length());
- }
-
- /**
- * Currently only used for folder name suggestion.
- */
- public void displayCompletions(List<String> suggestList) {
- int cnt = Math.min(suggestList.size(), FolderNameProvider.SUGGEST_MAX);
- CompletionInfo[] cInfo = new CompletionInfo[cnt];
- for (int i = 0; i < cnt; i++) {
- cInfo[i] = new CompletionInfo(i, i, suggestList.get(i));
- }
- post(() -> getContext().getSystemService(InputMethodManager.class)
- .displayCompletions(this, cInfo));
- }
-
private boolean showSoftInput() {
return requestFocus() &&
getContext().getSystemService(InputMethodManager.class)
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index e2b7b68..0fea0dc 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -45,6 +45,8 @@
*/
public static final int FLAG_MULTI_PAGE_ANIMATION = 0x00000004;
+ public static final int FLAG_MANUAL_FOLDER_NAME = 0x00000008;
+
public int options;
/**
@@ -140,4 +142,10 @@
writer.updateItemInDatabase(this);
}
}
+
+ @Override
+ protected String dumpProperties() {
+ return super.dumpProperties()
+ + " manuallyTypedTitle=" + hasOption(FLAG_MANUAL_FOLDER_NAME);
+ }
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 844189f..90d8125 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -96,7 +96,7 @@
View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener {
private static final String TAG = "Launcher.Folder";
-
+ private static final boolean DEBUG = false;
/**
* We avoid measuring {@link #mContent} with a 0 width or height, as this
* results in CellLayout being measured as UNSPECIFIED, which it does not support.
@@ -146,7 +146,7 @@
@Thunk FolderIcon mFolderIcon;
@Thunk FolderPagedView mContent;
- public ExtendedEditText mFolderName;
+ public FolderNameEditText mFolderName;
private PageIndicatorDots mPageIndicator;
protected View mFooter;
@@ -306,6 +306,7 @@
mFolderName.setText(suggestedNames[0]);
mFolderName.displayCompletions(Arrays.asList(suggestedNames).subList(1,
suggestedNames.length));
+ mFolderName.setEnteredCompose(false);
}
}
mFolderName.setHint("");
@@ -318,7 +319,13 @@
// Convert to a string here to ensure that no other state associated with the text field
// gets saved.
String newTitle = mFolderName.getText().toString();
+ if (DEBUG) {
+ Log.d(TAG, "onBackKey newTitle=" + newTitle);
+ }
+
mInfo.title = newTitle;
+ mInfo.setOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME, mFolderName.isEnteredCompose(),
+ mLauncher.getModelWriter());
mFolderIcon.onTitleChanged(newTitle);
mLauncher.getModelWriter().updateItemInDatabase(mInfo);
@@ -350,6 +357,10 @@
}
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (DEBUG) {
+ Log.d(TAG, "onEditorAction actionId=" + actionId + " key="
+ + (event != null ? event.getKeyCode() : "null event"));
+ }
if (actionId == EditorInfo.IME_ACTION_DONE) {
mFolderName.dispatchBackKey();
return true;
@@ -436,7 +447,8 @@
*/
public void showSuggestedTitle(String[] suggestName) {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()
- && TextUtils.isEmpty(mFolderName.getText().toString())) {
+ && TextUtils.isEmpty(mFolderName.getText().toString())
+ && !mInfo.hasOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME)) {
if (suggestName.length > 0 && !TextUtils.isEmpty(suggestName[0])) {
mFolderName.setHint("");
mFolderName.setText(suggestName[0]);
@@ -552,9 +564,6 @@
openFolder.close(true);
}
- if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- mLauncher.getFolderNameProvider().load(getContext());
- }
mContent.bindItems(items);
centerAboutIcon();
mItemsInvalidated = true;
@@ -1495,6 +1504,9 @@
return ContainerType.FOLDER;
}
+ /**
+ * Navigation bar back key or hardware input back key has been issued.
+ */
@Override
public boolean onBackPressed() {
if (isEditingName()) {
diff --git a/src/com/android/launcher3/folder/FolderNameEditText.java b/src/com/android/launcher3/folder/FolderNameEditText.java
new file mode 100644
index 0000000..7e3f847
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderNameEditText.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.folder;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
+import android.view.inputmethod.InputMethodManager;
+
+import com.android.launcher3.ExtendedEditText;
+
+import java.util.List;
+
+/**
+ * Handles additional edit text functionality to better support folder name suggestion.
+ * First, makes suggestion to the InputMethodManager via {@link #displayCompletions(List)}
+ * Second, intercepts whether user accepted the suggestion or manually edited their
+ * folder names.
+ */
+public class FolderNameEditText extends ExtendedEditText {
+ private static final String TAG = "FolderNameEditText";
+ private static final boolean DEBUG = false;
+
+ private boolean mEnteredCompose = false;
+
+ public FolderNameEditText(Context context) {
+ super(context);
+ }
+
+ public FolderNameEditText(Context context, AttributeSet attrs) {
+ // ctor chaining breaks the touch handling
+ super(context, attrs);
+ }
+
+ public FolderNameEditText(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ InputConnection con = super.onCreateInputConnection(outAttrs);
+ FolderNameEditTextInputConnection connectionWrapper =
+ new FolderNameEditTextInputConnection(con, true);
+ return connectionWrapper;
+ }
+
+ public void displayCompletions(List<String> suggestList) {
+ int cnt = Math.min(suggestList.size(), FolderNameProvider.SUGGEST_MAX);
+ CompletionInfo[] cInfo = new CompletionInfo[cnt];
+ for (int i = 0; i < cnt; i++) {
+ cInfo[i] = new CompletionInfo(i, i, suggestList.get(i));
+ }
+ post(() -> getContext().getSystemService(InputMethodManager.class)
+ .displayCompletions(this, cInfo));
+ }
+
+ /**
+ * Within 's', the 'count' characters beginning at 'start' have just replaced
+ * old text 'before'
+ */
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ String reason = "unknown";
+ if (start == 0 && count == 0 && before > 0) {
+ reason = "suggestion was rejected";
+ mEnteredCompose = true;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "onTextChanged " + start + "," + before + "," + count
+ + ", " + reason);
+ }
+ }
+
+ @Override
+ public void onCommitCompletion(CompletionInfo text) {
+ setText(text.getText());
+ setSelection(text.getText().length());
+ mEnteredCompose = false;
+ }
+
+ protected void setEnteredCompose(boolean value) {
+ mEnteredCompose = value;
+ }
+
+ protected boolean isEnteredCompose() {
+ if (DEBUG) {
+ Log.d(TAG, "isEnteredCompose " + mEnteredCompose);
+ }
+ return mEnteredCompose;
+ }
+
+ private class FolderNameEditTextInputConnection extends InputConnectionWrapper {
+
+ FolderNameEditTextInputConnection(InputConnection target, boolean mutable) {
+ super(target, mutable);
+ }
+
+ @Override
+ public boolean setComposingText(CharSequence cs, int newCursorPos) {
+ mEnteredCompose = true;
+ return super.setComposingText(cs, newCursorPos);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index 782b0e2..9ea292c 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -109,11 +109,14 @@
if (contains(candidatesOut, candidate)) {
return;
}
+
for (int i = 0; i < candidate.length(); i++) {
if (TextUtils.isEmpty(candidatesOut[i])) {
candidatesOut[i] = candidate;
+ return;
}
}
+ candidatesOut[candidate.length() - 1] = candidate;
}
private boolean contains(CharSequence[] list, CharSequence key) {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 0a3462f..5555eab 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -68,6 +68,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
+import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
@@ -93,10 +94,13 @@
private static final String TAG = "Tapl";
private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 20;
private static final int GESTURE_STEP_MS = 16;
+ private static final SimpleDateFormat DATE_TIME_FORMAT =
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
private static long START_TIME = System.currentTimeMillis();
static final Pattern EVENT_LOG_ENTRY = Pattern.compile(
- "[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]\\.[0-9][0-9][0-9]"
+ "(?<time>[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] "
+ + "[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\\.[0-9][0-9][0-9])"
+ ".*" + TestProtocol.TAPL_EVENTS_TAG + ": (?<event>.*)");
private static final Pattern EVENT_TOUCH_DOWN = getTouchEventPattern("ACTION_DOWN");
@@ -167,7 +171,7 @@
// Not null when we are collecting expected events to compare with actual ones.
private List<Pattern> mExpectedEvents;
- private String mTimeBeforeFirstLogEvent;
+ private Date mStartRecordingTime;
private boolean mCheckEventsForSuccessfulGestures = false;
private static Pattern getTouchEventPattern(String action) {
@@ -1187,32 +1191,38 @@
private List<String> getEvents() {
final ArrayList<String> events = new ArrayList<>();
try {
- final String logcatTimeParameter =
- mTimeBeforeFirstLogEvent != null ? " -t " + mTimeBeforeFirstLogEvent : "";
final String logcatEvents = mDevice.executeShellCommand(
- "logcat -d --pid=" + getPid() + logcatTimeParameter
+ "logcat -d -v year --pid=" + getPid() + " -t "
+ + DATE_TIME_FORMAT.format(mStartRecordingTime).replaceAll(" ", "")
+ " -s " + TestProtocol.TAPL_EVENTS_TAG);
final Matcher matcher = EVENT_LOG_ENTRY.matcher(logcatEvents);
while (matcher.find()) {
+ // Skip events before recording start time.
+ if (DATE_TIME_FORMAT.parse(matcher.group("time"))
+ .compareTo(mStartRecordingTime) < 0) {
+ continue;
+ }
+
events.add(matcher.group("event"));
}
return events;
} catch (IOException e) {
throw new RuntimeException(e);
+ } catch (ParseException e) {
+ throw new AssertionError(e);
}
}
private void startRecordingEvents() {
Assert.assertTrue("Already recording events", mExpectedEvents == null);
mExpectedEvents = new ArrayList<>();
- mTimeBeforeFirstLogEvent = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
- .format(new Date())
- .replaceAll(" ", "");
- log("startRecordingEvents: " + mTimeBeforeFirstLogEvent);
+ mStartRecordingTime = new Date();
+ log("startRecordingEvents: " + DATE_TIME_FORMAT.format(mStartRecordingTime));
}
private void stopRecordingEvents() {
mExpectedEvents = null;
+ mStartRecordingTime = null;
}
Closable eventsCheck() {