Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
FAT32 SHAMER
Aug 16, 2012



Hey guys, I've been going through the Big Nerd Ranch book and it's given me a massive amount of insight and I actually feel like I have some idea of what I'm doing now, which is great! I'm not sure if this is the right thread to ask, but I'm trying to create an Activity that asks the user to chose how they are feeling based on an emoji. After some research, I found that GridLayout seems to be best suited for what I want to do. Unfortunately, when I try to add a title at the top, it buggers my centre column up. On top of that, the padding for the images is kind of weird and I can't figure out why.

XML code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tools:context="tusentakk.blueharmony.FeelingActivity"
    android:background="@color/backgroundBlue">


    <GridLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:columnCount="3"
        >

        <ImageView
            android:id="@+id/feeling_angry"
            android:layout_width="116dp"
            android:layout_height="116dp"
            android:src="@drawable/feeling_angry"
            android:layout_column="0"
            android:layout_row="1"

            />

        <ImageView
            android:id="@+id/feeling_excited"
            android:layout_width="116dp"
            android:layout_height="116dp"
            android:src="@drawable/feeling_excited"
            android:layout_column="1"
            android:layout_row="1"

            />

        <ImageView
            android:id="@+id/feeling_furious"
            android:layout_width="116dp"
            android:layout_height="116dp"
            android:src="@drawable/feeling_furious"
            android:layout_column="2"
            android:layout_row="1"

            />

        <ImageView
            android:id="@+id/feeling_happy"
            android:layout_width="116dp"
            android:layout_height="116dp"
            android:src="@drawable/feeling_happy"
            android:layout_column="0"
            android:layout_row="2"

            />

        <ImageView
            android:id="@+id/feeling_numb"
            android:layout_width="116dp"
            android:layout_height="116dp"
            android:src="@drawable/feeling_numb"
            android:layout_column="1"
            android:layout_row="2"

            />

        <ImageView
            android:id="@+id/feeling_overjoyed"
            android:layout_width="116dp"
            android:layout_height="116dp"
            android:src="@drawable/feeling_overjoyed"
            android:layout_column="2"
            android:layout_row="2"

            />

        <ImageView
            android:id="@+id/feeling_overwhelmed"
            android:layout_width="116dp"
            android:layout_height="116dp"
            android:src="@drawable/feeling_overwhelmed"
            android:layout_column="0"
            android:layout_row="3"

            />

        <ImageView
            android:id="@+id/feeling_sad"
            android:layout_width="116dp"
            android:layout_height="116dp"
            android:src="@drawable/feeling_sad"
            android:layout_column="1"
            android:layout_row="3"

            />

        <ImageView
            android:id="@+id/feeling_worried"
            android:layout_width="116dp"
            android:layout_height="116dp"
            android:src="@drawable/feeling_worried"
            android:layout_column="2"
            android:layout_row="3"

            />

    </GridLayout>


</RelativeLayout>

results in this


My thinking was that if I nested the GridLayout inside of the Relative Layout, I would be able to put a TextView nested before the GridLayout began, then I would be able to not bugger the column sizes up. Does anyone have any guidance on this? I've been loving with it for the last hour and a half

edit: I read that there is a way to have content span across multiple columns, but I can't figure that one out either :shobon:

FAT32 SHAMER fucked around with this message at 06:37 on Feb 26, 2016

Adbot
ADBOT LOVES YOU

FAT32 SHAMER
Aug 16, 2012



fleshweasel posted:

I would make the GridLayout the child of a vertical LinearLayout and put a Toolbar above it to show the title. But I'm not an expert.

Doing this pushed the images to the top, which is good, but when I add the toolbar it makes the images disappear, which also happened when I tried using a RelativeLayout as the parent followed by a TextView before the GridLayout child.

Also, semi-related question: for the images to be clickable, I assume you just add android:clickable="true" and android:onClick="emotion", yeah? Or is it more similar to how you make a TextView a link to a new activity?

FAT32 SHAMER fucked around with this message at 07:09 on Feb 26, 2016

FAT32 SHAMER
Aug 16, 2012



Tunga posted:

Remember that a LinearLayout defaults to horizontal unless you specifically set the orientation property.

And for RelativeLayout you have to use layout_below or similar to set up the relative layout of the children, were you doing that?

Basically can you post the code that isn't working?

Nice! adding layout_below did the trick! Thanks 😊

edit: Here is what I have so far, for our senior project we wanted to build an app that allowed mental health patients to keep track of their moods among other things. I didn't pick any of the colours nor the logo, but I think it's turning out rather nicely. One weird thing to note is that the FeelingsActivity in the VM looks different from how the Android Studio Preview puts it

https://www.youtube.com/watch?v=ZOxiJQ4-tOc



versus



I suspect it may be because I defined the TextView textSize="30p" but I'm not sure

XML code:
<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="tusentakk.blueharmony.FeelingActivity"
    android:background="@color/backgroundBlue"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <TextView
        android:id="@+id/title_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/title_string"
        android:textSize="30dp"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"/>

    <GridLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:rowCount="3"
        android:layout_below="@id/title_text"
        android:paddingRight="8dp"
        >


        <ImageView
            android:id="@+id/feeling_angry"
            android:layout_width="125dp"
            android:layout_height="125dp"
            android:src="@drawable/feeling_angry"
            android:layout_column="0"
            android:layout_row="0"
            android:clickable="true"
            android:onClick="angry"
            />

        <ImageView
            android:id="@+id/feeling_excited"
            android:layout_width="125dp"
            android:layout_height="125dp"
            android:src="@drawable/feeling_excited"
            android:layout_column="1"
            android:layout_row="0"
            android:clickable="true"
            />

        <ImageView
            android:id="@+id/feeling_furious"
            android:layout_width="125dp"
            android:layout_height="125dp"
            android:src="@drawable/feeling_furious"
            android:layout_column="2"
            android:layout_row="0"
            android:clickable="true"
            />

        <ImageView
            android:id="@+id/feeling_happy"
            android:layout_width="125dp"
            android:layout_height="125dp"
            android:src="@drawable/feeling_happy"
            android:layout_column="0"
            android:layout_row="1"
            android:clickable="true"
            />

        <ImageView
            android:id="@+id/feeling_numb"
            android:layout_width="125dp"
            android:layout_height="125dp"
            android:src="@drawable/feeling_numb"
            android:layout_column="1"
            android:layout_row="1"
            android:clickable="true"
            />

        <ImageView
            android:id="@+id/feeling_overjoyed"
            android:layout_width="125dp"
            android:layout_height="125dp"
            android:src="@drawable/feeling_overjoyed"
            android:layout_column="2"
            android:layout_row="1"
            android:clickable="true"
            />

        <ImageView
            android:id="@+id/feeling_overwhelmed"
            android:layout_width="125dp"
            android:layout_height="125dp"
            android:src="@drawable/feeling_overwhelmed"
            android:layout_column="0"
            android:layout_row="2"
            android:clickable="true"
            />

        <ImageView
            android:id="@+id/feeling_sad"
            android:layout_width="125dp"
            android:layout_height="125dp"
            android:src="@drawable/feeling_sad"
            android:layout_column="1"
            android:layout_row="2"
            android:clickable="true"
            />

        <ImageView
            android:id="@+id/feeling_worried"
            android:layout_width="125dp"
            android:layout_height="125dp"
            android:src="@drawable/feeling_worried"
            android:layout_column="2"
            android:layout_row="2"
            android:clickable="true"
            />


    </GridLayout>
</RelativeLayout>
Considering three weeks ago I was lost and overwhelmed, I'm super stoked about my progress so far

FAT32 SHAMER fucked around with this message at 18:21 on Feb 26, 2016

FAT32 SHAMER
Aug 16, 2012



Okay here's a question:

I've been developing our senior project Android App on OS X and the only other person that has been pulling/submitting/messing with it is also on OS X, however today one of our partners pulled then opened Android Studio and the committed the changes, and it hosed everything up to the point that I had to roll back to a previous commit

is there a way to prevent this from happening so that the Windows users can work on the app with us or is this a totally different issue?

FAT32 SHAMER
Aug 16, 2012



speng31b posted:

Tough break, but you won't forget about the gitignore next time.

https://github.com/github/gitignore bookmark it

Nice! Thanks :)


Tunga posted:

If everyone is using Android Studio it really shouldn't matter what OS you're using unless you did some spectacularly bad with your .gitignore.

I went over the commits and it looks like it changed the SDK and gradle file locations because on mac its in a different part than windows or something

FAT32 SHAMER
Aug 16, 2012



Hey guys, more newbie questions! We are implementing a page in our app and we would like it so if you click on a TextView for, say, the Suicide Prevention hotline, it will ask you if you wish to dial the phone number. However, i can't seem to figure out how to do this.

I also can't figure out how to make a TextView a clickable link that opens chrome and takes the user to a certain website. I'm not sure if it can take you to a different app or if it can just pop up a little "web view"? thing that you doesn't remove you from the app itself.

Thanks for the help!

FAT32 SHAMER
Aug 16, 2012



Fergus Mac Roich posted:

The parent class View has the methods setClickable() and setOnClickListener(). All you need to do is make sure that clickable is set to true and then set a listener that does whatever you darned well please. You can do this in XML with the android:clickable attribute and the android:onClick attribute. I do not recommend using the latter as the method you use is required to be in the activity, which IMO gets a little unwieldy, so my preference is to get a reference to the TextView in code and set a listener for it. Listeners are easy to write, if you haven't written one yet.

Since a listener can contain any code you want, all of the things you mentioned are possible.

Lucky for you, Android provides a facility to make clicking on text with a phone number and dialing it REALLY simple. Check out this XML attribute. I don't think you even need an OnClickListener for that view. As far as opening up a page, it should be relatively straightforward. Your OnClickListener can do something that swaps the appropriate fragment or view with a fragment or naked WebView(this documentation also tells you how to open the browser).

Okay, brilliant! So for instance, in this snippet of code:

XML code:
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="20dp"
        android:text="Suicide Prevention Hotline: 1(800)273-8255"
        android:autoLink="phone"
        />
and that's that?

With the web links, can it be embedded like this or do i have to actually put the web link in a separate TextView with the actual URL spelled out?

FAT32 SHAMER
Aug 16, 2012



Oh and since this is a different subject, I'll double post:

I have a database that already exists that I have imported into Android Studio, and I would like to be able to insert values into it and keep it super simple just for demonstration purposes because the database that my partner decided to use for the website version of the app doesn't allow external connections and i didn't figure this out until it was already built out and it would be too annoying to backtrack and use something like firebase or whatever

SO I'm using the SQLiteOpenHelper and I'm struggling trying to figure out how to simply insert. I found this article but it seems to be for read-only database and doesn't have a way to actually write to the database. I tried using it anyways but I've been trying to figure this out for a few days and I'm getting to my wits end since all the online demos for the SQLiteOpenHelper class are for single-table databases


Here is the structure of my database with all the tables and columns:


Extrapolating what I've read, I think I would need to create an insert method for each table, so for instance insertJournal would insert the user's journal entry into the database, and insertFeeling would insert whether the user selected a certain feeling to accompany how they were feeling when they wrote that journal entry. I just can't figure out how to properly write an insert method that doesn't make the app crash and then call it from a different class in order to write whatever the user entered to it.

I'm positive that there has to be a simpler way to do it than what I have so far, so any guidance would be amazing :3:

Sorry i've been blowing this thread up lately, I have to do a dry run on the working product in two weeks and this has been my limiting factor.



edit: jesus christ i just watched a youtube video about it and inserting is way, way less complicated than i've been making it

I don't think I need help anymore :shobon:

FAT32 SHAMER fucked around with this message at 04:21 on Apr 1, 2016

FAT32 SHAMER
Aug 16, 2012



Volmarias posted:

First step may be to change that.

Yeah, I began to realize that using a HashMap as the insert argument is really dumb. I have it working now and I think that it's working, I just have to write a test class to go back and view the entries to make sure it's actually creating a db locally since i'm using the VM.

FAT32 SHAMER
Aug 16, 2012



I'm back and with more questions! So I have successfully set up a database that lets me insert poo poo with no issues and everything is amazing. Now, I'd like to be able to display this information to the user, but I'm not sure how exactly to do it. This is what I've been using:

SQLiteOpenHelper class
Java code:
package blueharmony.database;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;


/**
 * Created by thefalloftim on 3/19/16.
 */
public class BHDbHelper extends SQLiteOpenHelper {
    public static final String DATABASE_NAME = "blueharmony.db";

    public static final String JOURNAL_TABLE = "tbl_journal";
    public static final String FEELINGS_TABLE = "tbl_feeling";


    public BHDbHelper(Context context) {
        super(context, DATABASE_NAME, null, 1);
    }


    public boolean insertJournalEntry(String journalEntry, String feelingID) {
        SQLiteDatabase myDB = this.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("entry", journalEntry);
        values.put("feeling_id", feelingID);
        long result = myDB.insert("tbl_journal", null, values);

        if(result == -1){
            return false;
        }
        else{
            return true;
        }
    }

    public void insertFeeling(String feelingID) {
        SQLiteDatabase myDB = this.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("feeling_id", feelingID);
        myDB.insert("tbl_feeling", null, values);
        myDB.close();
    }

    public boolean insertMedReminder(String medName, String medTime){
        SQLiteDatabase myDB = this.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("med_name", medName);
        values.put("med_time", medTime);

        long result = myDB.insert("tbl_medreminders", null, values);

        if(result == -1){
            return false;
        }
        else{
            return true;
        }
    }

    public Cursor getJournalEntries(){
        SQLiteDatabase myDB = this.getWritableDatabase();
        Cursor res = myDB.rawQuery("SELECT 'feeling_id', 'entry' FROM TABLE 'tbl_journal';", null);

        if(res.getCount() == 0){
            return null;
        }
        else{
            return res;
        }
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE 'tbl_feeling' ( '_id' INTEGER PRIMARY KEY AUTOINCREMENT, 'feeling_id' TEXT );");
        db.execSQL("CREATE TABLE 'tbl_journal' ( '_id' INTEGER PRIMARY KEY AUTOINCREMENT, 'feeling_id' INTEGER, 'entry' TEXT );");
        db.execSQL("CREATE TABLE 'tbl_medreminders' ( '_id' INTEGER PRIMARY KEY AUTOINCREMENT, 'med_name' TEXT, 'med_time' DATETIME );");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS 'android_metadata'");
        db.execSQL("DROP TABLE IF EXISTS '" + JOURNAL_TABLE + "'");
        db.execSQL("DROP TABLE IF EXISTS '" + FEELINGS_TABLE + "'");
        db.execSQL("DROP TABLE IF EXISTS 'tbl_medreminders'");

        oldVersion = 0;
        newVersion = oldVersion + 1;

        onCreate(db);
    }

}

Previous Journal Entries dot class
Java code:
package blueharmony;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import blueharmony.database.BHDbHelper;

public class PrevJourActivity extends AppCompatActivity {

    private Button mBackButton;
    BHDbHelper myDB = new BHDbHelper(this);
    TextView mDisplayFeelingsView;
    TextView mDisplayJournalView;

    public void viewJournalEntries(){

        int column1 = myDB.getJournalEntries().getColumnIndex("feeling_id");
        int column2 = myDB.getJournalEntries().getColumnIndex("entry");
        myDB.getJournalEntries().moveToFirst();

        mDisplayJournalView = (TextView) findViewById(R.id.entry_display);
        mDisplayFeelingsView = (TextView) findViewById(R.id.feeling_display);
        String feelings = null;
        String entries = null;

        if(myDB.getJournalEntries() != null){
            do{
                String feeling = myDB.getJournalEntries().getString(column1);
                String entry = myDB.getJournalEntries().getString(column2);

                feelings = feelings + feeling + "\n";
                entries = entries + entry + "\n";

                System.out.println(feelings + entries);

            }while(myDB.getJournalEntries().moveToNext());
            mDisplayFeelingsView.setText(feelings);
            mDisplayJournalView.setText(entries);

        }



    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_prevjour);

        //viewJournalEntries();

        mBackButton = (Button) this.findViewById(R.id.button_pjback);
        mBackButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                PrevJourActivity.this.startActivity(new Intent(PrevJourActivity.this,
                        JournalActivity.class));
            }
        });



        //
    }

}

Activity.xml
XML code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/backgroundBlue"
    tools:context=".PrevJourActivity">

    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scrollbars="vertical"
        android:fillViewport="true"
        android:layout_above="@+id/button_pjback">


        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="test"
            android:id="@+id/feeling_display"/>
        
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/entry_display"/>
    </ScrollView>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button_pjback"
        android:text="Back"
        android:background="@drawable/button_background"
        android:textColor="@android:color/black"
        android:textStyle="bold"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="46dp"/>


</RelativeLayout>

Now, for some reason, the way I have my layout file causes the java file to throw an error on Line 54 (setContentView(R.layout.activity_prevjour);). I'm assuming it's because it doesn't like the grid view in the scrollview in the relative layout, but I have no idea.

I also assume that the rest of the reason for the crash is because it doesn't want to write the data to the TextView, and if that's the case, how the heck to I display it to the user? Or is it because I'm returning Null in the getJournalEntries() method?

Thanks for any help or input :)

FAT32 SHAMER
Aug 16, 2012



baka kaba posted:

What does the stack trace in your log say? It should tell you exactly where it's crashing and why (the type of exception and the error message it carries), and then further down it will probably give you the exception that caused that, and maybe there's another one that caused that...

Point is you don't really need to guess about it, there's a trail a clues right there which will at least point you in the right direction, show you which method crashed etc. You might need to google the error message to learn more but at least you'll be on the trail!

This isn't a 'fix it yourself!' post, I'm just encouraging you to look at the logs because they're really useful (and it'll make it easier for people to help you when you've narrowed the problem down)

Crash stack traces are logged at the Error level so you can filter logcat to display those and you'll find your app's crash really easily

Oh wow so that's what all of that is :shobon:. Here's what it output for me:

code:
04-04 15:49:44.678 29377-29377/? E/AndroidRuntime: FATAL EXCEPTION: main
                                                   Process: timclyne.blueharmony, PID: 29377
                                                   java.lang.RuntimeException: Unable to start activity ComponentInfo{timclyne.blueharmony/blueharmony.PrevJourActivity}: java.lang.IllegalStateException: ScrollView can host only one direct child
                                                       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2325)
                                                       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
                                                       at android.app.ActivityThread.access$800(ActivityThread.java:151)
                                                       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
                                                       at android.os.Handler.dispatchMessage(Handler.java:102)
                                                       at android.os.Looper.loop(Looper.java:135)
                                                       at android.app.ActivityThread.main(ActivityThread.java:5254)
                                                       at java.lang.reflect.Method.invoke(Native Method)
                                                       at java.lang.reflect.Method.invoke(Method.java:372)
                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
                                                    Caused by: java.lang.IllegalStateException: ScrollView can host only one direct child
                                                       at android.widget.ScrollView.addView(ScrollView.java:266)
                                                       at android.view.LayoutInflater.rInflate(LayoutInflater.java:810)
                                                       at android.view.LayoutInflater.rInflate(LayoutInflater.java:809)
                                                       at android.view.LayoutInflater.inflate(LayoutInflater.java:504)
                                                       at android.view.LayoutInflater.inflate(LayoutInflater.java:414)
                                                       at android.view.LayoutInflater.inflate(LayoutInflater.java:365)
                                                       at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:256)
                                                       at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:109)
                                                       at timclyne.blueharmony.PrevJourActivity.onCreate(PrevJourActivity.java:54) //<-------------------------------------
                                                       at android.app.Activity.performCreate(Activity.java:5990)
                                                       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
                                                       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
                                                       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387) 
                                                       at android.app.ActivityThread.access$800(ActivityThread.java:151) 
                                                       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303) 
                                                       at android.os.Handler.dispatchMessage(Handler.java:102) 
                                                       at android.os.Looper.loop(Looper.java:135) 
                                                       at android.app.ActivityThread.main(ActivityThread.java:5254) 
                                                       at java.lang.reflect.Method.invoke(Native Method) 
                                                       at java.lang.reflect.Method.invoke(Method.java:372) 
                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) 
                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698) 
So it says that it's crashing from Line 54 of PrevJourActivity.java, which is the line setContentView(R.layout.activity_prevjour);. Is it because "Caused by: java.lang.IllegalStateException: ScrollView can host only one direct child"?

Welp that makes life easy, the crashes were from having two textviews in one scroll view :newlol:

edit: I guess I have some SQLite syntax errors floating around and a few other easy fixes, so I guess that just leaves the question of what's the best way to getting the contents of your database into a textview or similar so the user can see it :D

FAT32 SHAMER fucked around with this message at 04:37 on Apr 10, 2016

FAT32 SHAMER
Aug 16, 2012



baka kaba posted:

Yep! You should look at the docs for ScrollView, but it's basically meant as a wrapper around another view, so that if that view gets too big to fit on the screen (or the space you've allowed for it), the ScrollView will let you swipe it around. So if you want to have two TextViews, stick them in a Layout of some kind, and wrap the Layout in the ScrollView

('View' is basically the base UI component class if the name sounds confusing, it took me a while to get my head around it. So Layouts are Views too, you build your UI with them)

As far as getting your stuff on-screen goes, it really depends how you want to display it and there are a lot of approaches you can take - some are a lot easier than others. Have a look at this, it's probably closest to what you have already:
https://github.com/codepath/android_guides/wiki/Populating-a-ListView-with-a-CursorAdapter

This is brilliant! Thanks so much for showing me this :3: I edited their code for it to work with mine, but for some reason for every entry it also puts the button under it, like this:



edited code!

Adapter:
Java code:
public class PrevJourCursorAdapter extends CursorAdapter {
    public PrevJourCursorAdapter(Context context, Cursor cursor, int flags) {
        super(context, cursor, 0);
    }

    // The newView method is used to inflate a new view and return it,
    // you don't bind any data to the view at this point.
    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        return LayoutInflater.from(context).inflate(R.layout.activity_prevjour, parent, false);
    }

    // The bindView method is used to bind all data to a given view
    // such as setting the text on a TextView.
    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        // Find fields to populate in inflated template
        TextView tvBody = (TextView) view.findViewById(R.id.feeling_display);
        TextView tvPriority = (TextView) view.findViewById(R.id.entry_display);
        // Extract properties from cursor
        String feeling = cursor.getString(cursor.getColumnIndexOrThrow("feeling_id"));
        String entry = cursor.getString(cursor.getColumnIndexOrThrow("entry"));
        // Populate fields with extracted properties
        tvBody.setText(feeling);
        tvPriority.setText(String.valueOf(entry));
    }
}
viewStuff method!
Java code:
    public void viewJournalEntries(){

        //mDisplayFeelingsView = (TextView) findViewById(R.id.feeling_display);
        //mDisplayJournalView = (TextView) findViewById(R.id.entry_display);

        BHDbHelper helper = new BHDbHelper(this);
        SQLiteDatabase db = helper.getWritableDatabase();
        Cursor cursor = db.rawQuery("SELECT * from 'tbl_journal';", null);

        ListView lvDisplayStuff = (ListView) findViewById(R.id.list_stuff);
        PrevJourCursorAdapter prevJourCursorAdapter = new PrevJourCursorAdapter(this, cursor, 0);

        lvDisplayStuff.setAdapter(prevJourCursorAdapter);

    }
And finally the Activity xml
XML code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/backgroundBlue"
    tools:context=".PrevJourActivity">


    <TextView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/feeling_display"
        android:layout_above="@+id/entry_display"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/entry_display"/>

    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/list_stuff">


    </ListView>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button_pjback"
        android:text="Back"
        android:background="@drawable/button_background"
        android:textColor="@android:color/black"
        android:textStyle="bold"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="46dp"/>


</RelativeLayout>

Now, based on what little I know about how View newView works, I assume it's creating a new instance of activity_prevjour every time it is called (which would be every time it pulls an item from the database), so now to figure that part out!

Thanks for all your help :)

FAT32 SHAMER
Aug 16, 2012



baka kaba posted:

You need two layouts - one is for the activity, and that contains the ListView, which is like a special frame that makes a list. You put that wherever you want it on the screen. You also need a layout for your items in the list

The ListView works by creating a View for each item in the list, which is what the Adapter handles. There are two methods you're overriding in there - newView() and bindView(). newView() is what's called when the ListView needs to create a new View to hold an item - you're inflating a layout to create your item's UI View, and that will go into the ViewGroup which is like an empty box in the list. "Hey what do I put in here" is what it's basically asking. This version is just inflating the same layout for every view, so they'll all look the same. This is where you need a layout that only holds your item's stuff - you're inflating the activity's layout again, so each list item has a mini activity layout in it, complete with button (and ListViews!)

Aaand just so you know, bindView() is where the ListView actually needs to display an item's details, so that's where you find all the TextViews and such in your item's layout, and set them to info from the Cursor. The view that's being passed in is the one newView() created. So newView() makes generic list items, and bindView() puts the data in them. Because it's a CursorAdapter it automagically gets the record for each item (the Cursor is always pointing at the right one), other Adapters might give you a position on the list, and then you look up what item that refers to in a list or array or whatever

This did the trick, thank you!!

And much to everyone's relief, that finishes our senior project app, so I probably won't be bothering you guys much now :3:

FAT32 SHAMER
Aug 16, 2012



StashAugustine posted:

I took a community college class on Android but the textbook was pretty garbage, and I like to have reference books for that kind of thing. Does anyone have a good recommendation for a reference for Android programming?

I wouldn't really call it a reference book but I got Big Nerd Ranch's Android Programming book and it took me from knowing jack poo poo about Android to writing a decent app and laying foundations for more complicated things/indepth complicated things in the course of three weeks. Probably quicker if I had devoted more than a few minutes a day to it.

FAT32 SHAMER
Aug 16, 2012



I'm trying to import a project into AndroidStudio.app and it keeps telling me that my gradle location is wrong. Where the gently caress is it supposed to be in OSX?

FAT32 SHAMER
Aug 16, 2012



Turns out you have to click on the folder inside the /androidstudio.app/gradle folder :doh:

FAT32 SHAMER
Aug 16, 2012



Does anyone have any experience with UiAutomator? Any tips or best practices? I keep getting this weird NullExceptionPointer error when I try to have it click on android system dialog buttons so I'm not sure what's up with that

FAT32 SHAMER
Aug 16, 2012



Mezzanine posted:

Pretty sure Android system dialog buttons have different "android.R.id"'s depending on the OS version, model, etc, so you may want to have it search by text or something?

I managed to get it working, but the issue is I'm doing this on apk's that companies won't release the source code to us and it's extremely tedious to hook UiObject2 methods onto objects so I was hoping I'm just retarded and doing it wrong, seems like I'm not based on the results though

:shrug:

FAT32 SHAMER
Aug 16, 2012



This is probably based on your browsing habits so thanks for revealing your French maid fetish

FAT32 SHAMER
Aug 16, 2012



What is Kotlin going to improve with Android development? I've only recently heard of it

FAT32 SHAMER
Aug 16, 2012



I was really hoping that they would announce that Android would be written in open source swift from here on out so it'd be easier to port my apps to Android but noooooooo

FAT32 SHAMER
Aug 16, 2012



The way it's written kind of reminds me of the way Swift is written at least, just more java-like instead of ObjC/C like

FAT32 SHAMER
Aug 16, 2012



Has anyone tried using Kotlin to write UiAutomator tests? I'm working on that right now and if I can get some Kotlin practince in doing that it would be good practice

FAT32 SHAMER
Aug 16, 2012



Taffer posted:

Can you explain why? I use both Kotlin and Swift daily and I vastly prefer Kotlin. Swift is better than Java obviously, but It's far behind Kotlin IMO.

And Kotlin Native is in the works, which would potentially allow Kotlin code to be used on IOS in the future.

What makes you prefer Kotlin over Swift?

FAT32 SHAMER
Aug 16, 2012



If it's running using the JVM doesn't that mean it's going to have all the same issues as Java, specifically its performance compared to languages like Swift/ObjC?

I'm down for easier to write Java but if it does essentially the same thing at the end of the day, I'm not sure there is much to celebrate here other than google pushing yet another shiny new thing that doesn't really change much

FAT32 SHAMER
Aug 16, 2012



Hell, even Go would be a huge performance improvement for android, though I understand the attachment to existing libraries. Pretty much any performance upgrade will require a language switch and a port of all the languages.

Edit: after looking at some benchmarks I guess Go wouldn't be as good of a performance upgrade? Idk I heard it was fantastic compared to Java for serverside stuff

FAT32 SHAMER
Aug 16, 2012



I guess that's a fair point that I really didn't consider :shobon:

FAT32 SHAMER
Aug 16, 2012



I only recently started following it after being assigned three android app dev projects at work since I'm the only one with mobile dev experience (in iOS)

FAT32 SHAMER
Aug 16, 2012



I think theres a bit of disappointment that they're pushing this instead of pushing something new that fixes all the issues developers face every day.

It's easier to post zingers about how this is smoke and mirrors to distract us for a while than it is to look at kotlin and be like "hmm maybe i'll use this one day when Im doing something that isnt critical"

FAT32 SHAMER
Aug 16, 2012



Ok guys I've been trying to figure this one out all day. I have a package name that I am trying to launch but I have neither the source code nor the main activity name to be able to launch it without simulating the click on screen using UiAutomator. Anyone have any ideas on how I would go about doing this? I saw someone on Stack Overflow say something about PackageManager but I couldnt parse what it was trying to say due to being in Hinglish.

FAT32 SHAMER
Aug 16, 2012



Volmarias posted:

adb logcat ActivityManager:V *:F

Launch the app, you'll see what the package name and the path to the main activity are.

Otherwise, you can use aapt to dump the manifest contents to find the activity with the internet filter with MAIN in it.

Edit: with regard to IDs, your IDE will generally let you do static imports, which provides cleaner layout without accidental collisions/pollutions from other namespaces.

Nice, thanks! I found this way of launching the package however I'm not sure why he's able to call the getPackageManager() and startActivity methods. I'm assuming it comes from a Context that is written somewhere else but I haven't written test code for a package I dont have the source code for before :shobon:

Java code:
try {
	Intent intent = getPackageManager().getLaunchIntentForPackage("com.missing.package.MAIN"]);
	startActivity(intent);
} catch (Exception e) {
    // returns null if application is not installed
}

FAT32 SHAMER
Aug 16, 2012



baka kaba posted:

This is in an instrumentation test? Can you use InstrumentationRegistry.getTargetContext() for it?

This worked, at least for the other apps on this device! There is an app that will only launch when you hold three physical buttons for three seconds and even though I have the package name it simply won't launch, and UiAutomator can grab the XML for me to simulate click events once the app is launched. I'm starting to wonder if the app was written weird for it to do this

For the record this is a automobile head unit/nav unit that I'm working on automating testing for so that's why this has been such a bitch compared to abnormal phone/tablet

FAT32 SHAMER
Aug 16, 2012



baka kaba posted:

Does it not launch at all, or does it start and immediately quit? They might have some weird Intent handler code that nopes out if you didn't launch it with the correct data

Unless, is the head unit already running that app as the UI? So it's capturing the keypresses and launching another 'app' that's really an internal component of the same app? Meaning the activity you're trying to launch might not actually be exported

It doesnt start at all and I get a "cannot call an intent from a null something something" error so I'm guessing the Intent is not exported :rip:


Volmarias posted:

You can always launch an activity via an explicit intent, as long as you know what the activity name is, and as long as the activity is marked as exported.

I'm curious how they're doing the key press thing though, is possible that this head unit remapped that to the camera key or something.

You press the Volume button + brightness button + home button for three seconds, I have no idea what they're overriding but it's pretty neato bandito :3:

(except for the part where i have to mark it as a semi-automated test instead of a fully automated test :argh:)

FAT32 SHAMER
Aug 16, 2012



Volmarias posted:

Redact the package name but post the error log here. What process gives that, the process you're trying to call, or the system_service?

Again, use logcat to parse ActivityManager logs to see what the entry Activity is. You can use an Explicit intent to start that activity, although how exactly you'll be testing is an open question.

code:
$ adb shell am instrument -w -r   -e debug false -e class com.group.benchautomation.DealerDiagsTest com.group.benchautomation.test/android.support.test.runner.AndroidJUnitRunner
Client not ready yet..
Started running tests

java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Intent android.content.Intent.addFlags(int)' on a null object reference
at com.group.benchautomation.DealerDiagsTest.setup(DealerDiagsTest.java:56)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:59)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:262)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1853)

Tests ran to completion.
and here is the code from the area

Java code:
    @Before
    public void setup() {
        //Initialize UiDevice instance
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        mDevice = UiDevice.getInstance(instrumentation);

        mDevice.pressHome();

        final String launcherPackage = "com.REDACTED.auto.diagnostics";
        assertThat(launcherPackage, notNullValue());
        mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT);

        Context context = InstrumentationRegistry.getContext();
        final Intent intent = context.getPackageManager()
                .getLaunchIntentForPackage(DEALER_DIAG_PACKAGE);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); //this is line 56
        context.startActivity(intent);

        mDevice.wait(Until.hasObject(By.pkg(DEALER_DIAG_PACKAGE).depth(0)), LAUNCH_TIMEOUT);
    }

FAT32 SHAMER
Aug 16, 2012



baka kaba posted:

Well you're getting it from this


This sounds like a pain in the :yayclod: to be honest!

Nah it just means instead of trying to launch it via an activity i will have to use a CAN database to simulate the signal that pressing the button does. The same thing happens when I try to launch the HVAC settings via activity instead of hitting the hard button, which tells me that it only wants a CAN/MOST signal instead of pure android implementation

FAT32 SHAMER
Aug 16, 2012



Volmarias posted:

:psyduck:

Again, look for the exported activity that gets launched when you press those buttons and specify it manually. Use setClassName to specify what you want to launch. You don't need to pick the one that would show up in the launcher, since you already told us it won't.

I tried every package name that the logcat spat out and none of them worked :shrug:

Are package names supposed to have slashes like com.bench.tuner/AMTuner or do I have to do some kind of string handling to handle the / character

FAT32 SHAMER
Aug 16, 2012



Volmarias posted:

The slash delineates the package name from the activity. Rather than repeating the entire package name, it strips the part that's already been provided. So, com.foo.bar with an activity at com.foo.bar.main.Butts would be represented as com.foo.bar/.main.Butts (I think it's .main and not main, but confirm this yourself)

Alternately, if you can get the apk itself, use aapt dump to dump out the (data representation of the) AndroidManifest file

Alternately, ADB dumpsys package will give you more than you ever wanted to know, but your info might show up there.

I'll have to check it out on monday and see if I can figure anything else out. I wont be able to get any source code from the client and with how modified the OS appears to be I'm guessing that i'm in some uncharted waters and will have to do it the hard way

FAT32 SHAMER
Aug 16, 2012



Ok here's the adb logcat ActivityManager:V *:F log dump when I launch the hidden app using button presses.

code:
--------- beginning of main
I/ActivityManager( 2296): START u0 {flg=0x10000000 cmp=com.android.systemui/.usb.UsbDebuggingActivity (has extras)} from uid 1000 on display 0
I/ActivityManager( 2296): Displayed com.android.systemui/.usb.UsbDebuggingActivity: +184ms
I/ActivityManager( 2296): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10200000 cmp=com.android.launcher3/.Launcher} from uid 1000 on display 0
I/ActivityManager( 2296): START u0 {act=com.REDACTED.auto.diagnostics.dealer.MAIN flg=0x10800000 cmp=com.REDACTED.auto.diagnostics/.dealer.MainActivity} from uid 1000 on display 0
I/ActivityManager( 2296): Start proc 20943:com.REDACTED.auto.diagnostics/1000 for activity com.REDACTED.auto.diagnostics/.dealer.MainActivity
I/ActivityManager( 2296): Displayed com.REDACTED.auto.diagnostics/.dealer.MainActivity: +572ms
Oddly the way they have their package names when i launch the AM app vs the FM app via intents/activities

code:
I/ActivityManager( 2296): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x34204000 cmp=com.REDACTED.tuner/.ui.FMActivity bnds=[374,117][608,331] (has extras)} from uid 1000 on display 0
I/ActivityManager( 2296): Displayed com.REDACTED.tuner/.ui.FMActivity: +544ms
I/ActivityManager( 2296): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10200000 cmp=com.android.launcher3/.Launcher} from uid 1000 on display 0
I/ActivityManager( 2296): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x34204000 cmp=com.REDACTED.tuner/.ui.AMActivity bnds=[890,117][1124,331] (has extras)} from uid 1000 on display 0
I/ActivityManager( 2296): Displayed com.REDACTED.tuner/.ui.AMActivity: +285ms

When I launch the com.REDACTED.tuner it will launch the AM tuner, however it crashes with a null object exception when i try to launch using com.REDACTED.tuner/.ui.AMActivity or com.REDACTED.tuner/.ui.FMActivity from my instrumentation tests. I tried removing the slashes from the package names as well but with no luck

FAT32 SHAMER
Aug 16, 2012



baka kaba posted:

I saw you're using getContext() - did you try getTargetContext() instead? I'm not sure exactly how it will work for what you're doing, but the target context is for the thing under instrumentation, instead of the thing running the tests

Hmm, that seems to be giving me the same java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Intent android.content.Intent.addFlags(int)' on a null object reference. Google hasnt exactly been very helpful in looking for clues on this one either so i'm not 100% sure.

Adbot
ADBOT LOVES YOU

FAT32 SHAMER
Aug 16, 2012



Volmarias posted:

Do this to start the diagnostics app:
code:
Intent intent = new Intent("com.REDACTED.auto.diagnostics.dealer.MAIN");
intent.setClassName("com.REDACTED.auto.diagnostics", "com.REDACTED.auto.diagnostics.dealer.MainActivity");
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
Context c = InstrumentationRegistry.getContext();
c.startActivity(intent);
Also


The error is exactly what it says. You can't call the "addFlags" instance method on a null instance. Your intent doesn't exist, that's the problem. There IS NO LAUNCH ACTIVITY (as far launchers are concerned) for this app. If you haven't yet learned how to, I highly recommend learning how to use the debugger.

Edit: to further clarify, here's the source for getLaunchIntentForPackage, which you should not be using here because there is no launch activity:
code:
    @Override
    public Intent getLaunchIntentForPackage(String packageName) {
        // First see if the package has an INFO activity; the existence of
        // such an activity is implied to be the desired front-door for the
        // overall package (such as if it has multiple launcher entries).
        Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
        intentToResolve.addCategory(Intent.CATEGORY_INFO);
        intentToResolve.setPackage(packageName);
        List<ResolveInfo> ris = queryIntentActivities(intentToResolve, 0);

        // Otherwise, try to find a main launcher activity.
        if (ris == null || ris.size() <= 0) {
            // reuse the intent instance
            intentToResolve.removeCategory(Intent.CATEGORY_INFO);
            intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
            intentToResolve.setPackage(packageName);
            ris = queryIntentActivities(intentToResolve, 0);
        }
        if (ris == null || ris.size() <= 0) {
            return null;
        }
        Intent intent = new Intent(intentToResolve);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setClassName(ris.get(0).activityInfo.packageName,
                ris.get(0).activityInfo.name);
        return intent;
    }
If the package doesn't have an activity with an intent filter for CATEGORY_INFO or CATEGORY_LAUNCHER, it returns null. This is why I kept telling you to glean the ACTUAL activity name / package name (and the action is a nice bonus) from the ActivityManager logcat output: It tells you what to actually invoke.

Now, mind you, it's possible that there are some extras in the intent that need to be provided, so unless you have root on the test device and can pull the target app to examine it, or flash a debug build which shows all of the intent info, you might still be SOL. That said, why haven't you contacted the OEM for info yet?
The OEM unfortunately will not discuss their system with me for reasons unknown, my guess is because the designers only speak japanese and it's not worth the effort when I'm working on something that wont necessarily benefit them as much as it benefits third parties.

That being said, the only fix I had to add was intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) after the clear task flag method call to get this to work :dance:. I wish I had caught on to your hints sooner, I didnt realize that you could specify intent class names using the .setClassName() method and google is wicked sparse on the details for doing anything with AndroidJUnit/UiAutomator for some reason (i'm assuming because most places have smarter people than me to figure it out :downs:)

Thanks for this!

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply