Contact Joining (Android M) - android

i try to join two Contacts, a default Contact from the Default Address-Store, and a Contact of my own Provider.
I have the following code-snippet of the com.android.Contact app:
private interface JoinContactQuery {
String[] PROJECTION = {
RawContacts._ID,
RawContacts.CONTACT_ID,
RawContacts.NAME_VERIFIED,
RawContacts.DISPLAY_NAME_SOURCE,
};
String SELECTION = RawContacts.CONTACT_ID + "=? OR " + RawContacts.CONTACT_ID + "=?";
int _ID = 0;
int CONTACT_ID = 1;
int NAME_VERIFIED = 2;
int DISPLAY_NAME_SOURCE = 3;
}
Cursor c = resolver.query(ContactsContract.RawContacts.CONTENT_URI,
JoinContactQuery.PROJECTION,
JoinContactQuery.SELECTION,
new String[]{String.valueOf(contactId1), String.valueOf(contactId2)}, null);
if (c == null) {
Log.e(TAG, "Unable to open Contacts DB cursor");
return;
}
long rawContactIds[];
long verifiedNameRawContactId = -1;
try {
if (c.getCount() == 0) {
return;
}
int maxDisplayNameSource = -1;
rawContactIds = new long[c.getCount()];
for (int i = 0; i < rawContactIds.length; i++) {
c.moveToPosition(i);
long rawContactId = c.getLong(JoinContactQuery._ID);
rawContactIds[i] = rawContactId;
int nameSource = c.getInt(JoinContactQuery.DISPLAY_NAME_SOURCE);
if (nameSource > maxDisplayNameSource) {
maxDisplayNameSource = nameSource;
}
}
// Find an appropriate display name for the joined contact:
// if should have a higher DisplayNameSource or be the name
// of the original contact that we are joining with another.
if (writable) {
for (int i = 0; i < rawContactIds.length; i++) {
c.moveToPosition(i);
if (c.getLong(JoinContactQuery.CONTACT_ID) == contactId1) {
int nameSource = c.getInt(JoinContactQuery.DISPLAY_NAME_SOURCE);
if (nameSource == maxDisplayNameSource
&& (verifiedNameRawContactId == -1
|| c.getInt(JoinContactQuery.NAME_VERIFIED) != 0)) {
verifiedNameRawContactId = c.getLong(JoinContactQuery._ID);
}
}
}
}
} finally {
c.close();
}
// For each pair of raw contacts, insert an aggregation exception
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
for (int i = 0; i < rawContactIds.length; i++) {
for (int j = 0; j < rawContactIds.length; j++) {
if (i != j) {
buildJoinContactDiff(operations, rawContactIds[i], rawContactIds[j]);
}
}
}
// Mark the original contact as "name verified" to make sure that the contact
// display name does not change as a result of the join
if (verifiedNameRawContactId != -1) {
Builder builder = ContentProviderOperation.newUpdate(
ContentUris.withAppendedId(RawContacts.CONTENT_URI, verifiedNameRawContactId));
builder.withValue("name_verified", 1);
operations.add(builder.build());
}
My Problem: The "name_verified" Field is removed on Android M (Preview 2). Whats the "correct" way to join two Contacts (and do not change the Name of the Contact)?
* EDIT / Solution *
Remove name_verified and set "IS_SUPER_PRIMARY" to content uri
//mark as SUPER PRIMARY
if (verifiedNameRawContactId != -1) {
operations.add(
ContentProviderOperation.newUpdate(ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, verifiedNameRawContactId))
.withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1)
.build());
}

The question is not quite clear to me but i think your are trying to join two contacts and one name is taking precedence over another but not the way you want it.
The correct way to join contacts is to add a row in this table and keep the type as TYPE_KEEP_TOGETHER. AggregationsExceptions table
If you want to keep the name of the first raw_contact mark the name record of the contact in Data table (StructuredName) as IS_SUPER_PRIMARY
IS_SUPER_PRIMARY

Related

Android 11 - Mediastore playlist remove multiple tracks from Playlist

On Android 11, when trying to remove multiple tracks from a Playlist, all the tracks are removed instead of just the selected ones. This used to work fine till Android 10.
However, if only 1 track is removed, no other tracks are removed. Is this a bug or an expected behavior going forward from Android 11? Is this a side-effect of scoped storage?
Tested on : Pixel 3, Android 11 Official release build
public void removeTracksFromPlaylist(long playlistId, String[] ids) {
Log.d(TAG, "removeTracksFromPlaylist() called with: playlistId = [" + playlistId + "], ids = [" + Arrays.toString(ids) + "]");
Uri uri = MediaStore.Audio.Playlists.Members.getContentUri(MediaStore.VOLUME_EXTERNAL, playlistId);
StringBuilder stringBuilder = new StringBuilder();
for (int index = 0; index < ids.length; index++) {
if (index > 0) {
stringBuilder.append(",");
}
stringBuilder.append("?");
}
String selection = MediaStore.Audio.Playlists.Members._ID + " IN (" + stringBuilder.toString() + ")";
contentResolver.delete(uri, selection, ids); // Returns count of all files removed from playlist
}
Edit:
Some information from source : (https://android.googlesource.com/platform/packages/providers/MediaProvider/+blame/refs/tags/android-11.0.0_r3/src/com/android/providers/media/MediaProvider.java#5610)
From the source, I see that if the count of playlist items are more than 1, the whole playlist is cleared.
try (Cursor c = qb.query(helper,
new String[] { Playlists.Members.PLAY_ORDER }, queryArgs, null)) {
if ((c.getCount() == 1) && c.moveToFirst()) { // This condition fails if more than 1 item to delete
return c.getInt(0) - 1;
} else {
return -1;
}
}
The index is used here which clears the playlist:(https://android.googlesource.com/platform/packages/providers/MediaProvider/+blame/refs/tags/android-11.0.0_r3/src/com/android/providers/media/MediaProvider.java#5566)
private int removePlaylistMembers(#NonNull Uri playlistUri, #NonNull Bundle queryArgs)
throws FallbackException {
final int index = resolvePlaylistIndex(playlistUri, queryArgs);
try {
final File playlistFile = queryForDataFile(playlistUri, null);
final Playlist playlist = new Playlist();
playlist.read(playlistFile);
final int count;
if (index == -1) {
count = playlist.asList().size();
playlist.clear(); //Clears whole playlist
} else {
count = 1;
playlist.remove(index);
}
playlist.write(playlistFile);
resolvePlaylistMembers(playlistUri);
return count;
} catch (IOException e) {
throw new FallbackException("Failed to update playlist", e,
android.os.Build.VERSION_CODES.R);
}
}

Filter number from Array of string and get the index of last two greater number in android

I have a ArrayList that has value like [Value,Sum3,121,data8input,in:21::7,7.00,9.01] and I want to extract only number as the output should be like this [3,121,8,21,7,7.00,9.01] and then have to rearrange ascending and then get the index of last two number as result will be [21,121].
My tried code below,
for (int i = 0; i < arrayString.size(); i++) {
Pattern p = Pattern.compile("-?\\d+(,\\d+)*?\\.?\\d+?");
List<String> numbers = new ArrayList<String>();
Matcher m = p.matcher(arrayString.get(i).getvalue);
numbers.addAll(m);
for (int j = 0; j < numbers.size(); j++) {
Log.d("REMEMBERFILTER", allCollection.get(i).getTextValue());
}
}
}
do something like this, though it is not exactly memory efficient as I am using another list.
ArrayList<String> tempList = new ArrayList<>();
for (int i = 0; i < yourArrayList.size(); i++) {
tempList.add(yourArrayList.get(i).replaceAll("[^0-9]", ""));
}
//Arrange in ascending order
Collections.sort(tempList);
//Also try to remove those indexes which has only letters with
tempList.removeAll(Arrays.asList("", null));
for (int i = 0; i < tempList.size(); i++) {
Log.d("+++++++++", "" + tempList.get(i));
}
//You can get the last two or any element by get method of list by //list.size()-1 and list.size()-2 so on
This is a way to do it, finalArray has the 2 numbers you want:
String[] str = new String[] {"Value", "Sum3", "121", "data8input", "in:21::7", "7.00,9.01"};
StringBuilder longStringBuilder = new StringBuilder();
for (String s : str) {
longStringBuilder.append(s).append(" ");
}
String longString = longStringBuilder.toString();
String onlyNumbers = " " + longString.replaceAll("[^0-9.]", " ") + " ";
onlyNumbers = onlyNumbers.replaceAll(" \\. ", "").trim();
while (onlyNumbers.indexOf(" ") > 0) {
onlyNumbers = onlyNumbers.replaceAll(" ", " ");
}
String[] array = onlyNumbers.split(" ");
Double[] doubleArray = new Double[array.length];
for (int i = 0; i < array.length; i++) {
try {
doubleArray[i] = Double.parseDouble(array[i]);
} catch (NumberFormatException e) {
e.printStackTrace();
doubleArray[i] = 0.0;
}
}
Arrays.sort(doubleArray);
int numbersCount = doubleArray.length;
Double[] finalArray;
if (numbersCount >= 2) {
finalArray = new Double[]{doubleArray[numbersCount - 2], doubleArray[numbersCount - 1]};
} else if (numbersCount == 1) {
finalArray = new Double[]{ doubleArray[0]};
} else {
finalArray = new Double[]{};
}
for (Double number : finalArray) {
System.out.println(number);
}

Cursor only returning one value / last value

I am really stuck as I have a cursor that retrieves values from the database but the cursor only returns the last value. I need the cursor to retrieve all values so I can display them all later on. Is there any way as I can return all the data stored through the cursor?
An Example as to whats happening. Eg Click on Button 1 and stores 1 perfectly but once I click on Button 2 and add 1. Button 1 data is not return or retrieved.
Any help would be greatly appreciated.
Execute
The first Cursor is goes through to check all the stored items.
public void exectute() {
AsyncTask.execute(new Runnable() {
#Override
public void run() {
Cursor c = TrackerDb.getStoredItems(getApplicationContext());
if (c != null) {
if (c.moveToFirst()) {
WorkoutDetails details = null;
//if (details != null) mWorkoutDetailsList.add(details);
do {
//if (details != null) mWorkoutDetailsList.add(details);
WorkoutDetails temp = getWorkoutFromCursor(c);
//if (details != null) mWorkoutDetailsList.add(details);
if (details == null) {
details = temp;
continue;
}
//if (details != null) mWorkoutDetailsList.add(details);
if (isSameDay(details.getWorkoutDate(), temp.getWorkoutDate())) {
//if (details != null) mWorkoutDetailsList.add(details);
if (DBG) Log.d(LOG_TAG, "isSameDay().. true");
//details.add(temp);
} else {
mWorkoutDetailsList.add(details);
details = temp;
}
// if (details != null) mWorkoutDetailsList.add(details);
} while (c.moveToNext());
if (details != null) mWorkoutDetailsList.add(details);
if (DBG)
Log.d(LOG_TAG, "AsyncTask: list size " + mWorkoutDetailsList.size());
runOnUiThread(new Runnable() {
#Override
public void run() {
mWorkoutsAdapter.updateList(mWorkoutDetailsList);
}
});
}
c.close();
}
}
});
}
Get Stored Items Code
This is the code which the excute class calls and where the cursor only returns one value.
public static Cursor getStoredItems(Context context) {
DBHelper dbHelper = new DBHelper(context);
SQLiteDatabase db = dbHelper.getWritableDatabase();
String[] projection = {ID, TIME, TYPE, DURATION, DATE, POINT};
String orderBy = TIME + " DESC";
Cursor cursor = db.query(TABLE_NAME, projection, null, null, null, null, orderBy);
return cursor;
}
Array
This is the array code where i want the cursor to store its values based on type.
private WorkoutDetails getWorkoutFromCursor(Cursor c) {
long time = c.getLong(c.getColumnIndex(TrackerDb.TIME));
int type = c.getInt(c.getColumnIndex(TrackerDb.TYPE));
int duration = c.getInt(c.getColumnIndex(TrackerDb.DURATION));
int point = c.getInt(c.getColumnIndex(TrackerDb.POINT));
int totalMoney = MoneyActivity.Money.values().length;
int[] points = new int[totalMoney];
int totalActivities = MeditationTrackerActivity.ACTIVITIES.values().length;
int[] durations = new int[totalActivities];
if (type < totalActivities) {
durations[type] = duration;
}
if( type == 0) {
for (int i = 0; i < totalMoney; i++) {
points[type] = point;
}
}
else if ( type == 1) {
for ( int ii = 0; ii < totalMoney; ii++) {
points[type] = point;
}
}
else if ( type == 2) {
for ( int iii = 0; iii < totalMoney; iii++) {
points[type] = point;
}
}
return new WorkoutDetails(time, durations, points);
}
Get Workout From Cursor Code
private static WorkoutDetails getWorkoutFromCursor(Cursor c) {
long time = c.getLong(c.getColumnIndex(TrackerDb.TIME));
int type = c.getInt(c.getColumnIndex(TrackerDb.TYPE));
int duration = c.getInt(c.getColumnIndex(TrackerDb.DURATION));
String date = c.getString(c.getColumnIndex(TrackerDb.DATE));
int point = c.getInt(c.getColumnIndex(TrackerDb.POINT));
int[] durations = new int[MeditationTrackerActivity.ACTIVITIES.values().length];
durations[type] = duration;
int[] points = new int[MoneyActivity.Money.values().length];
points[type] = point;
return new WorkoutDetails(time, durations, date, points);
}

regular-expressions android

i have string like these for example
309\306\308\337_338
309\306\337_338
310
311\315_316\336_337
311\315_316\336_337
311\335_336
these strings means list of page number , for example string "309\306\308\337_339" means
pages 309,306,308,337,338,339
i want to pass one of these string to function which return it as string like this
309,306,308,337,338,339
this function do that but in c# , i want to impalement in android
private static string Get_PageNumbers(string str)
{
ArrayList arrAll = new ArrayList();
MatchCollection match;
string[] excar;
string strid, firstNumber, lastlNumber;
int fn, ln;
ArrayList arrID = new ArrayList();
//***In Case The Range Number Between "_"
if (str.Contains("_"))
{
// match_reg = new Regex("(w?[\\d]+)*(_[\\d]+)");
Regex matchReg = new Regex("(w?[\\69]+_[\\d]+)*(q?[\\d]+//)*(a?[\\d]+_[\\d]+)*(y?[\\d]+)*");
match = matchReg.Matches(str);
int count = match.Count;
excar = new string[0];
for (int i = 0; i < count; i++)
{
Array.Resize(ref excar, count);
excar[i] = match[i].Groups[0].Value;
if (excar[i] != string.Empty)
arrID.Add(excar[i]);
}
//******IF Array Contains Range Of Number Like"102_110"
if (str.Contains("_"))
{
for (int i = 0; i < arrID.Count; i++)
{
strid = arrID[i].ToString();
if (arrID[i].ToString().Contains("_"))
{
int idy = strid.LastIndexOf("_");
firstNumber = strid.Substring(0, idy);
if (idy != -1)
{
lastlNumber = strid.Substring(idy + 1);
fn = int.Parse(firstNumber);
arrAll.Add(fn);
ln = int.Parse(lastlNumber);
for (int c = fn; c < ln; c++)
{
fn++;
arrAll.Add(fn);
}
}
}
else
{
arrAll.Add(arrID[i].ToString());
}
}
//******If Array Contain More Than One Number
if (arrAll.Count > 0)
{
str = "";
for (int i = 0; i < arrAll.Count; i++)
{
if (str != string.Empty)
str = str + "," + arrAll[i];
else
str = arrAll[i].ToString();
}
}
}
}
//***If string Contains between "/" only without "_"
else if (str.Contains("/") && !str.Contains("_"))
{
str = str.Replace("/", ",");
}
else if (str.Contains("\\"))
{
str = str.Replace("\\", ",");
}
return str;
}
I think this is easier to do with split function:
public static String Get_PageNumbers(String str) {// Assume str = "309\\306\\308\\337_338"
String result = "";
String[] pages = str.split("\\\\"); // now we have pages = {"309","306","308","337_338"}
for (int i = 0; i < pages.length; i++) {
String page = pages[i];
int index = page.indexOf('_');
if (index != -1) { // special case i.e. "337_338", index = 3
int start = Integer.parseInt(page.substring(0, index)); // start = 337
int end = Integer.parseInt(page.substring(index + 1)); // end = 338
for (int j = start; j <= end; j++) {
result += String.valueOf(j);
if (j != end) { // don't add ',' after last one
result += ",";
}
}
} else { // regular case i.e. "309","306","308"
result += page;
}
if (i != (pages.length-1)) { // don't add ',' after last one
result += ",";
}
}
return result; // result = "309,306,308,337,338"
}
For example this function when called as follows:
String result1 = Get_PageNumbers("309\\306\\308\\337_338");
String result2 = Get_PageNumbers("311\\315_316\\336_337");
String result3 = Get_PageNumbers("310");
Returns:
309,306,308,337,338
311,315,316,336,337
310
if i can suggest different implementation....
first, split string with "\" str.split("\\");, here you receive an array string with single number or a pattern like "num_num"
for all string founded, if string NOT contains "" char, put string in another array (othArr named), than, you split again with "" str.split("_");, now you have a 2 position array
convert that 2 strings in integer
now create a loot to min val form max val or two strings converted (and put it into othArr)
tranform othArr in a string separated with ","

Add the contents of one Cursor to another Cursor

I want to join two cursors so that the contents of the second Cursor shall also appear in first Cursor after joining.
Precisely here is my code,
public final Uri AllImage_URI_Int = MediaStore.Images.Media.INTERNAL_CONTENT_URI;
public final Uri AllAudio_URI = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
cContentList = managedQuery(AllImage_URI_Int, null, null, null, MediaStore.Images.ImageColumns.TITLE);
cList_Int = managedQuery(AllImage_URI, null, null, null, MediaStore.Images.ImageColumns.TITLE);
Should i use the CursorJoiner in this case?
I want to pass this Cursor to SimpleListAdapter? How can i join those two cursors?
Maybe you can use a MergeCursor wrapper to merge your two Cursors into a new one, and pass it to your Adapter.
well, i solved it myself and working, extendeb by AbstractCursor, heres the code,
private final int ROWCACHESIZE = 64;
private int mRowNumCache[] = new int[ROWCACHESIZE];
private int mCursorCache[] = new int[ROWCACHESIZE];
private int mCurRowNumCache[][];
private int mLastCacheHit = -1;
public SortCursor(Cursor[] cursors, String sortcolumn)
{
mCursors = cursors;
int length = mCursors.length;
mSortColumns = new int[length];
for (int i = 0 ; i < length ; i++) {
if (mCursors[i] == null) continue;
mCursors[i].moveToFirst();
// We don't catch the exception
mSortColumns[i] = mCursors[i].getColumnIndexOrThrow(sortcolumn);
}
mCursor = null;
String smallest = "";
for (int j = 0 ; j < length; j++) {
if (mCursors[j] == null || mCursors[j].isAfterLast())
continue;
String current = mCursors[j].getString(mSortColumns[j]);
if (mCursor == null || current.compareToIgnoreCase(smallest) < 0) {
smallest = current;
mCursor = mCursors[j];
}
}
for (int i = mRowNumCache.length - 1; i >= 0; i--) {
mRowNumCache[i] = -2;
}
mCurRowNumCache = new int[ROWCACHESIZE][length];
}

Categories

Resources